net.sf.jabref.MetaData.java Source code

Java tutorial

Introduction

Here is the source code for net.sf.jabref.MetaData.java

Source

/*  Copyright (C) 2003-2015 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.
*/
package net.sf.jabref;

import java.io.IOException;
import java.io.Reader;
import java.io.StringReader;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.TreeMap;
import java.util.Vector;

import net.sf.jabref.importer.fileformat.ParseException;
import net.sf.jabref.logic.config.SaveOrderConfig;
import net.sf.jabref.logic.exporter.FieldFormatterCleanups;
import net.sf.jabref.logic.groups.GroupTreeNode;
import net.sf.jabref.logic.l10n.Localization;
import net.sf.jabref.logic.labelpattern.AbstractLabelPattern;
import net.sf.jabref.logic.labelpattern.DatabaseLabelPattern;
import net.sf.jabref.logic.util.strings.StringUtil;
import net.sf.jabref.model.database.BibDatabaseMode;
import net.sf.jabref.model.entry.FieldName;
import net.sf.jabref.sql.DBStrings;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

public class MetaData implements Iterable<String> {

    private static final Log LOGGER = LogFactory.getLog(MetaData.class);
    public static final String META_FLAG = "jabref-meta: ";
    private static final String SAVE_ORDER_CONFIG = "saveOrderConfig";

    private static final String SAVE_ACTIONS = "saveActions";
    private static final String PREFIX_KEYPATTERN = "keypattern_";
    private static final String KEYPATTERNDEFAULT = "keypatterndefault";
    private static final String DATABASE_TYPE = "databaseType";

    private static final String GROUPSTREE = "groupstree";
    private static final String FILE_DIRECTORY = FieldName.FILE + Globals.DIR_SUFFIX;
    public static final String SELECTOR_META_PREFIX = "selector_";
    private static final String PROTECTED_FLAG_META = "protectedFlag";

    private final Map<String, List<String>> metaData = new HashMap<>();
    private GroupTreeNode groupsRoot;

    private AbstractLabelPattern labelPattern;

    private DBStrings dbStrings = new DBStrings();

    private Charset encoding = Globals.prefs.getDefaultEncoding();

    /**
     * The MetaData object stores all meta data sets in Vectors. To ensure that
     * the data is written correctly to string, the user of a meta data Vector
     * must simply make sure the appropriate changes are reflected in the Vector
     * it has been passed.
     */
    private MetaData(Map<String, String> inData) throws ParseException {
        Objects.requireNonNull(inData);

        for (Map.Entry<String, String> entry : inData.entrySet()) {
            StringReader data = new StringReader(entry.getValue());
            List<String> orderedData = new ArrayList<>();
            // We must allow for ; and \ in escape sequences.
            try {
                Optional<String> unit;
                while ((unit = getNextUnit(data)).isPresent()) {
                    orderedData.add(unit.get());
                }
            } catch (IOException ex) {
                LOGGER.error("Weird error while parsing meta data.", ex);
            }
            if (GROUPSTREE.equals(entry.getKey())) {
                putGroups(orderedData);
                // the keys "groupsversion" and "groups" were used in JabRef versions around 1.3, we will not support them anymore
            } else if (SAVE_ACTIONS.equals(entry.getKey())) {
                setSaveActions(FieldFormatterCleanups.parse(orderedData));
            } else {
                putData(entry.getKey(), orderedData);
            }
        }
    }

    /**
     * The MetaData object can be constructed with no data in it.
     */
    public MetaData() {
        // No data
    }

    public static MetaData parse(Map<String, String> data) throws ParseException {
        return new MetaData(data);
    }

    public Optional<SaveOrderConfig> getSaveOrderConfig() {
        List<String> storedSaveOrderConfig = getData(SAVE_ORDER_CONFIG);
        if (storedSaveOrderConfig != null) {
            return Optional.of(SaveOrderConfig.parse(storedSaveOrderConfig));
        }
        return Optional.empty();
    }

    /**
     * Add default metadata for new database:
     */
    public void initializeNewDatabase() {
        metaData.put(SELECTOR_META_PREFIX + FieldName.KEYWORDS, new Vector<>());
        metaData.put(SELECTOR_META_PREFIX + FieldName.AUTHOR, new Vector<>());
        metaData.put(SELECTOR_META_PREFIX + FieldName.JOURNAL, new Vector<>());
        metaData.put(SELECTOR_META_PREFIX + FieldName.PUBLISHER, new Vector<>());
        metaData.put(SELECTOR_META_PREFIX + FieldName.REVIEW, new Vector<>());
    }

    /**
     * @return Iterator on all keys stored in the metadata
     */
    @Override
    public Iterator<String> iterator() {
        return metaData.keySet().iterator();
    }

