/* Copyright (C) 2003-2011 Raik Nagel and JabRef contributors This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ // // function : Handling of bibtex fields. // All bibtex-field related stuff should be placed here! // Because we can export these informations into additional // config files -> simple extension and definition of new fields.... // // todo : - handling of identically fields with different names // e.g. LCCN = lib-congress // - group id for each fields, e.g. standard, jurabib, bio.... // - add a additional properties functionality into the // BibtexSingleField class // // modified : r.nagel 25.04.2006 // export/import of some definition from/to a xml file package net.sf.jabref ; import java.util.HashMap; import java.util.Vector; import java.util.HashSet; import net.sf.jabref.specialfields.SpecialFieldsUtils; import net.sf.jabref.util.TXMLReader; import org.w3c.dom.Element; import org.w3c.dom.NodeList; public class BibtexFields { public static final String KEY_FIELD = "bibtexkey" ; // some internal fields public static final String SEARCH = "__search", GROUPSEARCH = "__groupsearch", MARKED = "__markedentry", OWNER = "owner", TIMESTAMP = "timestamp", // it's also definied at the JabRefPreferences class ENTRYTYPE = "entrytype", // Using this when I have no database open or when I read // non bibtex file formats (used by the ImportFormatReader.java) DEFAULT_BIBTEXENTRY_ID = "__ID" ; public static final String[] DEFAULT_INSPECTION_FIELDS = new String[] {"author", "title", "year", KEY_FIELD}; // singleton instance private static final BibtexFields runtime = new BibtexFields() ; // contains all bibtex-field objects (BibtexSingleField) private HashMap<String, BibtexSingleField> fieldSet; // contains all known (and public) bibtex fieldnames private String[] PUBLIC_FIELDS = null ; private BibtexFields() { fieldSet = new HashMap<String, BibtexSingleField>(); BibtexSingleField dummy = null ; // FIRST: all standard fields // These are the fields that BibTex might want to treat, so these // must conform to BibTex rules. add( new BibtexSingleField( "address", true, GUIGlobals.SMALL_W ) ) ; // An annotation. It is not used by the standard bibliography styles, // but may be used by others that produce an annotated bibliography. // http://www.ecst.csuchico.edu/~jacobsd/bib/formats/bibtex.html add( new BibtexSingleField( "annote", true, GUIGlobals.LARGE_W ) ) ; add( new BibtexSingleField( "author", true, GUIGlobals.MEDIUM_W, 280 ) ) ; add( new BibtexSingleField( "booktitle", true, 175 ) ) ; add( new BibtexSingleField( "chapter", true, GUIGlobals.SMALL_W ) ) ; add( new BibtexSingleField( "crossref", true, GUIGlobals.SMALL_W ) ) ; add( new BibtexSingleField( "edition", true, GUIGlobals.SMALL_W ) ) ; add( new BibtexSingleField( "editor", true, GUIGlobals.MEDIUM_W, 280 ) ) ; add( new BibtexSingleField( "howpublished", true, GUIGlobals.MEDIUM_W ) ) ; add( new BibtexSingleField( "institution", true, GUIGlobals.MEDIUM_W ) ) ; dummy = new BibtexSingleField( "journal", true, GUIGlobals.SMALL_W ) ; dummy.setExtras("journalNames"); add(dummy) ; add( new BibtexSingleField( "key", true ) ) ; add( new BibtexSingleField( "month", true, GUIGlobals.SMALL_W ) ) ; add( new BibtexSingleField( "note", true, GUIGlobals.MEDIUM_W ) ) ; add( new BibtexSingleField( "number", true, GUIGlobals.SMALL_W, 60 ).setNumeric(true) ) ; add( new BibtexSingleField( "organization", true, GUIGlobals.MEDIUM_W ) ) ; add( new BibtexSingleField( "pages", true, GUIGlobals.SMALL_W ) ) ; add( new BibtexSingleField( "publisher", true, GUIGlobals.MEDIUM_W ) ) ; add( new BibtexSingleField( "school", true, GUIGlobals.MEDIUM_W ) ) ; add( new BibtexSingleField( "series", true, GUIGlobals.SMALL_W ) ) ; add( new BibtexSingleField( "title", true, 400 ) ) ; add( new BibtexSingleField( "type", true, GUIGlobals.SMALL_W ) ) ; add( new BibtexSingleField( "language", true, GUIGlobals.SMALL_W ) ) ; add( new BibtexSingleField( "volume", true, GUIGlobals.SMALL_W, 60 ).setNumeric(true) ) ; add( new BibtexSingleField( "year", true, GUIGlobals.SMALL_W, 60 ).setNumeric(true) ) ; // custom fields not displayed at editor, but as columns in the UI dummy = new BibtexSingleField( SpecialFieldsUtils.FIELDNAME_RANKING , false) ; if (!Globals.prefs.getBoolean(SpecialFieldsUtils.PREF_SERIALIZESPECIALFIELDS)) { dummy.setPrivate(); dummy.setWriteable(false); dummy.setDisplayable(false); } add(dummy) ; dummy = new BibtexSingleField( SpecialFieldsUtils.FIELDNAME_PRIORITY , false) ; if (!Globals.prefs.getBoolean(SpecialFieldsUtils.PREF_SERIALIZESPECIALFIELDS)) { dummy.setPrivate(); dummy.setWriteable(false); dummy.setDisplayable(false); } add(dummy) ; dummy = new BibtexSingleField( SpecialFieldsUtils.FIELDNAME_RELEVANCE , false) ; if (!Globals.prefs.getBoolean(SpecialFieldsUtils.PREF_SERIALIZESPECIALFIELDS)) { dummy.setPrivate(); dummy.setWriteable(false); dummy.setDisplayable(false); } add(dummy) ; dummy = new BibtexSingleField( SpecialFieldsUtils.FIELDNAME_QUALITY , false) ; if (!Globals.prefs.getBoolean(SpecialFieldsUtils.PREF_SERIALIZESPECIALFIELDS)) { dummy.setPrivate(); dummy.setWriteable(false); dummy.setDisplayable(false); } add(dummy) ; // some semi-standard fields dummy = new BibtexSingleField( KEY_FIELD, true ) ; dummy.setPrivate(); add( dummy ) ; dummy = new BibtexSingleField( "doi", true, GUIGlobals.SMALL_W ) ; dummy.setExtras("external"); add(dummy) ; add( new BibtexSingleField( "eid", true, GUIGlobals.SMALL_W ) ) ; dummy = new BibtexSingleField( "date", true ) ; dummy.setPrivate(); add( dummy ) ; add(new BibtexSingleField("pmid", false, GUIGlobals.SMALL_W, 60).setNumeric(true)); // additional fields ------------------------------------------------------ add( new BibtexSingleField( "location", false ) ) ; add( new BibtexSingleField( "abstract", false, GUIGlobals.LARGE_W, 400 ) ) ; dummy = new BibtexSingleField( "url", false, GUIGlobals.SMALL_W) ; dummy.setExtras("external"); add(dummy) ; dummy = new BibtexSingleField( "pdf", false, GUIGlobals.SMALL_W ) ; dummy.setExtras("browseDoc"); add(dummy) ; dummy = new BibtexSingleField( "ps", false, GUIGlobals.SMALL_W ) ; dummy.setExtras("browseDocZip"); add(dummy) ; add( new BibtexSingleField( "comment", false, GUIGlobals.MEDIUM_W ) ) ; add( new BibtexSingleField( "keywords", false, GUIGlobals.SMALL_W ) ) ; //FIELD_EXTRAS.put("keywords", "selector"); dummy = new BibtexSingleField(GUIGlobals.FILE_FIELD, false); dummy.setEditorType(GUIGlobals.FILE_LIST_EDITOR); add(dummy); add( new BibtexSingleField( "search", false, 75 ) ) ; // some internal fields ---------------------------------------------- dummy = new BibtexSingleField( GUIGlobals.NUMBER_COL, false, 32 ) ; dummy.setPrivate() ; dummy.setWriteable(false); dummy.setDisplayable(false); add( dummy ) ; dummy = new BibtexSingleField( OWNER, false, GUIGlobals.SMALL_W ) ; dummy.setExtras("setOwner"); dummy.setPrivate(); add(dummy) ; dummy = new BibtexSingleField( TIMESTAMP, false, GUIGlobals.SMALL_W ) ; dummy.setExtras("datepicker"); dummy.setPrivate(); add(dummy) ; dummy = new BibtexSingleField( ENTRYTYPE, false, 75 ) ; dummy.setPrivate(); add(dummy) ; dummy = new BibtexSingleField( SEARCH, false) ; dummy.setPrivate(); dummy.setWriteable(false); dummy.setDisplayable(false); add(dummy) ; dummy = new BibtexSingleField( GROUPSEARCH, false) ; dummy.setPrivate(); dummy.setWriteable(false); dummy.setDisplayable(false); add(dummy) ; dummy = new BibtexSingleField( MARKED, false) ; dummy.setPrivate(); dummy.setWriteable(true); // This field must be written to file! dummy.setDisplayable(false); add(dummy) ; // read external field definitions readXML( Globals.additionalFields ) ; // collect all public fields for the PUBLIC_FIELDS array Vector<String> pFields = new Vector<String>( fieldSet.size()) ; for (BibtexSingleField sField : fieldSet.values()){ if (sField.isPublic() ) { pFields.add( sField.getFieldName() ); // or export the complet BibtexSingleField ? // BibtexSingleField.toString() { return fieldname ; } } } PUBLIC_FIELDS = pFields.toArray(new String[pFields.size()]); // sort the entries java.util.Arrays.sort( PUBLIC_FIELDS ); } /** * Read the "numericFields" string array from preferences, and activate numeric * sorting for all fields listed in the array. If an unknown field name is included, * add a field descriptor for the new field. */ public static void setNumericFieldsFromPrefs() { String[] numFields = Globals.prefs.getStringArray("numericFields"); if (numFields == null) return; // Build a Set of field names for the fields that should be sorted numerically: HashSet<String> nF = new HashSet<String>(); for (int i = 0; i < numFields.length; i++) { nF.add(numFields[i]); } // Look through all registered fields, and activate numeric sorting if necessary: for (String fieldName : runtime.fieldSet.keySet()) { BibtexSingleField field = runtime.fieldSet.get(fieldName); if (!field.isNumeric() && nF.contains(fieldName)) { field.setNumeric(nF.contains(fieldName)); } nF.remove(fieldName); // remove, so we clear the set of all standard fields. } // If there are fields left in nF, these must be non-standard fields. Add descriptors for them: for (String fieldName : nF) { BibtexSingleField field = new BibtexSingleField(fieldName, false); field.setNumeric(true); runtime.fieldSet.put(fieldName, field); } } /** insert a field into the internal list */ private void add( BibtexSingleField field ) { // field == null check String key = field.name ; fieldSet.put( key, field ) ; } /** read a xml definiton file and put only NEW fields into the field list */ private void readXML( String resName ) { TXMLReader reader = new TXMLReader(resName) ; if (reader.isReady() ) { // get a list of all fields NodeList fieldNodes = reader.getNodes("field") ; int tagsCount = fieldNodes.getLength() ; for (int t = 0 ; t < tagsCount ; t++) { Element entry = (Element) fieldNodes.item(t) ; String fName = reader.readStringAttribute(entry, "name", null) ; if (fName != null) // something found ? { fName = fName.toLowerCase() ; BibtexSingleField dummy = fieldSet.get( fName ) ; if (dummy == null) // unknown field { dummy = new BibtexSingleField(reader, entry) ; fieldSet.put(fName, dummy) ; } } } } } // -------------------------------------------------------------------------- // the "static area" // -------------------------------------------------------------------------- private static final BibtexSingleField getField( String name ) { if (name != null) { return runtime.fieldSet.get(name.toLowerCase()) ; } return null ; } public static String getFieldExtras( String name ) { BibtexSingleField sField = getField( name ) ; if (sField != null) { return sField.getExtras() ; } return null ; } public static int getEditorType(String name) { BibtexSingleField sField = getField( name ) ; if (sField != null) { return sField.getEditorType(); } return GUIGlobals.STANDARD_EDITOR; } public static double getFieldWeight( String name ) { BibtexSingleField sField = getField( name ) ; if (sField != null) { return sField.getWeight() ; } return GUIGlobals.DEFAULT_FIELD_WEIGHT ; } public static void setFieldWeight( String fieldName, double weight ) { BibtexSingleField sField = getField( fieldName ) ; if (sField != null) { sField.setWeight( weight ) ; } } public static int getFieldLength( String name ) { BibtexSingleField sField = getField( name ) ; if (sField != null) { return sField.getLength() ; } return GUIGlobals.DEFAULT_FIELD_LENGTH ; } // returns an alternative name for the given fieldname public static String getFieldDisplayName( String fieldName ) { BibtexSingleField sField = getField( fieldName ) ; if (sField != null) { return sField.getAlternativeDisplayName() ; } return null ; } public static boolean isWriteableField( String field ) { BibtexSingleField sField = getField( field ) ; if (sField != null) { return sField.isWriteable() ; } return true ; } public static boolean isDisplayableField( String field ) { BibtexSingleField sField = getField( field ) ; if (sField != null) { return sField.isDisplayable() ; } return true ; } /** * Returns true if the given field is a standard Bibtex field. * * @param field a <code>String</code> value * @return a <code>boolean</code> value */ public static boolean isStandardField( String field ) { BibtexSingleField sField = getField( field ) ; if (sField != null) { return sField.isStandard() ; } return false ; } public static boolean isNumeric( String field ) { BibtexSingleField sField = getField( field ) ; if (sField != null) { return sField.isNumeric() ; } return false ; } /** returns an string-array with all fieldnames */ public static String[] getAllFieldNames() { return runtime.PUBLIC_FIELDS ; } /** returns the fieldname of the entry at index t */ public static String getFieldName( int t ) { return runtime.PUBLIC_FIELDS[t] ; } /** returns the number of available fields */ public static int numberOfPublicFields() { return runtime.PUBLIC_FIELDS.length ; } /* public static int getPreferredFieldLength(String name) { int l = DEFAULT_FIELD_LENGTH; Object o = fieldLength.get(name.toLowerCase()); if (o != null) l = ((Integer)o).intValue(); return l; }*/ // -------------------------------------------------------------------------- // a container class for all properties of a bibtex-field // -------------------------------------------------------------------------- private static class BibtexSingleField { private static final int STANDARD = 0x01, // it is a standard bibtex-field PRIVATE = 0x02, // internal use, e.g. owner, timestamp DISPLAYABLE = 0x04, // These fields cannot be shown inside the source editor panel WRITEABLE = 0x08 ; // These fields will not be saved to the .bib file. // the fieldname private String name ; // contains the standard, privat, displayable, writable infos // default is: not standard, public, displayable and writable private int flag = DISPLAYABLE | WRITEABLE ; private int length = GUIGlobals.DEFAULT_FIELD_LENGTH ; private double weight = GUIGlobals.DEFAULT_FIELD_WEIGHT ; private int editorType = GUIGlobals.STANDARD_EDITOR; // a alternative displayname, e.g. used for // "citeseercitationcount"="Popularity" private String alternativeDisplayName = null ; // the extras data // fieldExtras contains mappings to tell the EntryEditor to add a specific // function to this field, for instance a "browse" button for the "pdf" field. private String extras = null ; // This value defines whether contents of this field are expected to be // numeric values. This can be used to sort e.g. volume numbers correctly: private boolean numeric = false; // a comma separated list of alternative bibtex-fieldnames, e.g. // "LCCN" is the same like "lib-congress" // private String otherNames = null ; // a Hashmap for a lot of additional "not standard" properties // todo: add the handling in a key=value manner // private HashMap props = new HashMap() ; // some constructors ;-) public BibtexSingleField( String fieldName ) { name = fieldName ; } public BibtexSingleField( String fieldName, boolean pStandard ) { name = fieldName ; setFlag( pStandard, STANDARD) ; } public BibtexSingleField( String fieldName, boolean pStandard, double pWeight) { name = fieldName ; setFlag( pStandard, STANDARD) ; weight = pWeight ; } public BibtexSingleField( String fieldName, boolean pStandard, int pLength) { name = fieldName ; setFlag( pStandard, STANDARD) ; length = pLength ; } public BibtexSingleField( String fieldName, boolean pStandard, double pWeight, int pLength) { name = fieldName ; setFlag( pStandard, STANDARD) ; weight = pWeight ; length = pLength ; } /** the constructor reads all neccessary data from the xml file */ public BibtexSingleField( TXMLReader reader, Element node) { // default is: not standard, public, displayable and writable flag = DISPLAYABLE | WRITEABLE ; name = reader.readStringAttribute(node, "name", "field") ; name = name.toLowerCase() ; // read the weight String wStr = reader.readStringAttribute(node, "weight", null) ; if (wStr != null) { int hCode = wStr.toLowerCase().hashCode() ; if (hCode == "small".hashCode()) { weight = GUIGlobals.SMALL_W ; } else if (hCode == "medium".hashCode()) { weight = GUIGlobals.MEDIUM_W ; } else if (hCode == "large".hashCode()) { weight = GUIGlobals.LARGE_W ; } else // try to convert to a double value { try { weight = Double.parseDouble(wStr) ; if ((weight < 0.0) || (weight > GUIGlobals.MAX_FIELD_WEIGHT)) { weight = GUIGlobals.DEFAULT_FIELD_WEIGHT ; } } catch (Exception e) { weight = GUIGlobals.DEFAULT_FIELD_WEIGHT ; } } } length = reader.readIntegerAttribute( node, "length", GUIGlobals.DEFAULT_FIELD_LENGTH ) ; extras = reader.readStringAttribute(node, "extras", null) ; } // ----------------------------------------------------------------------- // ----------------------------------------------------------------------- private void setFlag( boolean onOff, int flagID) { if (onOff) // set the flag { flag = flag | flagID ; } else // unset the flag, { flag = flag & ( 0xff ^ flagID ) ; } } private boolean isSet( int flagID ) { if ( (flag & flagID) == flagID) return true ; return false ; } // ----------------------------------------------------------------------- public boolean isStandard() { return isSet( STANDARD ) ; } public void setPrivate() { flag = flag | PRIVATE ; } public boolean isPrivate() { return isSet( PRIVATE ) ; } public void setPublic() { setFlag( false, PRIVATE ) ; } public boolean isPublic() { return !isSet( PRIVATE ) ; } public void setDisplayable(boolean value) { setFlag( value, DISPLAYABLE ) ; } public boolean isDisplayable() { return isSet(DISPLAYABLE) ; } public void setWriteable(boolean value) { setFlag( value, WRITEABLE ) ; } public boolean isWriteable() { return isSet( WRITEABLE ) ; } // ----------------------------------------------------------------------- public void setAlternativeDisplayName( String aName) { alternativeDisplayName = aName ; } public String getAlternativeDisplayName() { return alternativeDisplayName ; } // ----------------------------------------------------------------------- public void setExtras( String pExtras) { extras = pExtras ; } // fieldExtras contains mappings to tell the EntryEditor to add a specific // function to this field, for instance a "browse" button for the "pdf" field. public String getExtras() { return extras ; } public void setEditorType(int type) { editorType = type; } public int getEditorType() { return editorType; } // ----------------------------------------------------------------------- public void setWeight( double value ) { this.weight = value ; } public double getWeight() { return this.weight ; } // ----------------------------------------------------------------------- public int getLength() { return this.length ; } // ----------------------------------------------------------------------- public String getFieldName() { return name ; } /** * Set this field's numeric propery * @param numeric true to indicate that this is a numeric field. * @return this BibtexSingleField instance. Makes it easier to call this * method on the fly while initializing without using a local variable. */ public BibtexSingleField setNumeric(boolean numeric) { this.numeric = numeric; return this; } public boolean isNumeric() { return numeric; } } }