package org.jabref.model.metadata;
import java.nio.charset.Charset;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import org.jabref.model.bibtexkeypattern.AbstractBibtexKeyPattern;
import org.jabref.model.bibtexkeypattern.DatabaseBibtexKeyPattern;
import org.jabref.model.bibtexkeypattern.GlobalBibtexKeyPattern;
import org.jabref.model.cleanup.FieldFormatterCleanups;
import org.jabref.model.database.BibDatabaseMode;
import org.jabref.model.database.event.ChangePropagation;
import org.jabref.model.entry.FieldName;
import org.jabref.model.groups.GroupTreeNode;
import org.jabref.model.groups.event.GroupUpdatedEvent;
import org.jabref.model.metadata.event.MetaDataChangedEvent;
import com.google.common.eventbus.EventBus;
public class MetaData {
public static final String META_FLAG = "jabref-meta: ";
public static final String SAVE_ORDER_CONFIG = "saveOrderConfig";
public static final String SAVE_ACTIONS = "saveActions";
public static final String PREFIX_KEYPATTERN = "keypattern_";
public static final String KEYPATTERNDEFAULT = "keypatterndefault";
public static final String DATABASE_TYPE = "databaseType";
public static final String GROUPSTREE = "grouping";
public static final String GROUPSTREE_LEGACY = "groupstree";
public static final String FILE_DIRECTORY = FieldName.FILE + FileDirectoryPreferences.DIR_SUFFIX;
public static final String PROTECTED_FLAG_META = "protectedFlag";
public static final String SELECTOR_META_PREFIX = "selector_";
public static final char ESCAPE_CHARACTER = '\\';
public static final char SEPARATOR_CHARACTER = ';';
public static final String SEPARATOR_STRING = String.valueOf(SEPARATOR_CHARACTER);
private final EventBus eventBus = new EventBus();
private final Map<String, String> citeKeyPatterns = new HashMap<>(); // <BibType, Pattern>
private final Map<String, String> userFileDirectory = new HashMap<>(); // <User, FilePath>
private GroupTreeNode groupsRoot;
private Charset encoding;
private SaveOrderConfig saveOrderConfig;
private String defaultCiteKeyPattern;
private FieldFormatterCleanups saveActions;
private BibDatabaseMode mode;
private boolean isProtected;
private String defaultFileDirectory;
private ContentSelectors contentSelectors = new ContentSelectors();
private Map<String, List<String>> unkownMetaData = new HashMap<>();
/**
* Constructs an empty metadata.
*/
public MetaData() {
// Do nothing
}
public Optional<SaveOrderConfig> getSaveOrderConfig() {
return Optional.ofNullable(saveOrderConfig);
}
public void setSaveOrderConfig(SaveOrderConfig saveOrderConfig) {
this.saveOrderConfig = saveOrderConfig;
postChange();
}
public Optional<GroupTreeNode> getGroups() {
return Optional.ofNullable(groupsRoot);
}
/**
* Sets a new group root node. <b>WARNING </b>: This invalidates everything
* returned by getGroups() so far!!!
*/
public void setGroups(GroupTreeNode root) {
groupsRoot = Objects.requireNonNull(root);
groupsRoot.subscribeToDescendantChanged(groupTreeNode -> eventBus.post(new GroupUpdatedEvent(this)));
eventBus.post(new GroupUpdatedEvent(this));
}
/**
* @return the stored label patterns
*/
public AbstractBibtexKeyPattern getCiteKeyPattern(GlobalBibtexKeyPattern globalPattern) {
Objects.requireNonNull(globalPattern);
AbstractBibtexKeyPattern bibtexKeyPattern = new DatabaseBibtexKeyPattern(globalPattern);
// Add stored key patterns
citeKeyPatterns.forEach(bibtexKeyPattern::addBibtexKeyPattern);
getDefaultCiteKeyPattern().ifPresent(bibtexKeyPattern::setDefaultValue);
return bibtexKeyPattern;
}
/**
* Updates the stored key patterns to the given key patterns.
*
* @param bibtexKeyPattern the key patterns to update to. <br /> A reference to this object is stored internally and
* is returned at getCiteKeyPattern();
*/
public void setCiteKeyPattern(AbstractBibtexKeyPattern bibtexKeyPattern) {
Objects.requireNonNull(bibtexKeyPattern);
List<String> defaultValue = bibtexKeyPattern.getDefaultValue();
Map<String, List<String>> nonDefaultPatterns = bibtexKeyPattern.getPatterns();
setCiteKeyPattern(defaultValue, nonDefaultPatterns);
}
public void setCiteKeyPattern(List<String> defaultValue, Map<String, List<String>> nonDefaultPatterns) {
// Remove all patterns from metadata
citeKeyPatterns.clear();
// Set new value if it is not a default value
for (Map.Entry<String, List<String>> pattern : nonDefaultPatterns.entrySet()) {
citeKeyPatterns.put(pattern.getKey(), pattern.getValue().get(0));
}
// Store default pattern
if (defaultValue.isEmpty()) {
defaultCiteKeyPattern = null;
} else {
defaultCiteKeyPattern = defaultValue.get(0);
}
postChange();
}
public Optional<FieldFormatterCleanups> getSaveActions() {
return Optional.ofNullable(saveActions);
}
public void setSaveActions(FieldFormatterCleanups saveActions) {
this.saveActions = Objects.requireNonNull(saveActions);
postChange();
}
public Optional<BibDatabaseMode> getMode() {
return Optional.ofNullable(mode);
}
public void setMode(BibDatabaseMode mode) {
this.mode = Objects.requireNonNull(mode);
postChange();
}
public boolean isProtected() {
return isProtected;
}
public ContentSelectors getContentSelectors() {
return contentSelectors;
}
public List<ContentSelector> getContentSelectorList() {
return contentSelectors.getContentSelectors();
}
public void addContentSelector(ContentSelector contentSelector) {
this.contentSelectors.addContentSelector(contentSelector);
postChange();
}
public void clearContentSelectors(String fieldName) {
contentSelectors.removeSelector(fieldName);
postChange();
}
public List<String> getContentSelectorValuesForField(String fieldName) {
return contentSelectors.getSelectorValuesForField(fieldName);
}
public Optional<String> getDefaultFileDirectory() {
return Optional.ofNullable(defaultFileDirectory);
}
public void setDefaultFileDirectory(String path) {
defaultFileDirectory = Objects.requireNonNull(path).trim();
postChange();
}
public Optional<String> getUserFileDirectory(String user) {
return Optional.ofNullable(userFileDirectory.get(user));
}
public void markAsProtected() {
isProtected = true;
postChange();
}
public void clearDefaultFileDirectory() {
defaultFileDirectory = null;
postChange();
}
public void setUserFileDirectory(String user, String path) {
userFileDirectory.put(Objects.requireNonNull(user), Objects.requireNonNull(path));
postChange();
}
public void clearUserFileDirectory(String user) {
userFileDirectory.remove(user);
postChange();
}
public void markAsNotProtected() {
isProtected = false;
postChange();
}
public void clearSaveActions() {
saveActions = null;
postChange();
}
public void clearSaveOrderConfig() {
saveOrderConfig = null;
postChange();
}
/**
* Posts a new {@link MetaDataChangedEvent} on the {@link EventBus}.
*/
private void postChange() {
eventBus.post(new MetaDataChangedEvent(this));
}
/**
* Returns the encoding used during parsing.
*/
public Optional<Charset> getEncoding() {
return Optional.ofNullable(encoding);
}
public void setEncoding(Charset encoding) {
setEncoding(encoding, ChangePropagation.POST_EVENT);
}
/**
* This Method (with additional parameter) has been introduced to avoid event loops while saving a database.
*/
public void setEncoding(Charset encoding, ChangePropagation postChanges) {
this.encoding = Objects.requireNonNull(encoding);
if (postChanges == ChangePropagation.POST_EVENT) {
postChange();
}
}
public void registerListener(Object listener) {
this.eventBus.register(listener);
}
public void unregisterListener(Object listener) {
try {
this.eventBus.unregister(listener);
} catch (IllegalArgumentException e) {
// occurs if the event source has not been registered, should not prevent shutdown
}
}
private Optional<String> getDefaultCiteKeyPattern() {
return Optional.ofNullable(defaultCiteKeyPattern);
}
public boolean isEmpty() {
return this.equals(new MetaData());
}
public Map<String, String> getUserFileDirectories() {
return Collections.unmodifiableMap(userFileDirectory);
}
public Map<String, List<String>> getUnknownMetaData() {
return Collections.unmodifiableMap(unkownMetaData);
}
public void putUnkownMetaDataItem(String key, List<String> value) {
Objects.requireNonNull(key);
Objects.requireNonNull(value);
unkownMetaData.put(key, value);
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if ((o == null) || (getClass() != o.getClass())) {
return false;
}
MetaData metaData = (MetaData) o;
return (isProtected == metaData.isProtected) && Objects.equals(groupsRoot, metaData.groupsRoot)
&& Objects.equals(encoding, metaData.encoding)
&& Objects.equals(saveOrderConfig, metaData.saveOrderConfig)
&& Objects.equals(citeKeyPatterns, metaData.citeKeyPatterns)
&& Objects.equals(userFileDirectory, metaData.userFileDirectory)
&& Objects.equals(defaultCiteKeyPattern, metaData.defaultCiteKeyPattern)
&& Objects.equals(saveActions, metaData.saveActions) && (mode == metaData.mode)
&& Objects.equals(defaultFileDirectory, metaData.defaultFileDirectory)
&& Objects.equals(contentSelectors, metaData.contentSelectors);
}
@Override
public int hashCode() {
return Objects.hash(groupsRoot, encoding, saveOrderConfig, citeKeyPatterns, userFileDirectory,
defaultCiteKeyPattern, saveActions, mode, isProtected, defaultFileDirectory);
}
}