d40c5ba8a1a0c4bce3cdea64669ce0834fca958f
[debian/jabref.git] / src / java / net / sf / jabref / MetaData.java
1 /*
2  Copyright (C) 2003 Morten O. Alver, Nizar N. Batada
3
4  All programs in this directory and
5  subdirectories are published under the GNU General Public License as
6  described below.
7
8  This program is free software; you can redistribute it and/or modify
9  it under the terms of the GNU General Public License as published by
10  the Free Software Foundation; either version 2 of the License, or (at
11  your option) any later version.
12
13  This program is distributed in the hope that it will be useful, but
14  WITHOUT ANY WARRANTY; without even the implied warranty of
15  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16  General Public License for more details.
17
18  You should have received a copy of the GNU General Public License
19  along with this program; if not, write to the Free Software
20  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
21  USA
22
23  Further information about the GNU GPL is available at:
24  http://www.gnu.org/copyleft/gpl.ja.html
25
26  */
27 package net.sf.jabref;
28
29 import java.io.*;
30 import java.util.*;
31
32 import net.sf.jabref.groups.*;
33
34 public class MetaData {
35     private HashMap metaData = new HashMap();
36     private StringReader data;
37     private GroupTreeNode groupsRoot = null;
38     private File file = null; // The File where this base gets saved.
39     
40     /**
41      * The MetaData object stores all meta data sets in Vectors. To ensure that
42      * the data is written correctly to string, the user of a meta data Vector
43      * must simply make sure the appropriate changes are reflected in the Vector
44      * it has been passed.
45      */
46     public MetaData(HashMap inData, BibtexDatabase db) {
47         boolean groupsTreePresent = false;
48         Vector flatGroupsData = null;
49         Vector treeGroupsData = null;
50         // The first version (0) lacked a version specification, 
51         // thus this value defaults to 0.
52         int groupsVersionOnDisk = 0;
53         
54         if (inData != null) for (Iterator i = inData.keySet().iterator(); i.hasNext();) {
55             String key = (String) i.next();
56             data = new StringReader((String) inData.get(key));
57             String unit;
58             Vector orderedData = new Vector();
59             // We must allow for ; and \ in escape sequences.
60             try {
61                 while ((unit = getNextUnit(data)) != null) {
62                     orderedData.add(unit);
63                 }
64             } catch (IOException ex) {
65                 System.err.println("Weird error while parsing meta data.");
66             }
67             if (key.equals("groupsversion")) {
68                 if (orderedData.size() >= 1)
69                     groupsVersionOnDisk = Integer.parseInt(orderedData.firstElement().toString());
70             } else if (key.equals("groupstree")) {
71                 groupsTreePresent = true;
72                 treeGroupsData = orderedData; // save for later user
73                 // actual import operation is handled later because "groupsversion"
74                 // tag might not yet have been read
75             } else if (key.equals("groups")) {
76                 flatGroupsData = orderedData;
77             } else {
78                 putData(key, orderedData);
79             }
80         }
81         
82         // this possibly handles import of a previous groups version
83         if (groupsTreePresent)
84             putGroups(treeGroupsData, db, groupsVersionOnDisk);
85         
86         if (!groupsTreePresent && flatGroupsData != null) {
87             groupsRoot = VersionHandling.importFlatGroups(flatGroupsData);
88         }
89     }
90
91     /**
92      * The MetaData object can be constructed with no data in it.
93      */
94     public MetaData() {
95
96     }
97
98     /**
99      * Add default metadata for new database:
100      */
101     public void initializeNewDatabase() {
102         metaData.put(Globals.SELECTOR_META_PREFIX + "keywords", new Vector());
103         metaData.put(Globals.SELECTOR_META_PREFIX + "author", new Vector());
104         metaData.put(Globals.SELECTOR_META_PREFIX + "journal", new Vector());
105         metaData.put(Globals.SELECTOR_META_PREFIX + "publisher", new Vector());
106     }
107
108     public Iterator iterator() {
109         return metaData.keySet().iterator();
110     }
111
112     public Vector getData(String key) {
113         return (Vector) metaData.get(key);
114     }
115
116     public void remove(String key) {
117         metaData.remove(key);
118     }
119
120     /**
121      * Stores the specified data in this object, using the specified key. For
122      * certain keys (e.g. "groupstree"), the objects in orderedData are
123      * reconstructed from their textual (String) representation if they are of
124      * type String, and stored as an actual instance.
125      */
126     public void putData(String key, Vector orderedData) {
127         metaData.put(key, orderedData);
128     }
129
130     /**
131      * Look up the directory set up for the given field type for this database.
132      * If no directory is set up, return that defined in global preferences.
133      * @param fieldName The field type
134      * @return The default directory for this field type.
135      */
136     public String getFileDirectory(String fieldName) {
137         // There can be up to two directory definitions for these files - the database's
138         // metadata can specify a directory, or the preferences can specify one. The
139         // metadata directory takes precedence if defined.
140         String key = fieldName + "Directory";
141         String dir;
142         Vector vec = getData(key);
143         if ((vec != null) && (vec.size() > 0)) {
144             dir = (String)vec.get(0);
145             // If this directory is relative, we try to interpret it as relative to
146             // the file path of this bib file:
147             if (!(new File(dir)).isAbsolute() && (file != null)) {
148                 String relDir = new StringBuffer(file.getParent()).
149                         append(System.getProperty("file.separator")).
150                         append(dir).toString();
151                 // If this directory actually exists, it is very likely that the
152                 // user wants us to use it:
153                 if ((new File(relDir)).exists())
154                     dir = relDir;
155             }
156         }
157         else
158             dir = Globals.prefs.get(key);
159
160
161         //System.out.println("MetaData: dir: '"+dir+"' relative: "+(new File(dir)).isAbsolute());
162         return dir;
163     }
164
165     private void putGroups(Vector orderedData, BibtexDatabase db, int version) {
166         try {
167             groupsRoot = VersionHandling.importGroups(orderedData, db, 
168                     version);
169         } catch (Exception e) {
170             // we cannot really do anything about this here
171             System.err.println(e);
172         }
173     }
174
175     public GroupTreeNode getGroups() {
176         return groupsRoot;
177     }
178     
179     /**
180      * Sets a new group root node. <b>WARNING </b>: This invalidates everything
181      * returned by getGroups() so far!!!
182      */
183     public void setGroups(GroupTreeNode root) {
184         groupsRoot = root;
185     }
186
187     /**
188      * Writes all data to the specified writer, using each object's toString()
189      * method.
190      */
191     public void writeMetaData(Writer out) throws IOException {
192         // write all meta data except groups
193         for (Iterator i = metaData.keySet().iterator(); i.hasNext();) {
194             String key = (String) i.next();
195             StringBuffer sb = new StringBuffer();
196             Vector orderedData = (Vector) metaData.get(key);
197             if (orderedData.size() >= 0) {
198                 sb.append("@comment{").append(GUIGlobals.META_FLAG).append(key).append(":");
199                 for (int j = 0; j < orderedData.size(); j++) {
200                     sb.append(Util.quote((String) orderedData.elementAt(j), ";", '\\')).append(";");
201                 }
202                 sb.append("}");
203                 sb.append(Globals.NEWLINE);
204                 sb.append(Globals.NEWLINE);
205             }
206             wrapStringBuffer(sb, Globals.METADATA_LINE_LENGTH);
207             out.write(sb.toString());
208         }
209         // write groups if present. skip this if only the root node exists 
210         // (which is always the AllEntriesGroup).
211         if (groupsRoot != null && groupsRoot.getChildCount() > 0) {
212             StringBuffer sb = new StringBuffer();
213             // write version first
214             sb.append("@comment{").append(GUIGlobals.META_FLAG).append("groupsversion:");
215             sb.append(""+VersionHandling.CURRENT_VERSION+";");
216             sb.append("}");
217             sb.append(Globals.NEWLINE);
218             sb.append(Globals.NEWLINE);
219             out.write(sb.toString());
220             
221             // now write actual groups
222             sb = new StringBuffer();
223             sb.append("@comment{").append(GUIGlobals.META_FLAG).append("groupstree:");
224             sb.append(Globals.NEWLINE);
225             // GroupsTreeNode.toString() uses "\n" for separation
226             StringTokenizer tok = new StringTokenizer(groupsRoot.getTreeAsString(),Globals.NEWLINE);
227             while (tok.hasMoreTokens()) {
228                 StringBuffer s = 
229                     new StringBuffer(Util.quote(tok.nextToken(), ";", '\\') + ";");
230                 wrapStringBuffer(s, Globals.METADATA_LINE_LENGTH);
231                 sb.append(s);
232                 sb.append(Globals.NEWLINE);
233             }
234             sb.append("}");
235             sb.append(Globals.NEWLINE);
236             sb.append(Globals.NEWLINE);
237             out.write(sb.toString());
238         }
239     }
240
241     private void wrapStringBuffer(StringBuffer sb, int lineLength) {
242         for (int i=lineLength; i<sb.length(); i+=lineLength+1) {
243             sb.insert(i, Globals.NEWLINE);
244         }
245     }
246     
247     /**
248      * Reads the next unit. Units are delimited by ';'. 
249      */
250     private String getNextUnit(Reader reader) throws IOException {
251         int c;
252         boolean escape = false;
253         StringBuffer res = new StringBuffer();
254         while ((c = reader.read()) != -1) {
255             if (escape) {
256                 res.append((char)c);
257                 escape = false;
258             } else if (c == '\\') {
259                 escape = true;
260             } else if (c == ';') {
261                 break;
262             } else {
263                 res.append((char)c);
264             }
265         }
266         if (res.length() > 0)
267             return res.toString();
268         return null;
269     }
270
271     public File getFile() {
272         return file;
273     }
274
275     public void setFile(File file) {
276         this.file = file;
277     }
278 }