    /**
     * Retrieves the stored meta data.
     *
     * @param key the key to look up
     * @return null if no data is found
     */
    public List<String> getData(String key) {
        return metaData.get(key);
    }

    /**
     * Removes the given key from metadata.
     * Nothing is done if key is not found.
     *
     * @param key the key to remove
     */
    public void remove(String key) {
        metaData.remove(key);
    }

    /**
     * Stores the specified data in this object, using the specified key. For
     * certain keys (e.g. "groupstree"), the objects in orderedData are
     * reconstructed from their textual (String) representation if they are of
     * type String, and stored as an actual instance.
     */
    public void putData(String key, List<String> orderedData) {
        metaData.put(key, orderedData);
    }

    /**
     * Parse the groups metadata string
     *
     * @param orderedData The vector of metadata strings
     */
    private void putGroups(List<String> orderedData) throws ParseException {
        try {
            groupsRoot = GroupTreeNode.parse(orderedData, Globals.prefs);
        } catch (ParseException e) {
            throw new ParseException(Localization.lang(
                    "Group tree could not be parsed. If you save the BibTeX database, all groups will be lost."),
                    e);
        }
    }

    public GroupTreeNode getGroups() {
        return groupsRoot;
    }

    /**
     * Sets a new group root node. <b>WARNING </b>: This invalidates everything
     * returned by getGroups() so far!!!
     */
    public void setGroups(GroupTreeNode root) {
        groupsRoot = root;
    }

    /**
     * Reads the next unit. Units are delimited by ';'.
     */
    private static Optional<String> getNextUnit(Reader reader) throws IOException {
        int c;
        boolean escape = false;
        StringBuilder res = new StringBuilder();
        while ((c = reader.read()) != -1) {
            if (escape) {
                res.append((char) c);
                escape = false;
            } else if (c == '\\') {
                escape = true;
            } else if (c == ';') {
                break;
            } else {
                res.append((char) c);
            }
        }
        if (res.length() > 0) {
            return Optional.of(res.toString());
        }
        return Optional.empty();
    }

    public DBStrings getDBStrings() {
        return dbStrings;
    }

    public void setDBStrings(DBStrings dbStrings) {
        this.dbStrings = dbStrings;
    }

    /**
     * @return the stored label patterns
     */
    public AbstractLabelPattern getLabelPattern() {
        if (labelPattern != null) {
            return labelPattern;
        }

        labelPattern = new DatabaseLabelPattern(Globals.prefs);

        // read the data from the metadata and store it into the labelPattern
        for (String key : this) {
            if (key.startsWith(PREFIX_KEYPATTERN)) {
                List<String> value = getData(key);
                String type = key.substring(PREFIX_KEYPATTERN.length());
                labelPattern.addLabelPattern(type, value.get(0));
            }
        }
        List<String> defaultPattern = getData(KEYPATTERNDEFAULT);
        if (defaultPattern != null) {
            labelPattern.setDefaultValue(defaultPattern.get(0));
        }

        return labelPattern;
    }

    /**
     * Updates the stored key patterns to the given key patterns.
     *
     * @param labelPattern the key patterns to update to. <br />
     *                     A reference to this object is stored internally and is returned at getLabelPattern();
     */
    public void setLabelPattern(AbstractLabelPattern labelPattern) {
        // remove all keypatterns from metadata
        Iterator<String> iterator = this.iterator();
        while (iterator.hasNext()) {
            String key = iterator.next();
            if (key.startsWith(PREFIX_KEYPATTERN)) {
                iterator.remove();
            }
        }

        // set new value if it is not a default value
        Set<String> allKeys = labelPattern.getAllKeys();
        for (String key : allKeys) {
            String metaDataKey = PREFIX_KEYPATTERN + key;
            if (!labelPattern.isDefaultValue(key)) {
                List<String> data = new ArrayList<>();
                data.add(labelPattern.getValue(key).get(0));
                this.putData(metaDataKey, data);
            }
        }

        // store default pattern
        if (labelPattern.getDefaultValue() == null) {
            this.remove(KEYPATTERNDEFAULT);
        } else {
            List<String> data = new ArrayList<>();
            data.add(labelPattern.getDefaultValue().get(0));
            this.putData(KEYPATTERNDEFAULT, data);
        }

        this.labelPattern = labelPattern;
    }

    public Optional<FieldFormatterCleanups> getSaveActions() {
        if (this.getData(SAVE_ACTIONS) == null) {
            return Optional.empty();
        } else {
            return Optional.of(FieldFormatterCleanups.parse(getData(SAVE_ACTIONS)));
        }
    }

    public Optional<BibDatabaseMode> getMode() {
        List<String> data = getData(DATABASE_TYPE);
        if ((data == null) || data.isEmpty()) {
            return Optional.empty();
        }
        return Optional.of(BibDatabaseMode.parse(data.get(0)));
    }

