c226ab61bf51ba21aa5f0a9b7e2f19073e73d6e5
[debian/jabref.git] / src / java / net / sf / jabref / BibtexFields.java
1 /*
2  Copyright (C) 2006 Raik Nagel <kiar@users.sourceforge.net>
3  All rights reserved.
4
5  Redistribution and use in source and binary forms, with or without
6  modification, are permitted provided that the following conditions are met:
7
8  * Redistributions of source code must retain the above copyright notice,
9   this list of conditions and the following disclaimer.
10  * Redistributions in binary form must reproduce the above copyright notice,
11   this list of conditions and the following disclaimer in the documentation
12   and/or other materials provided with the distribution.
13  * Neither the name of the author nor the names of its contributors may be
14   used to endorse or promote products derived from this software without
15   specific prior written permission.
16
17  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
20  PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
21  OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
22  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23  PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
24  OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
25  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
26  OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
27  ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28  */
29
30 // created by : r.nagel 19.04.2006
31 //
32 // function : Handling of bibtex fields.
33 //            All bibtex-field related stuff should be placed here!
34 //            Because we can export these informations into additional
35 //            config files -> simple extension and definition of new fields....
36 //
37 // todo     : - handling of identically fields with different names
38 //              e.g. LCCN = lib-congress
39 //            - group id for each fields, e.g. standard, jurabib, bio....
40 //            - add a additional properties functionality into the
41 //              BibtexSingleField class
42 //
43 // modified : r.nagel 25.04.2006
44 //            export/import of some definition from/to a xml file
45
46 package net.sf.jabref ;
47
48 import java.util.* ;
49 import net.sf.jabref.util.*;
50 import org.w3c.dom.Element;
51 import org.w3c.dom.NodeList;
52
53 public class BibtexFields
54 {
55   public static final String KEY_FIELD = "bibtexkey" ;
56
57   // some internal fields
58   public static final String
59       SEARCH = "__search",
60       GROUPSEARCH = "__groupsearch",
61       MARKED = "__markedentry",
62       OWNER = "owner",
63       TIMESTAMP = "timestamp", // it's also definied at the JabRefPreferences class
64       ENTRYTYPE = "entrytype",
65
66       // Using this when I have no database open or when I read
67       // non bibtex file formats (used by the ImportFormatReader.java)
68       DEFAULT_BIBTEXENTRY_ID = "__ID" ;
69
70   public static final String[] DEFAULT_INSPECTION_FIELDS = new String[]
71           {"author", "title", "year", KEY_FIELD};
72
73
74   // singleton instance
75   private static final BibtexFields runtime = new BibtexFields() ;
76
77   // contains all bibtex-field objects (BibtexSingleField)
78   private HashMap fieldSet = null ;
79
80   // contains all known (and public) bibtex fieldnames
81   private Object[] PUBLIC_FIELDS = null ;
82
83   private BibtexFields()
84   {
85     fieldSet = new HashMap() ;
86     BibtexSingleField dummy = null ;
87
88     // FIRST: all standard fields
89     // These are the fields that BibTex might want to treat, so these
90     // must conform to BibTex rules.
91     add( new BibtexSingleField( "address", true, GUIGlobals.SMALL_W  ) ) ;
92     // An annotation. It is not used by the standard bibliography styles,
93     // but may be used by others that produce an annotated bibliography.
94     // http://www.ecst.csuchico.edu/~jacobsd/bib/formats/bibtex.html
95     add( new BibtexSingleField( "annote", true, GUIGlobals.LARGE_W  ) ) ;
96     add( new BibtexSingleField( "author", true, GUIGlobals.MEDIUM_W, 280 ) ) ;
97     add( new BibtexSingleField( "booktitle", true, 175 ) ) ;
98     add( new BibtexSingleField( "chapter", true, GUIGlobals.SMALL_W  ) ) ;
99     add( new BibtexSingleField( "crossref", true, GUIGlobals.SMALL_W ) ) ;
100     add( new BibtexSingleField( "edition", true, GUIGlobals.SMALL_W  ) ) ;
101     add( new BibtexSingleField( "editor", true, GUIGlobals.MEDIUM_W, 280  ) ) ;
102     add( new BibtexSingleField( "howpublished", true, GUIGlobals.MEDIUM_W  ) ) ;
103     add( new BibtexSingleField( "institution", true, GUIGlobals.MEDIUM_W  ) ) ;
104
105     dummy = new BibtexSingleField( "journal", true, GUIGlobals.SMALL_W ) ;
106     dummy.setExtras("journalNames");
107     add(dummy) ;
108     add( new BibtexSingleField( "key", true ) ) ;
109     add( new BibtexSingleField( "month", true, GUIGlobals.SMALL_W ) ) ;
110     add( new BibtexSingleField( "note", true, GUIGlobals.MEDIUM_W  ) ) ;
111     add( new BibtexSingleField( "number", true, GUIGlobals.SMALL_W, 60  ) ) ;
112     add( new BibtexSingleField( "organization", true, GUIGlobals.MEDIUM_W  ) ) ;
113     add( new BibtexSingleField( "pages", true, GUIGlobals.SMALL_W ) ) ;
114     add( new BibtexSingleField( "publisher", true, GUIGlobals.MEDIUM_W  ) ) ;
115     add( new BibtexSingleField( "school", true, GUIGlobals.MEDIUM_W  ) ) ;
116     add( new BibtexSingleField( "series", true, GUIGlobals.SMALL_W  ) ) ;
117     add( new BibtexSingleField( "title", true, 400 ) ) ;
118     add( new BibtexSingleField( "type", true, GUIGlobals.SMALL_W  ) ) ;
119     add( new BibtexSingleField( "volume", true, GUIGlobals.SMALL_W, 60  ) ) ;
120     add( new BibtexSingleField( "year", true, GUIGlobals.SMALL_W, 60 ) ) ;
121
122     // some semi-standard fields
123     dummy = new BibtexSingleField( KEY_FIELD, true ) ;
124     dummy.setPrivate();
125     add( dummy ) ;
126
127     dummy = new BibtexSingleField( "doi", true, GUIGlobals.SMALL_W ) ;
128     dummy.setExtras("external");
129     add(dummy) ;
130     add( new BibtexSingleField( "eid", true, GUIGlobals.SMALL_W  ) ) ;
131
132     dummy = new BibtexSingleField( "date", true ) ;
133     dummy.setPrivate();
134     add( dummy ) ;
135
136
137     // additional fields ------------------------------------------------------
138     dummy =  new BibtexSingleField( "citeseercitationcount", false,
139                                                  GUIGlobals.SMALL_W, 75) ;
140     dummy.setAlternativeDisplayName("Popularity") ;
141     add(dummy) ;
142     add( new BibtexSingleField( "location", false ) ) ;
143     add( new BibtexSingleField( "abstract", false, GUIGlobals.LARGE_W, 400  ) ) ;
144
145     dummy =  new BibtexSingleField( "url", false, GUIGlobals.SMALL_W) ;
146     dummy.setExtras("external");
147     add(dummy) ;
148
149     dummy = new BibtexSingleField( "citeseerurl", false, GUIGlobals.SMALL_W ) ;
150     dummy.setExtras("external");
151     add(dummy) ;
152
153     dummy = new BibtexSingleField( "pdf", false, GUIGlobals.SMALL_W ) ;
154     dummy.setExtras("browseDoc");
155     add(dummy) ;
156
157     dummy = new BibtexSingleField( "ps", false, GUIGlobals.SMALL_W ) ;
158     dummy.setExtras("browseDocZip");
159     add(dummy) ;
160     add( new BibtexSingleField( "comment", false, GUIGlobals.MEDIUM_W  ) ) ;
161     add( new BibtexSingleField( "keywords", false, GUIGlobals.SMALL_W  ) ) ;
162     //FIELD_EXTRAS.put("keywords", "selector");
163
164     add( new BibtexSingleField( "search", false, 75 ) ) ;
165
166
167     // some internal fields ----------------------------------------------
168     dummy = new BibtexSingleField( GUIGlobals.NUMBER_COL, false, 32  ) ;
169     dummy.setPrivate() ;
170     dummy.setWriteable(false);
171     dummy.setDisplayable(false);
172     add( dummy ) ;
173
174     dummy = new BibtexSingleField( OWNER, false, GUIGlobals.SMALL_W ) ;
175     dummy.setPrivate();
176     add(dummy) ;
177
178     dummy = new BibtexSingleField( TIMESTAMP, false, GUIGlobals.SMALL_W ) ;
179     dummy.setExtras("datepicker");
180     dummy.setPrivate();
181     add(dummy) ;
182
183     dummy =  new BibtexSingleField( ENTRYTYPE, false, 75 ) ;
184     dummy.setPrivate();
185     add(dummy) ;
186
187     dummy =  new BibtexSingleField( SEARCH, false) ;
188     dummy.setPrivate();
189     dummy.setWriteable(false);
190     dummy.setDisplayable(false);
191     add(dummy) ;
192
193     dummy =  new BibtexSingleField( GROUPSEARCH, false) ;
194     dummy.setPrivate();
195     dummy.setWriteable(false);
196     dummy.setDisplayable(false);
197     add(dummy) ;
198
199     dummy =  new BibtexSingleField( MARKED, false) ;
200     dummy.setPrivate();
201     dummy.setWriteable(true); // This field must be written to file!
202     dummy.setDisplayable(false);
203     add(dummy) ;
204
205      // read external field definitions
206     readXML( Globals.additionalFields ) ;
207
208     // collect all public fields for the PUBLIC_FIELDS array
209     Vector pFields = new Vector( fieldSet.size()) ;
210     for(Iterator iter = fieldSet.values().iterator(); iter.hasNext() ; )
211     {
212       BibtexSingleField sField = (BibtexSingleField) iter.next() ;
213       if (sField.isPublic() )
214       {
215         pFields.add( sField.getFieldName() );
216         // or export the complet BibtexSingleField ?
217         // BibtexSingleField.toString() { return fieldname ; }
218       }
219     }
220
221     PUBLIC_FIELDS = pFields.toArray() ;
222     // sort the entries
223     java.util.Arrays.sort( PUBLIC_FIELDS );
224   }
225
226
227   /** insert a field into the internal list */
228   private void add( BibtexSingleField field )
229   {
230     // field == null check
231     String key = field.name ;
232     fieldSet.put( key, field ) ;
233   }
234
235   /** read a xml definiton file and put only NEW fields into the field list */
236   private void readXML( String resName )
237   {
238     TXMLReader reader = new TXMLReader(resName) ;
239     if (reader.isReady() )
240     {
241       // get a list of all fields
242       NodeList fieldNodes = reader.getNodes("field") ;
243
244       int tagsCount = fieldNodes.getLength() ;
245       for (int t = 0 ; t < tagsCount ; t++)
246       {
247         Element entry = (Element) fieldNodes.item(t) ;
248         String fName = reader.readStringAttribute(entry, "name", null) ;
249         if (fName != null)  // something found ?
250         {
251           fName = fName.toLowerCase() ;
252           BibtexSingleField dummy = (BibtexSingleField) fieldSet.get( fName ) ;
253           if (dummy == null)  // unknown field
254           {
255             dummy = new BibtexSingleField(reader, entry) ;
256             fieldSet.put(fName, dummy) ;
257           }
258         }
259       }
260     }
261   }
262
263   // --------------------------------------------------------------------------
264   //  the "static area"
265   // --------------------------------------------------------------------------
266   private static final BibtexSingleField getField( String name )
267   {
268     if (name != null)
269     {
270       return (BibtexSingleField) runtime.fieldSet.get(name.toLowerCase()) ;
271     }
272
273     return null ;
274   }
275
276   public static String getFieldExtras( String name )
277   {
278     BibtexSingleField sField = getField( name ) ;
279     if (sField != null)
280     {
281       return sField.getExtras() ;
282     }
283     return null ;
284   }
285
286   public static double getFieldWeight( String name )
287   {
288     BibtexSingleField sField = getField( name ) ;
289     if (sField != null)
290     {
291       return sField.getWeight() ;
292     }
293     return GUIGlobals.DEFAULT_FIELD_WEIGHT ;
294   }
295
296   public static void setFieldWeight( String fieldName, double weight )
297   {
298     BibtexSingleField sField = getField( fieldName ) ;
299     if (sField != null)
300     {
301       sField.setWeight( weight ) ;
302     }
303   }
304
305   public static int getFieldLength( String name )
306   {
307     BibtexSingleField sField = getField( name ) ;
308     if (sField != null)
309     {
310       return sField.getLength() ;
311     }
312     return GUIGlobals.DEFAULT_FIELD_LENGTH ;
313   }
314
315   // returns an alternative name for the given fieldname
316   public static String getFieldDisplayName( String fieldName )
317   {
318     BibtexSingleField sField = getField( fieldName ) ;
319     if (sField != null)
320     {
321       return sField.getAlternativeDisplayName() ;
322     }
323     return null ;
324   }
325
326   public static boolean isWriteableField( String field )
327   {
328     BibtexSingleField sField = getField( field ) ;
329     if (sField != null)
330     {
331       return sField.isWriteable() ;
332     }
333     return true ;
334   }
335
336   public static boolean isDisplayableField( String field )
337   {
338     BibtexSingleField sField = getField( field ) ;
339     if (sField != null)
340     {
341       return sField.isDisplayable() ;
342     }
343     return true ;
344   }
345
346   /**
347    * Returns true if the given field is a standard Bibtex field.
348    *
349    * @param field a <code>String</code> value
350    * @return a <code>boolean</code> value
351    */
352   public static boolean isStandardField( String field )
353   {
354     BibtexSingleField sField = getField( field ) ;
355     if (sField != null)
356     {
357       return sField.isStandard() ;
358     }
359     return false ;
360   }
361
362   /** returns an string-array with all fieldnames */
363   public static Object[] getAllFieldNames()
364   {
365     return runtime.PUBLIC_FIELDS ;
366   }
367
368   /** returns the fieldname of the entry at index t */
369   public static String getFieldName( int t )
370   {
371     return  (String) runtime.PUBLIC_FIELDS[t] ;
372   }
373
374   /** returns the number of available fields */
375   public static int numberOfPublicFields()
376   {
377     return runtime.PUBLIC_FIELDS.length ;
378   }
379
380   /*
381      public static int getPreferredFieldLength(String name) {
382      int l = DEFAULT_FIELD_LENGTH;
383      Object o = fieldLength.get(name.toLowerCase());
384      if (o != null)
385      l = ((Integer)o).intValue();
386      return l;
387      }*/
388
389
390   // --------------------------------------------------------------------------
391   // a container class for all properties of a bibtex-field
392   // --------------------------------------------------------------------------
393   private class BibtexSingleField
394   {
395     private static final int
396         STANDARD       = 0x01,  // it is a standard bibtex-field
397         PRIVATE        = 0x02,  // internal use, e.g. owner, timestamp
398         DISPLAYABLE    = 0x04,  // These fields cannot be shown inside the source editor panel
399         WRITEABLE      = 0x08 ; // These fields will not be saved to the .bib file.
400
401     // the fieldname
402     private String name ;
403
404     // contains the standard, privat, displayable, writable infos
405     // default is: not standard, public, displayable and writable
406     private int flag = DISPLAYABLE | WRITEABLE ;
407
408     private int length = GUIGlobals.DEFAULT_FIELD_LENGTH ;
409     private double weight = GUIGlobals.DEFAULT_FIELD_WEIGHT ;
410
411     // a alternative displayname, e.g. used for
412     // "citeseercitationcount"="Popularity"
413     private String alternativeDisplayName = null ;
414
415     // the extras data
416     // fieldExtras contains mappings to tell the EntryEditor to add a specific
417     // function to this field, for instance a "browse" button for the "pdf" field.
418     private String extras = null ;
419
420     // a comma separated list of alternative bibtex-fieldnames, e.g.
421     // "LCCN" is the same like "lib-congress"
422     // private String otherNames = null ;
423
424     // a Hashmap for a lot of additional "not standard" properties
425     // todo: add the handling in a key=value manner
426     // private HashMap props = new HashMap() ;
427
428     // some constructors ;-)
429     public BibtexSingleField( String fieldName )
430     {
431       name = fieldName ;
432     }
433
434     public BibtexSingleField( String fieldName, boolean pStandard )
435     {
436       name = fieldName ;
437       setFlag( pStandard, STANDARD) ;
438     }
439
440     public BibtexSingleField( String fieldName, boolean pStandard, double pWeight)
441     {
442       name = fieldName ;
443       setFlag( pStandard, STANDARD) ;
444       weight = pWeight ;
445     }
446
447     public BibtexSingleField( String fieldName, boolean pStandard, int pLength)
448     {
449       name = fieldName ;
450       setFlag( pStandard, STANDARD) ;
451       length = pLength ;
452     }
453
454     public BibtexSingleField( String fieldName, boolean pStandard,
455                               double pWeight, int pLength)
456     {
457       name = fieldName ;
458       setFlag( pStandard, STANDARD) ;
459       weight = pWeight ;
460       length = pLength ;
461     }
462
463     /** the constructor reads all neccessary data from the xml file */
464     public BibtexSingleField( TXMLReader reader, Element node)
465     {
466       // default is: not standard, public, displayable and writable
467       flag = DISPLAYABLE | WRITEABLE ;
468
469       name = reader.readStringAttribute(node, "name", "field") ;
470       name = name.toLowerCase() ;
471
472       // read the weight
473       String wStr = reader.readStringAttribute(node, "weight", null) ;
474       if (wStr != null)
475       {
476         int hCode = wStr.toLowerCase().hashCode() ;
477         if (hCode == "small".hashCode())
478         {
479           weight = GUIGlobals.SMALL_W ;
480         }
481         else if (hCode == "medium".hashCode())
482         {
483           weight = GUIGlobals.MEDIUM_W ;
484         }
485         else if (hCode == "large".hashCode())
486         {
487           weight = GUIGlobals.LARGE_W ;
488         }
489         else // try to convert to a double value
490         {
491           try
492           {
493             weight = Double.parseDouble(wStr) ;
494             if ((weight < 0.0) || (weight > GUIGlobals.MAX_FIELD_WEIGHT))
495             {
496               weight = GUIGlobals.DEFAULT_FIELD_WEIGHT ;
497             }
498           }
499           catch (Exception e)
500           {
501             weight = GUIGlobals.DEFAULT_FIELD_WEIGHT ;
502           }
503         }
504       }
505       length = reader.readIntegerAttribute( node, "length", GUIGlobals.DEFAULT_FIELD_LENGTH ) ;
506
507       extras = reader.readStringAttribute(node, "extras", null) ;
508     }
509
510     // -----------------------------------------------------------------------
511     // -----------------------------------------------------------------------
512
513     private void setFlag( boolean onOff, int flagID)
514     {
515       if (onOff)  // set the flag
516       {
517         flag = flag | flagID ;
518       }
519       else // unset the flag,
520       {
521         flag = flag & ( 0xff ^ flagID ) ;
522       }
523     }
524
525     private boolean isSet( int flagID )
526     {
527       if ( (flag & flagID) == flagID)
528         return true ;
529
530       return false ;
531     }
532
533     // -----------------------------------------------------------------------
534     public boolean isStandard()
535     {
536       return isSet( STANDARD ) ;
537     }
538
539     public void setPrivate()
540     {
541       flag = flag | PRIVATE ;
542     }
543
544     public boolean isPrivate()
545     {
546       return isSet( PRIVATE ) ;
547     }
548
549     public void setPublic()
550     {
551       setFlag( false, PRIVATE ) ;
552     }
553
554     public boolean isPublic()
555     {
556       return !isSet( PRIVATE ) ;
557     }
558
559     public void setDisplayable(boolean value)
560     {
561       setFlag( value, DISPLAYABLE ) ;
562     }
563
564     public boolean isDisplayable()
565     {
566       return isSet(DISPLAYABLE) ;
567     }
568
569
570     public void setWriteable(boolean value)
571     {
572       setFlag( value, WRITEABLE ) ;
573     }
574
575     public boolean isWriteable()
576     {
577       return isSet( WRITEABLE ) ;
578     }
579
580     // -----------------------------------------------------------------------
581     public void setAlternativeDisplayName( String aName)
582     {
583       alternativeDisplayName = aName ;
584     }
585
586     public String getAlternativeDisplayName()
587     {
588       return alternativeDisplayName ;
589     }
590     // -----------------------------------------------------------------------
591
592     public void setExtras( String pExtras)
593     {
594       extras = pExtras ;
595     }
596
597     // fieldExtras contains mappings to tell the EntryEditor to add a specific
598     // function to this field, for instance a "browse" button for the "pdf" field.
599     public String getExtras()
600     {
601       return extras ;
602     }
603     // -----------------------------------------------------------------------
604
605     public void setWeight( double value )
606     {
607       this.weight = value ;
608     }
609
610     public double getWeight()
611     {
612       return this.weight ;
613     }
614
615     // -----------------------------------------------------------------------
616     public int getLength()
617     {
618       return this.length ;
619     }
620
621     // -----------------------------------------------------------------------
622
623     public String getFieldName()
624     {
625       return name ;
626     }
627
628   }
629 }