package org.jabref.model;
import java.util.Arrays;
import java.util.Collection;
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.TreeMap;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import org.jabref.model.database.BibDatabaseMode;
import org.jabref.model.entry.BiblatexEntryTypes;
import org.jabref.model.entry.BibtexEntryTypes;
import org.jabref.model.entry.CustomEntryType;
import org.jabref.model.entry.EntryType;
import org.jabref.model.entry.IEEETranEntryTypes;
public class EntryTypes {
/**
* This class is used to specify entry types for either BIBTEX and BIBLATEX.
*/
private static class InternalEntryTypes {
private final Map<String, EntryType> ALL_TYPES = new TreeMap<>();
private final Map<String, EntryType> STANDARD_TYPES;
private final EntryType defaultType;
public InternalEntryTypes(EntryType defaultType, List<List<EntryType>> entryTypes) {
this.defaultType = defaultType;
for (List<EntryType> list : entryTypes) {
for (EntryType type : list) {
ALL_TYPES.put(type.getName().toLowerCase(Locale.ROOT), type);
}
}
STANDARD_TYPES = new TreeMap<>(ALL_TYPES);
}
/**
* This method returns the BibtexEntryType for the name of a type,
* or null if it does not exist.
*/
public Optional<EntryType> getType(String name) {
return Optional.ofNullable(ALL_TYPES.get(name.toLowerCase(Locale.ROOT)));
}
/**
* This method returns the EntryType for the name of a type,
* or the default type (*.MISC) if it does not exist.
*/
// Get an entry type defined in BibtexEntryType
public EntryType getTypeOrDefault(String type) {
return getType(type).orElse(defaultType);
}
/**
* This method returns the standard BibtexEntryType for the
* name of a type, or null if it does not exist.
*/
public Optional<EntryType> getStandardType(String name) {
return Optional.ofNullable(STANDARD_TYPES.get(name.toLowerCase(Locale.ROOT)));
}
private void addOrModifyEntryType(EntryType type) {
ALL_TYPES.put(type.getName().toLowerCase(Locale.ROOT), type);
}
public Set<String> getAllTypes() {
return ALL_TYPES.keySet();
}
public Collection<EntryType> getAllValues() {
return ALL_TYPES.values();
}
/**
* Removes a customized entry type from the type map. If this type
* overrode a standard type, we reinstate the standard one.
*
* @param name The customized entry type to remove.
*/
public void removeType(String name) {
String toLowerCase = name.toLowerCase(Locale.ROOT);
if (!ALL_TYPES.get(toLowerCase).equals(STANDARD_TYPES.get(toLowerCase))) {
ALL_TYPES.remove(toLowerCase);
if (STANDARD_TYPES.containsKey(toLowerCase)) {
// In this case the user has removed a customized version
// of a standard type. We reinstate the standard type.
addOrModifyEntryType(STANDARD_TYPES.get(toLowerCase));
}
}
}
}
public static final InternalEntryTypes BIBTEX = new InternalEntryTypes(BibtexEntryTypes.MISC,
Arrays.asList(BibtexEntryTypes.ALL, IEEETranEntryTypes.ALL));
public static final InternalEntryTypes BIBLATEX = new InternalEntryTypes(BiblatexEntryTypes.MISC,
Arrays.asList(BiblatexEntryTypes.ALL));
private EntryTypes() {
}
/**
* This method returns the BibtexEntryType for the name of a type,
* or null if it does not exist.
*/
public static Optional<EntryType> getType(String name, BibDatabaseMode type) {
return type == BibDatabaseMode.BIBLATEX ? BIBLATEX.getType(name) : BIBTEX.getType(name);
}
/**
* This method returns the EntryType for the name of a type,
* or the default type (*.MISC) if it does not exist.
*/
// Get an entry type defined in BibtexEntryType
public static EntryType getTypeOrDefault(String name, BibDatabaseMode mode) {
return mode == BibDatabaseMode.BIBLATEX ? BIBLATEX.getTypeOrDefault(name) : BIBTEX.getTypeOrDefault(name);
}
/**
* This method returns the standard BibtexEntryType for the
* name of a type, or null if it does not exist.
*/
public static Optional<EntryType> getStandardType(String name, BibDatabaseMode mode) {
return mode == BibDatabaseMode.BIBLATEX ? BIBLATEX.getStandardType(name) : BIBTEX.getStandardType(name);
}
public static void addOrModifyCustomEntryType(CustomEntryType customEntryType, BibDatabaseMode mode) {
if (BibDatabaseMode.BIBLATEX == mode) {
BIBLATEX.addOrModifyEntryType(customEntryType);
} else if (BibDatabaseMode.BIBTEX == mode) {
BIBTEX.addOrModifyEntryType(customEntryType);
}
}
public static Set<String> getAllTypes(BibDatabaseMode type) {
return type == BibDatabaseMode.BIBLATEX ? BIBLATEX.getAllTypes() : BIBTEX.getAllTypes();
}
public static Collection<EntryType> getAllValues(BibDatabaseMode type) {
return type == BibDatabaseMode.BIBLATEX ? BIBLATEX.getAllValues() : BIBTEX.getAllValues();
}
/**
* Determine all CustomTypes which are not overwritten standard types but real custom types for a given BibDatabaseMode
*
* I.e., a modified "article" type will not be included in the list, but an EntryType like "MyCustomType" will be included.
*
* @param mode the BibDatabaseMode to be checked
* @return the list of all found custom types
*/
public static List<EntryType> getAllCustomTypes(BibDatabaseMode mode) {
Collection<EntryType> allTypes = getAllValues(mode);
if (mode == BibDatabaseMode.BIBTEX) {
return allTypes.stream().filter(entryType -> !BibtexEntryTypes.getType(entryType.getName()).isPresent())
.filter(entryType -> !IEEETranEntryTypes.getType(entryType.getName()).isPresent())
.collect(Collectors.toList());
} else {
return allTypes.stream().filter(entryType -> !BiblatexEntryTypes.getType(entryType.getName()).isPresent())
.collect(Collectors.toList());
}
}
public static List<EntryType> getAllModifiedStandardTypes(BibDatabaseMode mode) {
if (mode == BibDatabaseMode.BIBTEX) {
return getAllModifiedStandardTypes(BIBTEX);
} else {
return getAllModifiedStandardTypes(BIBLATEX);
}
}
private static List<EntryType> getAllModifiedStandardTypes(InternalEntryTypes internalTypes) {
return internalTypes.getAllValues().stream().filter(type -> type instanceof CustomEntryType)
.filter(type -> internalTypes.getStandardType(type.getName()).isPresent())
.collect(Collectors.toList());
}
/**
* Removes a customized entry type from the type map. If this type
* overrode a standard type, we reinstate the standard one.
*
* @param name The customized entry type to remove.
*/
public static void removeType(String name, BibDatabaseMode type) {
if (type == BibDatabaseMode.BIBLATEX) {
BIBLATEX.removeType(name);
} else {
BIBTEX.removeType(name);
}
}
public static void removeAllCustomEntryTypes() {
for (BibDatabaseMode type : BibDatabaseMode.values()) {
for (String typeName : new HashSet<>(getAllTypes(type))) {
getType(typeName, type).ifPresent(entryType -> {
if (entryType instanceof CustomEntryType) {
removeType(typeName, type);
}
});
}
}
}
/**
* Load given custom entry types for BibTeX and biblatex mode
*/
public static void loadCustomEntryTypes(List<CustomEntryType> customBibtexEntryTypes, List<CustomEntryType> customBiblatexEntryTypes) {
for (CustomEntryType type : customBibtexEntryTypes) {
EntryTypes.addOrModifyCustomEntryType(type, BibDatabaseMode.BIBTEX);
}
for (CustomEntryType type : customBiblatexEntryTypes) {
EntryTypes.addOrModifyCustomEntryType(type, BibDatabaseMode.BIBLATEX);
}
}
/**
* Checks whether two EntryTypes are equal or not based on the equality of the type names and on the equality of
* the required and optional field lists
*
* @param type1 the first EntryType to compare
* @param type2 the secend EntryType to compare
* @return returns true if the two compared entry types have the same name and equal required and optional fields
*/
public static boolean isEqualNameAndFieldBased(EntryType type1, EntryType type2) {
if ((type1 == null) && (type2 == null)) {
return true;
} else if ((type1 == null) || (type2 == null)) {
return false;
} else {
return type1.getName().equals(type2.getName())
&& type1.getRequiredFields().equals(type2.getRequiredFields())
&& type1.getOptionalFields().equals(type2.getOptionalFields())
&& type1.getSecondaryOptionalFields().equals(type2.getSecondaryOptionalFields());
}
}
public static boolean isExclusiveBiblatex(String type) {
return filterEntryTypesNames(BiblatexEntryTypes.ALL, isNotIncludedIn(BibtexEntryTypes.ALL)).contains(type.toLowerCase(Locale.ROOT));
}
private static List<String> filterEntryTypesNames(List<EntryType> types, Predicate<EntryType> predicate) {
return types.stream().filter(predicate).map(type -> type.getName().toLowerCase(Locale.ROOT)).collect(Collectors.toList());
}
private static Predicate<EntryType> isNotIncludedIn(List<EntryType> collection) {
return entry -> collection.stream().noneMatch(c -> c.getName().equalsIgnoreCase(entry.getName()));
}
}