    public boolean isProtected() {
        List<String> data = getData(PROTECTED_FLAG_META);
        if ((data == null) || data.isEmpty()) {
            return false;
        } else {
            return Boolean.parseBoolean(data.get(0));
        }
    }

    public List<String> getContentSelectors(String fieldName) {
        List<String> contentSelectors = getData(SELECTOR_META_PREFIX + fieldName);
        if (contentSelectors == null) {
            return Collections.emptyList();
        } else {
            return contentSelectors;
        }
    }

    public Optional<String> getDefaultFileDirectory() {
        List<String> fileDirectory = getData(FILE_DIRECTORY);
        if ((fileDirectory == null) || fileDirectory.isEmpty()) {
            return Optional.empty();
        } else {
            return Optional.of(fileDirectory.get(0).trim());
        }
    }

    public Optional<String> getUserFileDirectory(String user) {
        List<String> fileDirectory = getData(FILE_DIRECTORY + '-' + user);
        if ((fileDirectory == null) || fileDirectory.isEmpty()) {
            return Optional.empty();
        } else {
            return Optional.of(fileDirectory.get(0).trim());
        }
    }

    /**
     * Writes all data in the format <key, serialized data>.
     */
    public Map<String, String> getAsStringMap() {

        Map<String, String> serializedMetaData = new TreeMap<>();

        // first write all meta data except groups
        for (Map.Entry<String, List<String>> metaItem : metaData.entrySet()) {

            StringBuilder stringBuilder = new StringBuilder();
            for (String dataItem : metaItem.getValue()) {
                stringBuilder.append(StringUtil.quote(dataItem, ";", '\\')).append(";");

                //in case of save actions, add an additional newline after the enabled flag
                if (metaItem.getKey().equals(SAVE_ACTIONS)
                        && ("enabled".equals(dataItem) || "disabled".equals(dataItem))) {
                    stringBuilder.append(Globals.NEWLINE);
                }
            }

            String serializedItem = stringBuilder.toString();
            // Only add non-empty values
            if (!serializedItem.isEmpty() && !";".equals(serializedItem)) {
                serializedMetaData.put(metaItem.getKey(), serializedItem);
            }
        }

        // write groups if present. skip this if only the root node exists
        // (which is always the AllEntriesGroup).
        if ((groupsRoot != null) && (groupsRoot.getNumberOfChildren() > 0)) {
            StringBuilder stringBuilder = new StringBuilder();
            stringBuilder.append(Globals.NEWLINE);

            for (String groupNode : groupsRoot.getTreeAsString()) {
                stringBuilder.append(StringUtil.quote(groupNode, ";", '\\'));
                stringBuilder.append(";");
                stringBuilder.append(Globals.NEWLINE);
            }
            serializedMetaData.put(GROUPSTREE, stringBuilder.toString());
        }

        return serializedMetaData;
    }

    public void setSaveActions(FieldFormatterCleanups saveActions) {
        List<String> actionsSerialized = saveActions.getAsStringList();
        putData(SAVE_ACTIONS, actionsSerialized);
    }

    public void setSaveOrderConfig(SaveOrderConfig saveOrderConfig) {
        List<String> serialized = saveOrderConfig.getAsStringList();
        putData(SAVE_ORDER_CONFIG, serialized);
    }

    public void setMode(BibDatabaseMode mode) {
        putData(DATABASE_TYPE, Collections.singletonList(mode.getAsString()));
    }

    public void markAsProtected() {
        putData(PROTECTED_FLAG_META, Collections.singletonList("true"));
    }

    public void setContentSelectors(String fieldName, List<String> contentSelectors) {
        putData(SELECTOR_META_PREFIX + fieldName, contentSelectors);
    }

    public void setDefaultFileDirectory(String path) {
        putData(FILE_DIRECTORY, Collections.singletonList(path));
    }

    public void clearDefaultFileDirectory() {
        remove(FILE_DIRECTORY);
    }

    public void setUserFileDirectory(String user, String path) {
        putData(FILE_DIRECTORY + '-' + user, Collections.singletonList(path.trim()));
    }

    public void clearUserFileDirectory(String user) {
        remove(FILE_DIRECTORY + '-' + user);
    }

    public void clearContentSelectors(String fieldName) {
        remove(SELECTOR_META_PREFIX + fieldName);
    }

    public void markAsNotProtected() {
        remove(PROTECTED_FLAG_META);
    }

    public void clearSaveActions() {
        remove(SAVE_ACTIONS);
    }

    public void clearSaveOrderConfig() {
        remove(SAVE_ORDER_CONFIG);
    }

    /**
     * Returns the encoding used during parsing.
     */
    public Charset getEncoding() {
        return encoding;
    }

    public void setEncoding(Charset encoding) {
        this.encoding = Objects.requireNonNull(encoding);
    }
}