package org.jabref.model.entry; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.EnumSet; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; import org.jabref.model.entry.specialfields.SpecialField; /** * Handling of bibtex fields. * All bibtex-field related stuff should be placed here! * Because we can export this information into additional * config files -> simple extension and definition of new fields * * TODO: * - handling of identically fields with different names (https://github.com/JabRef/jabref/issues/521) * e.g. LCCN = lib-congress, journaltitle = journal * - group id for each fields, e.g. standard, jurabib, bio, ... * - add a additional properties functionality into the BibtexSingleField class */ public class InternalBibtexFields { /** * These are the fields JabRef always displays as default * {@link org.jabref.preferences.JabRefPreferences#setLanguageDependentDefaultValues()} * * A user can change them. The change is currently stored in the preferences only and not explicitley exposed as separte preferences object */ public static final List<String> DEFAULT_GENERAL_FIELDS = Arrays.asList(FieldName.CROSSREF, FieldName.KEYWORDS, FieldName.FILE, FieldName.DOI, FieldName.URL, FieldName.COMMENT, FieldName.OWNER, FieldName.TIMESTAMP); // Lists of fields with special properties private static final List<String> INTEGER_FIELDS = Arrays.asList(FieldName.CTLMAX_NAMES_FORCED_ETAL, FieldName.CTLNAMES_SHOW_ETAL, FieldName.CTLALT_STRETCH_FACTOR, FieldName.VOLUMES, FieldName.PMID); private static final List<String> IEEETRANBSTCTL_YES_NO_FIELDS = Arrays.asList(FieldName.CTLUSE_ARTICLE_NUMBER, FieldName.CTLUSE_PAPER, FieldName.CTLUSE_URL, FieldName.CTLUSE_FORCED_ETAL, FieldName.CTLUSE_ALT_SPACING, FieldName.CTLDASH_REPEATED_NAMES); private static final List<String> BIBLATEX_DATE_FIELDS = Arrays.asList(FieldName.DATE, FieldName.EVENTDATE, FieldName.ORIGDATE, FieldName.URLDATE); private static final List<String> BIBLATEX_PERSON_NAME_FIELDS = Arrays.asList(FieldName.AUTHOR, FieldName.EDITOR, FieldName.EDITORA, FieldName.EDITORB, FieldName.EDITORC, FieldName.TRANSLATOR, FieldName.ANNOTATOR, FieldName.COMMENTATOR, FieldName.INTRODUCTION, FieldName.FOREWORD, FieldName.AFTERWORD, FieldName.BOOKAUTHOR, FieldName.HOLDER, FieldName.SHORTAUTHOR, FieldName.SHORTEDITOR, FieldName.SORTNAME, FieldName.NAMEADDON, FieldName.ASSIGNEE); private static final List<String> BIBLATEX_EDITOR_TYPE_FIELDS = Arrays.asList(FieldName.EDITORTYPE, FieldName.EDITORATYPE, FieldName.EDITORBTYPE, FieldName.EDITORCTYPE); private static final List<String> BIBLATEX_PAGINATION_FIELDS = Arrays.asList(FieldName.PAGINATION, FieldName.BOOKPAGINATION); private static final List<String> BIBLATEX_JOURNAL_NAME_FIELDS = Arrays.asList(FieldName.JOURNAL, FieldName.JOURNALTITLE, FieldName.JOURNALSUBTITLE); private static final List<String> BIBLATEX_BOOK_NAME_FIELDS = Arrays.asList(FieldName.BOOKTITLE, FieldName.MAINTITLE, FieldName.MAINSUBTITLE, FieldName.MAINTITLEADDON, FieldName.BOOKSUBTITLE, FieldName.BOOKTITLEADDON); private static final List<String> BIBLATEX_LANGUAGE_FIELDS = Arrays.asList(FieldName.LANGUAGE, FieldName.ORIGLANGUAGE); private static final List<String> BIBLATEX_MULTI_KEY_FIELDS = Arrays.asList(FieldName.RELATED, FieldName.ENTRYSET); private static final List<String> VERBATIM_FIELDS = Arrays.asList(FieldName.URL, FieldName.FILE, FieldName.CTLNAME_FORMAT_STRING, FieldName.CTLNAME_LATEX_CMD, FieldName.CTLNAME_URL_PREFIX); private static final List<String> SPECIAL_FIELDS = Arrays.asList(SpecialField.PRINTED.getFieldName(), SpecialField.PRIORITY.getFieldName(), SpecialField.QUALITY.getFieldName(), SpecialField.RANKING.getFieldName(), SpecialField.READ_STATUS.getFieldName(), SpecialField.RELEVANCE.getFieldName()); // singleton instance private static InternalBibtexFields RUNTIME = new InternalBibtexFields(FieldName.TIMESTAMP); // contains all bibtex-field objects (BibtexSingleField) private final Map<String, BibtexSingleField> fieldSet; // the name with the current time stamp field, needed in case we want to change it private String timeStampField; private InternalBibtexFields(String timeStampFieldName) { fieldSet = new HashMap<>(); BibtexSingleField dummy; // FIRST: all standard fields // These are the fields that BibTeX might want to treat, so these // must conform to BibTeX rules. add(new BibtexSingleField(FieldName.ADDRESS, true, BibtexSingleField.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(FieldName.ANNOTE, true, BibtexSingleField.LARGE_W)); add(new BibtexSingleField(FieldName.AUTHOR, true, BibtexSingleField.MEDIUM_W, 280)); add(new BibtexSingleField(FieldName.BOOKTITLE, true, 175)); add(new BibtexSingleField(FieldName.CHAPTER, true, BibtexSingleField.SMALL_W)); dummy = new BibtexSingleField(FieldName.CROSSREF, true, BibtexSingleField.LARGE_W); dummy.setExtras(EnumSet.of(FieldProperty.CROSSREF, FieldProperty.SINGLE_ENTRY_LINK)); add(dummy); add(new BibtexSingleField(FieldName.EDITION, true, BibtexSingleField.SMALL_W)); add(new BibtexSingleField(FieldName.EDITOR, true, BibtexSingleField.MEDIUM_W, 280)); dummy = new BibtexSingleField(FieldName.EPRINT, true, BibtexSingleField.SMALL_W); dummy.setExtras(EnumSet.of(FieldProperty.EPRINT)); add(dummy); add(new BibtexSingleField(FieldName.HOWPUBLISHED, true, BibtexSingleField.MEDIUM_W)); add(new BibtexSingleField(FieldName.INSTITUTION, true, BibtexSingleField.MEDIUM_W)); dummy = new BibtexSingleField(FieldName.ISBN, true, BibtexSingleField.SMALL_W); dummy.setExtras(EnumSet.of(FieldProperty.ISBN)); add(dummy); add(new BibtexSingleField(FieldName.ISSN, true, BibtexSingleField.SMALL_W)); dummy = new BibtexSingleField(FieldName.JOURNAL, true, BibtexSingleField.SMALL_W); dummy.setExtras(EnumSet.of(FieldProperty.JOURNAL_NAME)); add(dummy); dummy = new BibtexSingleField(FieldName.JOURNALTITLE, true, BibtexSingleField.SMALL_W); dummy.setExtras(EnumSet.of(FieldProperty.JOURNAL_NAME)); add(dummy); add(new BibtexSingleField(FieldName.KEY, true)); dummy = new BibtexSingleField(FieldName.MONTH, true, BibtexSingleField.SMALL_W); dummy.setExtras(EnumSet.of(FieldProperty.MONTH)); add(dummy); add(new BibtexSingleField(FieldName.NOTE, true, BibtexSingleField.MEDIUM_W)); add(new BibtexSingleField(FieldName.NUMBER, true, BibtexSingleField.SMALL_W, 60).setNumeric(true)); add(new BibtexSingleField(FieldName.ORGANIZATION, true, BibtexSingleField.MEDIUM_W)); add(new BibtexSingleField(FieldName.PAGES, true, BibtexSingleField.SMALL_W)); add(new BibtexSingleField(FieldName.PUBLISHER, true, BibtexSingleField.MEDIUM_W)); add(new BibtexSingleField(FieldName.SCHOOL, true, BibtexSingleField.MEDIUM_W)); add(new BibtexSingleField(FieldName.SERIES, true, BibtexSingleField.SMALL_W)); add(new BibtexSingleField(FieldName.TITLE, true, 400)); dummy = new BibtexSingleField(FieldName.TYPE, true, BibtexSingleField.SMALL_W); dummy.getFieldProperties().add(FieldProperty.TYPE); add(dummy); add(new BibtexSingleField(FieldName.LANGUAGE, true, BibtexSingleField.SMALL_W)); add(new BibtexSingleField(FieldName.VOLUME, true, BibtexSingleField.SMALL_W, 60).setNumeric(true)); add(new BibtexSingleField(FieldName.YEAR, true, BibtexSingleField.SMALL_W, 60).setNumeric(true)); // custom fields not displayed at editor, but as columns in the UI for (String fieldName : SPECIAL_FIELDS) { dummy = new BibtexSingleField(fieldName, false); dummy.setPrivate(); dummy.setWriteable(false); dummy.setDisplayable(false); add(dummy); } // some semi-standard fields dummy = new BibtexSingleField(BibEntry.KEY_FIELD, true); dummy.setPrivate(); add(dummy); dummy = new BibtexSingleField(FieldName.DOI, true, BibtexSingleField.SMALL_W); dummy.setExtras(EnumSet.of(FieldProperty.DOI)); add(dummy); add(new BibtexSingleField(FieldName.EID, true, BibtexSingleField.SMALL_W)); dummy = new BibtexSingleField(FieldName.DATE, true); dummy.setExtras(EnumSet.of(FieldProperty.DATE)); add(dummy); add(new BibtexSingleField(FieldName.PMID, false, BibtexSingleField.SMALL_W, 60).setNumeric(true)); // additional fields ------------------------------------------------------ add(new BibtexSingleField(FieldName.LOCATION, false)); add(new BibtexSingleField(FieldName.ABSTRACT, false, BibtexSingleField.LARGE_W, 400)); dummy = new BibtexSingleField(FieldName.URL, false, BibtexSingleField.SMALL_W); dummy.setExtras(EnumSet.of(FieldProperty.EXTERNAL, FieldProperty.VERBATIM)); add(dummy); add(new BibtexSingleField(FieldName.COMMENT, false, BibtexSingleField.MEDIUM_W)); add(new BibtexSingleField(FieldName.KEYWORDS, false, BibtexSingleField.SMALL_W)); dummy = new BibtexSingleField(FieldName.FILE, false); dummy.setExtras(EnumSet.of(FieldProperty.FILE_EDITOR, FieldProperty.VERBATIM)); add(dummy); dummy = new BibtexSingleField(FieldName.RELATED, false); dummy.setExtras(EnumSet.of(FieldProperty.MULTIPLE_ENTRY_LINK)); add(dummy); // some biblatex fields dummy = new BibtexSingleField(FieldName.GENDER, true, BibtexSingleField.SMALL_W); dummy.getFieldProperties().add(FieldProperty.GENDER); add(dummy); dummy = new BibtexSingleField(FieldName.PUBSTATE, true, BibtexSingleField.SMALL_W); dummy.getFieldProperties().add(FieldProperty.PUBLICATION_STATE); add(dummy); // some internal fields ---------------------------------------------- dummy = new BibtexSingleField(FieldName.NUMBER_COL, false, 32); dummy.setPrivate(); dummy.setWriteable(false); dummy.setDisplayable(false); add(dummy); dummy = new BibtexSingleField(FieldName.OWNER, false, BibtexSingleField.SMALL_W); dummy.setExtras(EnumSet.of(FieldProperty.OWNER)); dummy.setPrivate(); add(dummy); timeStampField = timeStampFieldName; dummy = new BibtexSingleField(timeStampFieldName, false, BibtexSingleField.SMALL_W); dummy.setExtras(EnumSet.of(FieldProperty.DATE)); dummy.setPrivate(); add(dummy); dummy = new BibtexSingleField(BibEntry.TYPE_HEADER, false, 75); dummy.setPrivate(); add(dummy); dummy = new BibtexSingleField(FieldName.SEARCH_INTERNAL, false); dummy.setPrivate(); dummy.setWriteable(false); dummy.setDisplayable(false); add(dummy); dummy = new BibtexSingleField(FieldName.GROUPSEARCH_INTERNAL, false); dummy.setPrivate(); dummy.setWriteable(false); dummy.setDisplayable(false); add(dummy); dummy = new BibtexSingleField(FieldName.MARKED_INTERNAL, false); dummy.setPrivate(); dummy.setWriteable(true); // This field must be written to file! dummy.setDisplayable(false); add(dummy); // IEEEtranBSTCTL fields that should be "yes" or "no" for (String yesNoField : IEEETRANBSTCTL_YES_NO_FIELDS) { dummy = new BibtexSingleField(yesNoField, false); dummy.setExtras(EnumSet.of(FieldProperty.YES_NO)); add(dummy); } // Fields that should be an integer value for (String numericField : INTEGER_FIELDS) { BibtexSingleField field = fieldSet.get(numericField); if (field == null) { field = new BibtexSingleField(numericField, true, BibtexSingleField.SMALL_W).setNumeric(true); } field.getFieldProperties().add(FieldProperty.INTEGER); add(field); } // Fields that should be treated as verbatim, so no formatting requirements for (String fieldText : VERBATIM_FIELDS) { BibtexSingleField field = fieldSet.get(fieldText); if (field == null) { field = new BibtexSingleField(fieldText, true, BibtexSingleField.SMALL_W); } field.getFieldProperties().add(FieldProperty.VERBATIM); add(field); } // Set all fields with person names for (String fieldText : BIBLATEX_PERSON_NAME_FIELDS) { BibtexSingleField field = fieldSet.get(fieldText); if (field == null) { field = new BibtexSingleField(fieldText, true, BibtexSingleField.SMALL_W); } field.getFieldProperties().add(FieldProperty.PERSON_NAMES); add(field); } // Set all fields which should contain editor types for (String fieldText : BIBLATEX_EDITOR_TYPE_FIELDS) { BibtexSingleField field = fieldSet.get(fieldText); if (field == null) { field = new BibtexSingleField(fieldText, true, BibtexSingleField.SMALL_W); } field.getFieldProperties().add(FieldProperty.EDITOR_TYPE); add(field); } // Set all fields which are pagination fields for (String fieldText : BIBLATEX_PAGINATION_FIELDS) { BibtexSingleField field = fieldSet.get(fieldText); if (field == null) { field = new BibtexSingleField(fieldText, true, BibtexSingleField.SMALL_W); } field.getFieldProperties().add(FieldProperty.PAGINATION); add(field); } // Set all fields with dates for (String fieldText : BIBLATEX_DATE_FIELDS) { BibtexSingleField field = fieldSet.get(fieldText); if (field == null) { field = new BibtexSingleField(fieldText, true, BibtexSingleField.SMALL_W); } field.getFieldProperties().add(FieldProperty.DATE); field.getFieldProperties().add(FieldProperty.ISO_DATE); add(field); } // Set all fields with journal names for (String fieldText : BIBLATEX_JOURNAL_NAME_FIELDS) { BibtexSingleField field = fieldSet.get(fieldText); if (field == null) { field = new BibtexSingleField(fieldText, true, BibtexSingleField.SMALL_W); } field.getFieldProperties().add(FieldProperty.JOURNAL_NAME); add(field); } // Set all fields with book names for (String fieldText : BIBLATEX_BOOK_NAME_FIELDS) { BibtexSingleField field = fieldSet.get(fieldText); if (field == null) { field = new BibtexSingleField(fieldText, true, BibtexSingleField.SMALL_W); } field.getFieldProperties().add(FieldProperty.BOOK_NAME); add(field); } // Set all fields containing a language for (String fieldText : BIBLATEX_LANGUAGE_FIELDS) { BibtexSingleField field = fieldSet.get(fieldText); if (field == null) { field = new BibtexSingleField(fieldText, true, BibtexSingleField.SMALL_W); } field.getFieldProperties().add(FieldProperty.LANGUAGE); add(field); } // Set all fields with multiple key links for (String fieldText : BIBLATEX_MULTI_KEY_FIELDS) { BibtexSingleField field = fieldSet.get(fieldText); if (field == null) { field = new BibtexSingleField(fieldText, true, BibtexSingleField.SMALL_W); } field.getFieldProperties().add(FieldProperty.MULTIPLE_ENTRY_LINK); add(field); } } public static void updateTimeStampField(String timeStampFieldName) { getField(RUNTIME.timeStampField).ifPresent(field -> { field.setName(timeStampFieldName); RUNTIME.timeStampField = timeStampFieldName; }); } public static void updateSpecialFields(boolean serializeSpecialFields) { for (String fieldName : SPECIAL_FIELDS) { getField(fieldName).ifPresent(field -> { if (serializeSpecialFields) { field.setPublic(); } else { field.setPrivate(); } field.setWriteable(serializeSpecialFields); field.setDisplayable(serializeSpecialFields); }); } } /** * 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 setNumericFields(List<String> numFields) { if (numFields.isEmpty()) { return; } // Build a Set of field names for the fields that should be sorted numerically: Set<String> nF = new HashSet<>(); nF.addAll(numFields); // Look through all registered fields, and activate numeric sorting if necessary: for (String fieldName : InternalBibtexFields.RUNTIME.fieldSet.keySet()) { BibtexSingleField field = InternalBibtexFields.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); InternalBibtexFields.RUNTIME.fieldSet.put(fieldName, field); } } /** * insert a field into the internal list */ private void add(BibtexSingleField field) { // field == null check String key = field.getFieldName(); fieldSet.put(key, field); } // -------------------------------------------------------------------------- // the "static area" // -------------------------------------------------------------------------- private static Optional<BibtexSingleField> getField(String name) { if (name != null) { return Optional.ofNullable(InternalBibtexFields.RUNTIME.fieldSet.get(name.toLowerCase(Locale.ENGLISH))); } return Optional.empty(); } public static Set<FieldProperty> getFieldProperties(String name) { Optional<BibtexSingleField> sField = InternalBibtexFields.getField(name); if (sField.isPresent()) { return sField.get().getFieldProperties(); } return EnumSet.noneOf(FieldProperty.class); } public static double getFieldWeight(String name) { Optional<BibtexSingleField> sField = InternalBibtexFields.getField(name); if (sField.isPresent()) { return sField.get().getWeight(); } return BibtexSingleField.DEFAULT_FIELD_WEIGHT; } public static void setFieldWeight(String fieldName, double weight) { Optional<BibtexSingleField> sField = InternalBibtexFields.getField(fieldName); if (sField.isPresent()) { sField.get().setWeight(weight); } } public static int getFieldLength(String name) { Optional<BibtexSingleField> sField = InternalBibtexFields.getField(name); if (sField.isPresent()) { return sField.get().getLength(); } return BibtexSingleField.DEFAULT_FIELD_LENGTH; } public static boolean isWriteableField(String field) { Optional<BibtexSingleField> sField = InternalBibtexFields.getField(field); return !sField.isPresent() || sField.get().isWriteable(); } public static boolean isDisplayableField(String field) { Optional<BibtexSingleField> sField = InternalBibtexFields.getField(field); return !sField.isPresent() || sField.get().isDisplayable(); } /** * 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) { Optional<BibtexSingleField> sField = InternalBibtexFields.getField(field); return sField.isPresent() && sField.get().isStandard(); } public static boolean isNumeric(String field) { Optional<BibtexSingleField> sField = InternalBibtexFields.getField(field); return sField.isPresent() && sField.get().isNumeric(); } public static boolean isInternalField(String field) { return field.startsWith("__"); } /** * returns a List with all fieldnames */ public static List<String> getAllPublicFieldNames() { // collect all public fields List<String> publicFields = new ArrayList<>(); for (BibtexSingleField sField : InternalBibtexFields.RUNTIME.fieldSet.values()) { if (!sField.isPrivate()) { publicFields.add(sField.getFieldName()); // or export the complete BibtexSingleField ? // BibtexSingleField.toString() { return fieldname ; } } } // sort the entries Collections.sort(publicFields); return publicFields; } /** * returns a List with all fieldnames incl. internal fieldnames */ public static List<String> getAllPublicAndInternalFieldNames() { //add the internal field names to public fields List<String> publicAndInternalFields = new ArrayList<>(); publicAndInternalFields.addAll(InternalBibtexFields.getAllPublicFieldNames()); publicAndInternalFields.add(FieldName.INTERNAL_ALL_FIELD); publicAndInternalFields.add(FieldName.INTERNAL_ALL_TEXT_FIELDS_FIELD); // sort the entries Collections.sort(publicAndInternalFields); return publicAndInternalFields; } public static List<String> getJournalNameFields() { return InternalBibtexFields.getAllPublicFieldNames().stream().filter( fieldName -> InternalBibtexFields.getFieldProperties(fieldName).contains(FieldProperty.JOURNAL_NAME)) .collect(Collectors.toList()); } public static List<String> getBookNameFields() { return InternalBibtexFields.getAllPublicFieldNames().stream() .filter(fieldName -> InternalBibtexFields.getFieldProperties(fieldName).contains(FieldProperty.BOOK_NAME)) .collect(Collectors.toList()); } public static List<String> getPersonNameFields() { return InternalBibtexFields.getAllPublicFieldNames().stream().filter( fieldName -> InternalBibtexFields.getFieldProperties(fieldName).contains(FieldProperty.PERSON_NAMES)) .collect(Collectors.toList()); } public static List<String> getIEEETranBSTctlYesNoFields() { return IEEETRANBSTCTL_YES_NO_FIELDS; } }