/*
* Zettelkasten - nach Luhmann
* Copyright (C) 2001-2015 by Daniel Lüdecke (http://www.danielluedecke.de)
*
* Homepage: http://zettelkasten.danielluedecke.de
*
*
* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*
* Dieses Programm ist freie Software. Sie können es unter den Bedingungen der GNU
* General Public License, wie von der Free Software Foundation veröffentlicht, weitergeben
* und/oder modifizieren, entweder gemäß Version 3 der Lizenz oder (wenn Sie möchten)
* jeder späteren Version.
*
* Die Veröffentlichung dieses Programms erfolgt in der Hoffnung, daß es Ihnen von Nutzen sein
* wird, aber OHNE IRGENDEINE GARANTIE, sogar ohne die implizite Garantie der MARKTREIFE oder
* der VERWENDBARKEIT FÜR EINEN BESTIMMTEN ZWECK. Details finden Sie in der
* GNU General Public License.
*
* Sie sollten ein Exemplar der GNU General Public License zusammen mit diesem Programm
* erhalten haben. Falls nicht, siehe <http://www.gnu.org/licenses/>.
*/
package de.danielluedecke.zettelkasten.database;
import bibtex.dom.BibtexAbstractValue;
import bibtex.dom.BibtexEntry;
import bibtex.dom.BibtexFile;
import bibtex.dom.BibtexNode;
import bibtex.parser.BibtexParser;
import bibtex.parser.ParseException;
import de.danielluedecke.zettelkasten.ZettelkastenView;
import de.danielluedecke.zettelkasten.util.Constants;
import de.danielluedecke.zettelkasten.util.FileOperationsUtil;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
import java.util.regex.Pattern;
import javax.swing.JOptionPane;
/**
* This class is responsible for managing bibtex files.<br><br>
* Usually, first of all a file must be opened ("attached") using the
* {@link #openAttachedFile(java.lang.String) openAttachedFile(java.lang.String)}
* method. after that, all entries are stored in the variable
* {@link #bibtexfile bibtexfile}, while the single bibtex entries are stored in
* {@link #attachedbibtexentries}.
* <br><br>
* With this class, you can then retrieve single entries, retrieve bibtex
* entries (i.e. author values) in a certain citation style etc.
* <br><br>
* This class is mainly used for importing literatur values from a bibtex file
* (see
* <b>CImportBibTex</b>) or changing bibkey values from entry's author values
* (see
* <b>CSetBibKey</b>).
*
* @author danielludecke
*/
public class BibTex {
/**
* A reference to the settings-class (CSettings)
*/
private final Settings settingsObj;
/**
* The main variable that stors the currently opened bibtex-file
*/
private BibtexFile bibtexfile = new BibtexFile();
/**
* This array stores all single entries from the attached bibtex file
* {@code bibtexfile}.
*/
private final ArrayList<BibtexEntry> attachedbibtexentries = new ArrayList<>();
/**
* This array stores all single entries from the attached bibtex file
* {@code bibtexfile}.
*/
private final ArrayList<BibtexEntry> bibtexentries = new ArrayList<>();
/**
* Thi array stores bibtex entries that should be exported. Since bibtex
* entries that should be exported may contain only a selection of all
* bibtex entries of the currently opened bibtex file, we use an extra array
* to store export entrie.
*/
private final ArrayList<BibtexEntry> outputbibtexentries = new ArrayList<>();
/**
* Stores the file path to the currently opened bibtex file.
*/
private File currentlyattachedfile = null;
/**
* Stores the <b>general</b> citation style which is used as Zettelkasten
* default.<br><br>
* Used when the user requests a formatted bibtex entry via
* {@link #getFormattedEntryFromAttachedFile(int) getFormattedEntryFromAttachedFile()}.
* The bibtex entry (i.e. author value) is formatted according to the
* selected citation style.
*/
private final List<Map<String, String>> importtypes = new ArrayList<>();
/**
* Stores the <b>CBE</b> citation style.<br><br>
* Used when the user requests a formatted bibtex entry via
* {@link #getFormattedEntryFromAttachedFile(int) getFormattedEntryFromAttachedFile()}.
* The bibtex entry (i.e. author value) is formatted according to the
* selected citation style.
*/
private final List<Map<String, String>> importtypesCBE = new ArrayList<>();
/**
* Stores the <b>APA</b> citation style.<br><br>
* Used when the user requests a formatted bibtex entry via
* {@link #getFormattedEntryFromAttachedFile(int) getFormattedEntryFromAttachedFile()}.
* The bibtex entry (i.e. author value) is formatted according to the
* selected citation style.
*/
private final List<Map<String, String>> importtypesAPA = new ArrayList<>();
/**
* A variable indicating which citation-style is used when requesting a
* formatted bibtex-entry (see
* {@link #getFormattedEntry(bibtex.dom.BibtexEntry) getFormattedEntryFromAttachedFile()}).
*/
private int citestyle = Constants.BIBTEX_CITE_STYLE_GENERAL;
private boolean modified;
private final String editorToken = "°###°";
/**
* Reference to the main frame.
*/
private final ZettelkastenView zknframe;
/**
* get the strings for file descriptions from the resource map
*/
private final static org.jdesktop.application.ResourceMap resourceMap
= org.jdesktop.application.Application.getInstance(de.danielluedecke.zettelkasten.ZettelkastenApp.class).
getContext().getResourceMap(ZettelkastenView.class);
public BibTex(ZettelkastenView zkn, Settings s) {
zknframe = zkn;
settingsObj = s;
modified = false;
initStyles();
}
/**
* sets the modified state. should be called whenever changes have been made
* to the desktopfile (set it to true) or when the data has been saved (set
* it to false)
*
* @param m true when changes are unsaved, false otherwise
*/
public void setModified(boolean m) {
modified = m;
// update indicator for autobackup
zknframe.setBackupNecessary();
}
/**
* Checks whether the datafile is modified
*
* @return {@code true} if it is modified, false otherwise
*/
public boolean isModified() {
return modified;
}
private void initStyles() {
initStyleGeneral();
initStyleAPA();
initStyleCBE();
}
private void initStyleGeneral() {
/*
* Here we start with the styles for default-values (non-defined).
*/
Map<String, String> importstyles = new LinkedHashMap<>();
importstyles.put("author", "*");
importstyles.put("year", " (*)");
importstyles.put("title", ": *.");
importstyles.put("editor", " In * (Hrsg.)");
importstyles.put("series", ": *.");
importstyles.put("address", " *");
importstyles.put("publisher", ": *");
importstyles.put("pages", ", *");
importstyles.put("url", " [*]");
importtypes.add(importstyles);
/*
* Here we start with the styles for books.
*/
importstyles = new LinkedHashMap<>();
importstyles.put("author", "*");
importstyles.put("year", " (*)");
importstyles.put("title", ": *.");
importstyles.put("address", " *");
importstyles.put("publisher", ": *");
importstyles.put("url", " [*]");
importtypes.add(importstyles);
/*
* Here we start with the styles for articles.
*/
importstyles = new LinkedHashMap<>();
importstyles.put("author", "*");
importstyles.put("year", " (*)");
importstyles.put("title", ": *.");
importstyles.put("journal", " *");
importstyles.put("journaltitle", " *");
importstyles.put("volume", ", *");
importstyles.put("number", "(*)");
importstyles.put("pages", ", *");
importstyles.put("url", " [*]");
importtypes.add(importstyles);
/*
* Here we start with the styles for incollections.
*/
importstyles = new LinkedHashMap<>();
importstyles.put("author", "*");
importstyles.put("year", " (*)");
importstyles.put("title", ": *.");
importstyles.put("editor", " In * (Hrsg.)");
importstyles.put("booktitle", ": *.");
importstyles.put("address", " *");
importstyles.put("publisher", ": *");
importstyles.put("pages", ", *");
importstyles.put("url", " [*]");
importtypes.add(importstyles);
/*
* Here we start with the styles for inbooks.
*/
importstyles = new LinkedHashMap<>();
importstyles.put("author", "*");
importstyles.put("year", " (*)");
importstyles.put("title", ": *.");
importstyles.put("editor", " In * (Hrsg.)");
importstyles.put("booktitle", ": *.");
importstyles.put("chapter", " (Kapitel *)");
importstyles.put("address", " *");
importstyles.put("publisher", ": *");
importstyles.put("pages", ", *");
importstyles.put("url", " [*]");
importtypes.add(importstyles);
/*
* Here we start with the styles for misc-values.
*/
importstyles = new LinkedHashMap<>();
importstyles.put("author", "*");
importstyles.put("year", " (*)");
importstyles.put("title", ": *.");
importstyles.put("howpublished", " [*]");
importstyles.put("url", " [*]");
importtypes.add(importstyles);
}
private void initStyleCBE() {
/*
* Here we start with the styles for default-values (non-defined).
*/
Map<String, String> importstyles = new LinkedHashMap<>();
importstyles.put("author", "*.");
importstyles.put("year", " *.");
importstyles.put("title", " *.");
importstyles.put("editor", " In: *, editors.");
importstyles.put("series", " *.");
importstyles.put("address", " *");
importstyles.put("publisher", ": *");
importstyles.put("pages", ". p. *.");
importstyles.put("url", " [*]");
importtypesCBE.add(importstyles);
/*
* Here we start with the styles for books.
*/
importstyles = new LinkedHashMap<>();
importstyles.put("author", "*.");
importstyles.put("year", " *.");
importstyles.put("title", " *.");
importstyles.put("address", " *");
importstyles.put("publisher", ": *.");
importstyles.put("url", " [*]");
importtypesCBE.add(importstyles);
/*
* Here we start with the styles for articles.
*/
importstyles = new LinkedHashMap<>();
importstyles.put("author", "*.");
importstyles.put("year", " *.");
importstyles.put("title", " *.");
importstyles.put("journal", " *");
importstyles.put("journaltitle", " *");
importstyles.put("volume", " *");
importstyles.put("number", "(*)");
importstyles.put("pages", ":*.");
importstyles.put("url", " [*]");
importtypesCBE.add(importstyles);
/*
* Here we start with the styles for incollections.
*/
importstyles = new LinkedHashMap<>();
importstyles.put("author", "*.");
importstyles.put("year", " *.");
importstyles.put("title", " *.");
importstyles.put("editor", " In: *, editors.");
importstyles.put("booktitle", " *.");
importstyles.put("address", " *");
importstyles.put("publisher", ": *");
importstyles.put("pages", ". p. *.");
importstyles.put("url", " [*]");
importtypesCBE.add(importstyles);
/*
* Here we start with the styles for inbooks.
*/
importstyles = new LinkedHashMap<>();
importstyles.put("author", "*.");
importstyles.put("year", " *.");
importstyles.put("title", " *.");
importstyles.put("editor", " In: *, editors.");
importstyles.put("booktitle", " *.");
importstyles.put("address", " *");
importstyles.put("publisher", ": *");
importstyles.put("pages", ". p. *.");
importstyles.put("url", " [*]");
importtypesCBE.add(importstyles);
/*
* Here we start with the styles for misc.
*/
importstyles = new LinkedHashMap<>();
importstyles.put("author", "*.");
importstyles.put("year", " *.");
importstyles.put("title", " *.");
importstyles.put("howpublished", " [*]");
importstyles.put("url", " [*]");
importtypesCBE.add(importstyles);
}
private void initStyleAPA() {
/*
* Here we start with the styles for default-values (non-defined).
*/
Map<String, String> importstyles = new LinkedHashMap<>();
importstyles.put("author", "*");
importstyles.put("year", " (*)");
importstyles.put("title", ": *.");
importstyles.put("editor", " In * (Hrsg.)");
importstyles.put("series", ": *.");
importstyles.put("address", " *");
importstyles.put("publisher", ": *");
importstyles.put("pages", ", *");
importstyles.put("url", " [*]");
importtypesAPA.add(importstyles);
/*
* Here we start with the styles for books.
*/
importstyles = new LinkedHashMap<>();
importstyles.put("author", "*");
importstyles.put("year", " (*)");
importstyles.put("title", ": *.");
importstyles.put("address", " *");
importstyles.put("publisher", ": *");
importstyles.put("url", " [*]");
importtypesAPA.add(importstyles);
/*
* Here we start with the styles for articles.
*/
importstyles = new LinkedHashMap<>();
importstyles.put("author", "*");
importstyles.put("year", " (*)");
importstyles.put("title", ": *.");
importstyles.put("journal", " *");
importstyles.put("journaltitle", " *");
importstyles.put("volume", ", *");
importstyles.put("number", "(*)");
importstyles.put("pages", ", *");
importstyles.put("url", " [*]");
importtypesAPA.add(importstyles);
/*
* Here we start with the styles for incollections.
*/
importstyles = new LinkedHashMap<>();
importstyles.put("author", "*");
importstyles.put("year", " (*)");
importstyles.put("title", ": *.");
importstyles.put("editor", " In * (Hrsg.)");
importstyles.put("booktitle", ": *.");
importstyles.put("address", " *");
importstyles.put("publisher", ": *");
importstyles.put("pages", ", *");
importstyles.put("url", " [*]");
importtypesAPA.add(importstyles);
/*
* Here we start with the styles for inbooks.
*/
importstyles = new LinkedHashMap<>();
importstyles.put("author", "*");
importstyles.put("year", " (*)");
importstyles.put("title", ": *.");
importstyles.put("editor", " In * (Hrsg.)");
importstyles.put("booktitle", ": *.");
importstyles.put("chapter", " (Kapitel *)");
importstyles.put("address", " *");
importstyles.put("publisher", ": *");
importstyles.put("pages", ", *");
importstyles.put("url", " [*]");
importtypesAPA.add(importstyles);
/*
* Here we start with the styles for misc.
*/
importstyles = new LinkedHashMap<>();
importstyles.put("author", "*");
importstyles.put("year", " (*)");
importstyles.put("title", ": *.");
importstyles.put("howpublished", " [*]");
importstyles.put("url", " [*]");
importtypesAPA.add(importstyles);
}
/**
* Stores the style which should be used when e.g. importing authors into
* the database.
*
* @param style the style. use following constants:<br>
* {@code BIBTEX_CITE_STYLE_GENERAL}<br> {@code BIBTEX_CITE_STYLE_CBE}<br>
* {@code BIBTEX_CITE_STYLE_APA}
*/
public void setCiteStyle(int style) {
citestyle = style;
}
/**
* Retrieves the style which should be used when e.g. importing authors into
* the database.
*
* @return one of the following values:<br>
* {@code BIBTEX_CITE_STYLE_GENERAL}<br> {@code BIBTEX_CITE_STYLE_CBE}<br>
* {@code BIBTEX_CITE_STYLE_APA}
*/
public int getCiteStyle() {
return citestyle;
}
/**
* Sets the filepath to the latest used bibtex-file.
*
* @param fp filepath to the current bibtex-file
*/
public void setFilePath(File fp) {
settingsObj.setLastUsedBibTexFile(fp.toString());
}
/**
*
* @param be
* @return
*/
public int setEntries(ArrayList<BibtexEntry> be) {
int totalcount = 0;
if (be != null) {
bibtexentries.clear();
totalcount = addEntries(be);
}
return totalcount;
}
/**
*
* @param be
* @return
*/
public int addEntries(ArrayList<BibtexEntry> be) {
if (be != null) {
int totalcount = 0;
// iterate nodes
for (BibtexEntry node : be) {
totalcount += addEntry(node);
}
return totalcount;
}
return 0;
}
/**
*
* @param be
* @return
*/
public int addEntry(BibtexEntry be) {
// check for valid vaöue
if (be != null) {
// check whether entry does not already exist and bibkey does not exist
if (!bibtexentries.contains(be) && null == getEntry(be.getEntryKey())) {
// get type
String type = be.getEntryType();
// check for valid value. don't import comments
if (type != null && !type.equalsIgnoreCase("comment")) {
// if yes, add that entry to the linked list
bibtexentries.add(be);
// change modified state
setModified(true);
return 1;
}
}
}
return 0;
}
/**
* Gets the path to the last used bibtex-file
*
* @return the path of the last used bibtex-file, or {@code null} if no such
* path was specified nor does exist.
*/
public File getFilePath() {
return settingsObj.getLastUsedBibTexFile();
}
/**
* Returns the currently attached bibtex-file. May be used to determine
* whether the user's chosen bibtex-file <i>is already</i> opened or <i>has
* to be</i> opened.
*
* @return the currently attached bibtex-file
*/
public File getCurrentlyAttachedFile() {
return currentlyattachedfile;
}
/**
* "Detaches" the currently attached bibtex-file, which means that the
* filepath to the currently attached file is set to {@code null}.
*/
public void detachCurrentlyAttachedFile() {
currentlyattachedfile = null;
}
public void setEncoding(int encoding) {
settingsObj.setLastUsedBibtexFormat(encoding);
}
public int getEncoding() {
return settingsObj.getLastUsedBibtexFormat();
}
public boolean refreshBibTexFile(Settings settingsObj) {
// attach new file
boolean success = openAttachedFile(Constants.BIBTEX_ENCODINGS[settingsObj.getLastUsedBibtexFormat()], false, true);
return success;
}
/**
* This method opens (and "attaches") a bibtex-file which is specified via
* the {@link #setFilePath(java.io.File) setFilePath(File)} method. The file
* is parsed into the private variable {@code bibtexfile}, which can be
* accessed via {@link #getFile() getFile()}.
*
* @param encoding the character encoding of the file. use values of the
* array {@code CConstants.BIBTEX_ENCODINGS} as parameter.
* @param suppressNewEntryImport {@code true} if missing entries should
* <b>not</b> be added to the {@link #bibtexentries}, i.e. the
* ZKN3-Database. Use {@code false} if missing entries should be added.
* @param updateExistingEntries {@code true} whether entries with identical
* bibkey that have already been imported into the internal data base should
* be updated (replaced) with entries from the attached bibtex file.
*
* @return {@code true} if attachedfile was successfully opened,
* {@code false} otherwise.
*/
public boolean openAttachedFile(String encoding, boolean suppressNewEntryImport, boolean updateExistingEntries) {
// reset currently attached filepath
currentlyattachedfile = null;
// if we have no bibtex-filepath, return false
if (null == getFilePath() || !getFilePath().exists()) {
return false;
}
// create a new bibtex-parser for parsing the bibtex-file
BibtexParser bp = new BibtexParser(false);
// create stream-readers for reading the file
InputStreamReader isr = null;
InputStream is = null;
try {
// create fileinput-stream
is = new FileInputStream(getFilePath());
// read the stream, using the related character encoding
isr = new InputStreamReader(is, encoding);
// create new bibtex-file
bibtexfile = new BibtexFile();
// parse file into our bibtexfile-variable
bp.parse(bibtexfile, isr);
// get all nodes (entries) from the bibtex-file, so we can
// prepare a linked list containing all entries of that bibtex-file
List<BibtexNode> bibNodes = bibtexfile.getEntries();
// reset old linked list
attachedbibtexentries.clear();
// iterate nodes
for (BibtexNode node : bibNodes) {
// check whether the node is of type "bibtexentry"
if (node instanceof BibtexEntry) {
BibtexEntry be = (BibtexEntry) node;
// if yes, add that entry to the linked list
attachedbibtexentries.add(be);
// now we have all entries from the specified bibtex-file
// parsed into a linked list, so we have easy access to each
// single bibtex-entry via the list "bibtexentries"
}
}
// set new attached filepath, so we can figure out whether we have any
// attached file or not...
currentlyattachedfile = getFilePath();
} catch (ParseException | IOException e) {
Constants.zknlogger.log(Level.SEVERE, e.getLocalizedMessage());
return false;
} finally {
try {
if (isr != null) {
isr.close();
}
if (is != null) {
is.close();
}
} catch (IOException e) {
Constants.zknlogger.log(Level.SEVERE, e.getLocalizedMessage());
return false;
}
}
// check whether missing entries should be added
if (!updateExistingEntries && !suppressNewEntryImport) {
// add all new entries to data base
int newentries = addEntries(attachedbibtexentries);
// tell user
if (newentries > 0) {
JOptionPane.showMessageDialog(null, resourceMap.getString("importMissingBibtexEntriesText", String.valueOf(newentries)), "BibTex-Import", JOptionPane.PLAIN_MESSAGE);
}
}
return true;
}
/**
* This method opens (and "attaches") a bibtex-file which is specified via
* the {@link #setFilePath(java.io.File) setFilePath(File)} method. The file
* is parsed into the private variable {@code bibtexfile}, which can be
* accessed via {@link #getFile() getFile()}.
*
* @param encoding the character encoding of the file. use values of the
* array {@code CConstants.BIBTEX_ENCODINGS} as parameter.
* @param suppressNewEntryImport {@code true} if missing entries should
* <b>not</b> be added to the {@link #bibtexentries}, i.e. the
* ZKN3-Database. Use {@code false} if missing entries should be added.
*
* @return {@code true} if attachedfile was successfully opened,
* {@code false} otherwise.
*/
public boolean openAttachedFile(String encoding, boolean suppressNewEntryImport) {
return openAttachedFile(encoding, suppressNewEntryImport, false);
}
/**
* This method opens (and "attaches") a bibtex-file which is specified via
* the {@link #setFilePath(java.io.File) setFilePath(File)} method. The file
* is parsed into the private variable {@code bibtexfile}, which can be
* accessed via {@link #getFile() getFile()}.
*
* @param is
* @param encoding the character encoding of the file. use values of the
* array {@code Constants.BIBTEX_ENCODINGS} as parameter.
* @return {@code true} if attachedfile was successfully opened,
* {@code false} otherwise.
*/
public boolean openFile(InputStream is, String encoding) {
// if we have no bibtex-filepath, return false
if (null == is) {
return false;
}
// create a new bibtex-parser for parsing the bibtex-file
BibtexParser bp = new BibtexParser(false);
// create stream-readers for reading the file
InputStreamReader isr = null;
try {
// read the stream, using the related character encoding
isr = new InputStreamReader(is, encoding);
// create new bibtex-file
bibtexfile = new BibtexFile();
// parse file into our bibtexfile-variable
bp.parse(bibtexfile, isr);
// get all nodes (entries) from the bibtex-file, so we can
// prepare a linked list containing all entries of that bibtex-file
List<BibtexNode> bibNodes = bibtexfile.getEntries();
// reset old linked list
bibtexentries.clear();
// iterate nodes
for (BibtexNode node : bibNodes) {
// check whether the node is of type "bibtexentry"
if (node instanceof BibtexEntry) {
// if yes, add that entry to the linked list
bibtexentries.add((BibtexEntry) node);
// now we have all entries from the specified bibtex-file
// parsed into a linked list, so we have easy access to each
// single bibtex-entry via the list "bibtexentries"
}
}
} catch (ParseException | IOException e) {
Constants.zknlogger.log(Level.SEVERE, e.getLocalizedMessage());
return false;
} finally {
try {
if (isr != null) {
isr.close();
}
is.close();
} catch (IOException e) {
Constants.zknlogger.log(Level.SEVERE, e.getLocalizedMessage());
return false;
}
}
return true;
}
public boolean appendFile(InputStream is, String encoding) {
// if we have no bibtex-filepath, return false
if (null == is) {
return false;
}
// create a new bibtex-parser for parsing the bibtex-file
BibtexParser bp = new BibtexParser(false);
// create stream-readers for reading the file
InputStreamReader isr = null;
try {
// read the stream, using the related character encoding
isr = new InputStreamReader(is, encoding);
// create new bibtex-file
BibtexFile appfile = new BibtexFile();
// parse file into our bibtexfile-variable
bp.parse(appfile, isr);
// get all nodes (entries) from the bibtex-file, so we can
// prepare a linked list containing all entries of that bibtex-file
List<BibtexNode> bibNodes = appfile.getEntries();
// iterate nodes
for (BibtexNode node : bibNodes) {
// check whether the node is of type "bibtexentry"
if (node instanceof BibtexEntry) {
// if yes, add that entry to the linked list
addEntry((BibtexEntry) node);
// now we have all entries from the specified bibtex-file
// parsed into a linked list, so we have easy access to each
// single bibtex-entry via the list "bibtexentries"
}
}
} catch (ParseException | IOException e) {
Constants.zknlogger.log(Level.SEVERE, e.getLocalizedMessage());
return false;
} finally {
try {
if (isr != null) {
isr.close();
}
is.close();
} catch (IOException e) {
Constants.zknlogger.log(Level.SEVERE, e.getLocalizedMessage());
return false;
}
}
return true;
}
/**
* This method exports all BibtexEntries in the list
* {@link #outputbibtexentries outputbibtexentries} to the file given in the
* parameter {@code fp}.<br><br>
* Use {@link #clearExportBibtexEntries() clearExportBibtexEntries()} to
* reset the list of export entries and
* {@link #addBibtexEntryForExport(bibtex.dom.BibtexEntry) addBibtexEntryForExport(bibtex.dom.BibtexEntry)}
* to add new entries to this list that should be exported.
*
* @return {@code true} if entries have been successfully exported,
* {@code false} otherwise.
*/
public ByteArrayOutputStream saveFile() {
ByteArrayOutputStream bs = new ByteArrayOutputStream();
OutputStreamWriter osw = new OutputStreamWriter(bs, Charset.forName("UTF-8"));
PrintWriter pf = new PrintWriter(osw);
Iterator<BibtexEntry> i = bibtexentries.iterator();
while (i.hasNext()) {
i.next().printBibtex(pf);
}
try {
osw.close();
pf.close();
} catch (IOException ex) {
Constants.zknlogger.log(Level.SEVERE, ex.getLocalizedMessage());
}
return bs;
}
/**
* This method returns the amount of entries in the currently attachedfile,
* which equals the size of the list {@code inputbibtexentries}.
*
* @return the amount of entries in the currently attachedfile
*/
public int getAttachedFileCount() {
return attachedbibtexentries.size();
}
/**
* This method returns the amount of entries in the bibtex file, which
* equals the size of the list {@code inputbibtexentries}.
*
* @return the amount of entries in the currently bibtex file in the
* ZKN3-Database
*/
public int getCount() {
return bibtexentries.size();
}
/**
*
* @return
*/
public BibtexFile getFile() {
return bibtexfile;
}
/**
* This method returns the file name of the last used bibtex-file.
*
* @return the name of the given file, excluding extension, or {@code null}
* if an error occured.
*/
public String getFileName() {
return FileOperationsUtil.getFileName(settingsObj.getLastUsedBibTexFile());
}
/**
* This method returns the imported entries of the original ("attached")
* bibtex-file.
* <br><br>
* Bibtex-entries that should be exported to a new created bibtex-file
* always use functions with the suffix "ForExport" (e.g.
* {@link #addBibtexEntryForExport(bibtex.dom.BibtexEntry) addBibtexEntryForExport()}).
*
* @return
*/
public ArrayList<BibtexEntry> getEntriesFromAttachedFile() {
return attachedbibtexentries;
}
/**
* This method returns the entries of the bibtex-file in the ZKN3-Database.
* <br><br>
* Bibtex-entries that should be exported to a new created bibtex-file
* always use functions with the suffix "ForExport" (e.g.
* {@link #addBibtexEntryForExport(bibtex.dom.BibtexEntry) addBibtexEntryForExport()}).
*
* @return
*/
public ArrayList<BibtexEntry> getEntries() {
return bibtexentries;
}
/**
* Gets the bibtex-entry indicated by {@code nr} from the original
* (attached) bibtex-file.
*
* @param nr the entry-number of the entry that should be returned
* @return the related entry, or {@code null} if an error occured.
*/
public BibtexEntry getEntryFromAttachedFile(int nr) {
try {
return attachedbibtexentries.get(nr);
} catch (IndexOutOfBoundsException e) {
return null;
}
}
/**
* Gets the bibtex-entry indicated by {@code nr} from the original
* bibtex-file in the ZKN3-Database.
*
* @param nr the entry-number of the entry that should be returned
* @return the related entry, or {@code null} if an error occured.
*/
public BibtexEntry getEntry(int nr) {
try {
return bibtexentries.get(nr);
} catch (IndexOutOfBoundsException e) {
return null;
}
}
/**
* Gets the bibtex-entry indicated by {@code bibkey} from the original
* (attached) bibtex-file.
*
* @param bibkey the bibkey of the entry that should be returned
* @return the related entry, or {@code null} if an error occured.
*/
public BibtexEntry getEntryFromAttachedFile(String bibkey) {
// create an iterator
Iterator<BibtexEntry> i = attachedbibtexentries.iterator();
// and iterate all parsed entries from the attached file
while (i.hasNext()) {
// get each bibtex-entry
BibtexEntry be = i.next();
// if the entry-key (which is a bibkey) equals the parameter bibkey,
// return that entry
if (be.getEntryKey().equals(bibkey)) {
return be;
}
}
// else, if nothing found, return null...
return null;
}
/**
* Gets the bibtex-entry indicated by {@code bibkey} from the original
* bibtex-file in the ZKN3-Database.
*
* @param bibkey the bibkey of the entry that should be returned
* @return the related entry, or {@code null} if an error occured or nothing
* found.
*/
public BibtexEntry getEntry(String bibkey) {
// create an iterator
Iterator<BibtexEntry> i = bibtexentries.iterator();
// and iterate all parsed entries from the attached file
while (i.hasNext()) {
// get each bibtex-entry
BibtexEntry be = i.next();
// if the entry-key (which is a bibkey) equals the parameter bibkey,
// return that entry
if (be.getEntryKey().equals(bibkey)) {
return be;
}
}
// else, if nothing found, return null...
return null;
}
/**
* Checks whether an entry with the {@code bibkey} already exists in the
* data base.
*
* @param bibkey the bibkey, which should be checked for existence
* @return {@code true} if an entry with {@code bibkey} exists in the
* interal data base, {@code false} otherwise.
*/
public boolean hasEntry(String bibkey) {
return getEntry(bibkey) != null;
}
/**
* Sets (replaces) the bibtex-entry indicated by {@code bibkey} from the
* original bibtex-file in the ZKN3-Database with a new BibTexEntry
* {@code entry}.
*
* @param bibkey the bibkey of the entry that should be updated / set
* @param entry the bibtexentry that should replace the old value
*/
public void setEntry(String bibkey, BibtexEntry entry) {
// do we have any entries?
if (null == bibtexentries || 0 == bibtexentries.size()) {
return;
}
// create an iterator
for (int i = 0; i < bibtexentries.size(); i++) {
// get each bibtex-entry
BibtexEntry be = bibtexentries.get(i);
// if the entry-key (which is a bibkey) equals the parameter bibkey,
// return that entry
if (be.getEntryKey().equals(bibkey)) {
bibtexentries.set(i, entry);
return;
}
}
}
/**
* Removes the entry with the bibkey {@code bibkey} from the database.
*
* @param bibkey the bibkey value of the entry that should be removed from
* the internal bibtex database.
* @return the removed bibtex-entry, or {@code null} of no entry was
* removed.
*/
public BibtexEntry removeEntry(String bibkey) {
// retrieve entry index number
int nr = getEntryIndexNumber(bibkey);
// check for valid value
if (nr != -1) {
// remove entry
BibtexEntry be = bibtexentries.remove(nr);
// set modified state
setModified(true);
// return removed entry
return be;
}
return null;
}
/**
* Gets an bibtex-entry index-number from the bibtex-entry that is
* associated with the bibkey-value {@code bibkey} from the original
* bibtex-file in the ZKN3-Database.
*
* @param bibkey the bibkey of the entry which number should be returned
* @return the related entry-number of the bibtex-entry that has the
* bibkey-value {@code bibkey}, or {@code -1} if no such entry or bibkey
* exists
*/
public int getEntryIndexNumber(String bibkey) {
// create an iterator
Iterator<BibtexEntry> i = bibtexentries.iterator();
// init counter
int counter = 0;
// and iterate all parsed entries from the attached file
while (i.hasNext()) {
// get each bibtex-entry
BibtexEntry be = i.next();
// if the entry-key (which is a bibkey) equals the parameter bibkey,
// return that entry-index-number
if (be.getEntryKey().equals(bibkey)) {
return counter;
}
// else increase counter
counter++;
}
// else, if nothing found, return null...
return -1;
}
/**
* This method returns the bibkey of the entry {@code entrynr} from the
* currently attached file.
*
* @param entrynr the entry-number of an entry within the currently attached
* bibtex-file
* @return the related bibkey, or null if no key or no such entry exists.
*/
public String getBibkeyFromAttachedFile(int entrynr) {
// retrieve entry
BibtexEntry be = getEntryFromAttachedFile(entrynr);
// if we have no valid entry, return null
if (null == be) {
return null;
}
// return entry-key
return be.getEntryKey();
}
/**
* This method returns the bibkey of the entry {@code entrynr} from the
* currently attached file.
*
* @param entrynr the entry-number of an entry within the currently attached
* bibtex-file
* @return the related bibkey, or null if no key or no such entry exists.
*/
public String getBibkey(int entrynr) {
// retrieve entry
BibtexEntry be = getEntry(entrynr);
// if we have no valid entry, return null
if (null == be) {
return null;
}
// return entry-key
return be.getEntryKey();
}
public String getAbstract(String bibkey) {
// return abstract from bibtex entry
return getAbstract(getEntry(bibkey));
}
/**
* This method returns the abstract or annotation of the entry
* {@code entrynr} from the currently attached file.
*
* @param bibkey
* @return the related abstract, or null if no key or no such entry exists.
*/
public String getAbstractFromAttachedFile(String bibkey) {
// return abstract from bibtex entry
return getAbstract(getEntryFromAttachedFile(bibkey));
}
/**
* This method retrieves the content of an bibtex-entry's abstract or
* annotation and returns it as string.
*
* @param be the bibtex-entry which abstract/annotation should be retrieved
* @return the abstract or annotation as string, or {@code null} if no such
* entry or content exists.
*/
private String getAbstract(BibtexEntry be) {
// if we have no valid entry, return null
if (null == be) {
return null;
}
// init variable
String content = null;
// retrieve abstract
BibtexAbstractValue bav = be.getFieldValue("abstract");
if (bav != null) {
content = bav.toString();
}
// if the entry does not have a field named "abstract", try field-name "annote" instead
// (this name is used by Synapsen when entries are exported).
if (null == content || content.isEmpty()) {
bav = be.getFieldValue("annote");
if (bav != null) {
content = bav.toString();
}
// if the entry does not have a field named "abstract" nor "annote", try field-name "note" instead
// (this name is used by some apps when entries are exported).
if (null == content || content.isEmpty()) {
bav = be.getFieldValue("note");
if (bav != null) {
content = bav.toString();
}
}
}
// if we have any content, replace braces
if (content != null) {
content = content.replace("{", "").replace("}", "");
}
// return content
return content;
}
public String[] getKeywords(String bibkey) {
return getKewords(getEntry(bibkey));
}
public String[] getKeywords(int entrynr) {
return getKewords(getEntry(entrynr));
}
/**
* This method returns the keywords or annotation of the entry
* {@code entrynr} from the currently attached file.
*
* @param entrynr the entry-number of an entry within the currently attached
* bibtex-file
* @return the related keywords as string-array, or null if no keywords or
* no such entry exists.
*/
public String[] getKeywordsFromAttachedFile(int entrynr) {
return getKewords(getEntryFromAttachedFile(entrynr));
}
/**
* This method returns the keywords or annotation of the entry
* {@code entrynr} from the currently attached file.
*
* @param bibkey
* @return the related keywords as string-array, or null if no keywords or
* no such entry exists.
*/
public String[] getKeywordsFromAttachedFile(String bibkey) {
return getKewords(getEntryFromAttachedFile(bibkey));
}
/**
* This method retrieves the keywords of a bibtex-entry and returns them as
* string-array.
*
* @param be the bibtex-entry which keywords are requested
* @return the keywords of the bibtex-entry {@code be} as string-array, or
* {@code null} if no such entry or keywords exist.
*/
private String[] getKewords(BibtexEntry be) {
// if we have no valid entry, return null
if (null == be) {
return null;
}
// init variable
String[] keywords = null;
// retrieve keywods
BibtexAbstractValue bav = be.getFieldValue("keywords");
if (bav != null) {
// remove braces
String keywordline = bav.toString().replace("{", "").replace("}", "");
// check whether keywords contain ; or , as separator-char
String sep = (-1 == keywordline.indexOf(";")) ? "," : ";";
// split keywords
keywords = keywordline.split(sep);
} // in some cases, the field "keywords" is named "tags" instead. look for this
// field if "keywords" does not exist
else {
// retrieve keywods
bav = be.getFieldValue("tags");
if (bav != null) {
// remove braces
String keywordline = bav.toString().replace("{", "").replace("}", "");
// check whether keywords contain ; or , as separator-char
String sep = (-1 == keywordline.indexOf(";")) ? "," : ";";
// split keywords
keywords = keywordline.split(sep);
}
}
// trim spaces
if (keywords != null) {
for (int i = 0; i < keywords.length; i++) {
keywords[i] = keywords[i].trim();
}
}
return keywords;
}
/**
*
* @param bibkey
* @return
*/
public int getEntryType(String bibkey) {
// retrieve entry
BibtexEntry be = getEntry(bibkey);
// if we have no valid entry, return null
if (null == be) {
return -1;
}
// get entry's type
String entrytype = be.getEntryType();
// if no valid value was found, return null
if (null == entrytype || entrytype.isEmpty()) {
return -1;
}
if (entrytype.equalsIgnoreCase("book")) {
return Constants.BIBTEX_ENTRYTYPE_BOOK;
} else if (entrytype.equalsIgnoreCase("article")) {
return Constants.BIBTEX_ENTRYTYPE_ARTICLE;
} else if (entrytype.equalsIgnoreCase("incollection")) {
return Constants.BIBTEX_ENTRYTYPE_BOOKARTICLE;
} else if (entrytype.equalsIgnoreCase("inbook")) {
return Constants.BIBTEX_ENTRYTYPE_CHAPTER;
} else if (entrytype.equalsIgnoreCase("mastersthesis")) {
return Constants.BIBTEX_ENTRYTYPE_THESIS;
} else if (entrytype.equalsIgnoreCase("phdthesis")) {
return Constants.BIBTEX_ENTRYTYPE_PHD;
} else if (entrytype.equalsIgnoreCase("unpublished")) {
return Constants.BIBTEX_ENTRYTYPE_UNPUBLISHED;
} else if (entrytype.equalsIgnoreCase("conference")) {
return Constants.BIBTEX_ENTRYTYPE_CONFERENCE;
} else if (entrytype.equalsIgnoreCase("techreport") || entrytype.equalsIgnoreCase("report")) {
return Constants.BIBTEX_ENTRYTYPE_REPORT;
}
return -1;
}
/**
*
* @param be
*/
public void addBibtexEntryForExport(BibtexEntry be) {
outputbibtexentries.add(be);
}
/**
* Clears the list {@link #outputbibtexentries outputbibtexentries} which
* contains all entries that should be exported to a new Bibtex-file
*/
public void clearExportBibtexEntries() {
outputbibtexentries.clear();
}
public void clearEntries() {
bibtexentries.clear();
setModified(true);
}
/**
* This method exports all BibtexEntries in the list
* {@link #outputbibtexentries outputbibtexentries} to the file given in the
* parameter {@code fp}.<br><br>
* Use {@link #clearExportBibtexEntries() clearExportBibtexEntries()} to
* reset the list of export entries and
* {@link #addBibtexEntryForExport(bibtex.dom.BibtexEntry) addBibtexEntryForExport(bibtex.dom.BibtexEntry)}
* to add new entries to this list that should be exported.
*
* @param fp the filepath and filename of the new bibtex-file that should be
* created, containing all entries which are currently saved to
* {@link #outputbibtexentries outputbibtexentries}.
* @return {@code true} if entries have been successfully exported,
* {@code false} otherwise.
*/
public boolean exportBibtexEntries(File fp) {
if (outputbibtexentries.size() < 1) {
return false;
}
OutputStream os = null;
OutputStreamWriter osw = null;
PrintWriter pf = null;
try {
os = new FileOutputStream(fp);
osw = new OutputStreamWriter(os, Constants.BIBTEX_ENCODINGS[settingsObj.getLastUsedBibtexFormat()]);
pf = new PrintWriter(osw);
Iterator<BibtexEntry> i = outputbibtexentries.iterator();
while (i.hasNext()) {
i.next().printBibtex(pf);
}
} catch (IOException e) {
Constants.zknlogger.log(Level.SEVERE, e.getLocalizedMessage());
return false;
} finally {
try {
if (pf != null) {
pf.close();
}
if (osw != null) {
osw.close();
}
if (os != null) {
os.close();
}
} catch (IOException e) {
Constants.zknlogger.log(Level.SEVERE, e.getLocalizedMessage());
return false;
}
}
return true;
}
/**
* This method returns a formatted string consisting of the author-, year-
* and title-value of an bibtex-entry of the currently attached bibtex-file.
* The requested entry is specified by its {@code entrynr}.
*
* @param entrynr the number of the entry that should be retrieved
* @return a string containing author, year and title-information of the
* entry that matched the bibkey {@code bibkey}
*/
public String getFormattedEntryFromAttachedFile(int entrynr) {
// retrieve entry that is associated with the "bibkey"-parameter
BibtexEntry be = getEntryFromAttachedFile(entrynr);
return getFormattedEntry(be, true);
}
/**
* This method returns a formatted string consisting of the author-, year-
* and title-value of an bibtex-entry of the currently attached bibtex-file.
* The requested entry is specified by its {@code entrynr}.
*
* @param entrynr the number of the entry that should be retrieved
* @return a string containing author, year and title-information of the
* entry that matched the bibkey {@code bibkey}
*/
public String getFormattedEntry(int entrynr) {
// retrieve entry that is associated with the "bibkey"-parameter
BibtexEntry be = getEntry(entrynr);
return getFormattedEntry(be, false);
}
/**
* This method returns a formatted string consisting of the author-, year-
* and title-value of an bibtex-entry of the currently attached bibtex-file.
* The requested entry is specified by its {@code bibkey}.
*
* @param bibkey the bibkey of the entry that should be retrieved
* @return a string containing author, year and title-information of the
* entry that matched the bibkey {@code bibkey}
*/
public String getFormattedEntry(String bibkey) {
// retrieve entry that is associated with the "bibkey"-parameter
BibtexEntry be = getEntry(bibkey);
return getFormattedEntry(be, false);
}
/**
* This method returns a formatted string consisting of the author-, year-
* and title-value of an bibtex-entry of the currently attached bibtex-file.
* The requested entry is specified by its {@code entrynr}.
*
* @param entrynr the number of the entry that should be retrieved
* @return a string containing author, year and title-information of the
* entry that matched the bibkey {@code bibkey}
*/
public String getFormattedAuthor(int entrynr) {
// retrieve entry that is associated with the "bibkey"-parameter
BibtexEntry be = getEntry(entrynr);
return getFormattedAuthor(be);
}
/**
* This method returns a formatted string consisting of the author-, year-
* and title-value of an bibtex-entry of the currently attached bibtex-file.
* The requested entry is specified by its {@code bibkey}.
*
* @param bibkey the bibkey of the entry that should be retrieved
* @return a string containing author, year and title-information of the
* entry that matched the bibkey {@code bibkey}
*/
public String getFormattedAuthor(String bibkey) {
// retrieve entry that is associated with the "bibkey"-parameter
BibtexEntry be = getEntry(bibkey);
return getFormattedAuthor(be);
}
/**
* This method returns a formatted string consisting of the author-, year-
* and title-value of an bibtex-entry of the currently attached bibtex-file.
* This method does the work for both
* {@link #getFormattedEntryFromAttachedFile(java.lang.String) getFormattedEntryFromAttachedFile(String)}
* and
* {@link #getFormattedEntryFromAttachedFile(int) getFormattedEntryFromAttachedFile(int)}.<br><br>
* The way an author-value from the imported bibtex-value is formatted and
* output as string is defined via the
* {@link #initStyles() initStyles()}-method. There we create linked
* hashmaps that contain the elements for different literatur-types (books,
* articles, abstracts...).<br><br>
* In this method, we first check out the <i>type</i> of the bibtex-entry
* (book, article...) and then get the related LinkedHashMap. In this
* HashMap, we have the single elements of the literatur (author, title,
* year, publisher...) in a certain order, with special values associated to
* each element. This is the formatting.<br><br>
* E.g.: The key <b>"year"</b> has the value <b>" (*):"</b>, where the
* asterisk is replaced by the year-value, if the bibtex-entry contains a
* year-value. The formatted year thus would be for instance <b>"
* (2009):"</b>.
*
* @param be the BibtexEntry that should be formatted
* @param fromAttachedFile
* @return the formatted output-string containing author, year and
* title-information of the BibtexEntry {@code be}.
*/
public String getFormattedEntry(BibtexEntry be, boolean fromAttachedFile) {
// if we found any entry, go on...
if (be != null) {
// get all entry fields
Map m = be.getFields();
// create a new map that will contain all fields that have to be replaced
// for formatting the author-value. see below
Map<String, String> fields;
// copy import-type based on current citestyle into variable
List<Map<String, String>> citetype = new ArrayList<>();
switch (getCiteStyle()) {
case Constants.BIBTEX_CITE_STYLE_GENERAL:
citetype = importtypes;
break;
case Constants.BIBTEX_CITE_STYLE_CBE:
citetype = importtypesCBE;
break;
case Constants.BIBTEX_CITE_STYLE_APA:
citetype = importtypesAPA;
break;
}
// check whether entry type is known. needed below
boolean entryTypeKnown = true;
// here we choose the import-style, depending on the type of author (book, article...)
if (be.getEntryType().equalsIgnoreCase("book")) {
fields = citetype.get(1);
} else if (be.getEntryType().equalsIgnoreCase("article")) {
fields = citetype.get(2);
} else if (be.getEntryType().equalsIgnoreCase("incollection")) {
fields = citetype.get(3);
} else if (be.getEntryType().equalsIgnoreCase("inbook")) {
fields = citetype.get(4);
} else if (be.getEntryType().equalsIgnoreCase("misc")) {
fields = citetype.get(5);
} else {
fields = citetype.get(0);
entryTypeKnown = false;
}
// retrieve all keys, i.e. author, title etc.
Set<?> ks = m.keySet();
StringBuilder sb = new StringBuilder("");
// get the field from the import-type
Set<String> fieldsets = fields.keySet();
// create iterator
Iterator<String> fi = fieldsets.iterator();
// now we go through all field-elements that have been defined in the import-style,
// i.e. we look for authors, years, titles etc., in that order that was used when
// initiating the linked maps (see "initStyles()").
while (fi.hasNext()) {
// reset dummy-string
String dummy = null;
// retrieve each element of the author-type
String f = fi.next();
// check whether the field (author, year, title...) exists, and if yes, retrieve value
if (ks.contains(f)) {
// get abstract value. we do not convert it directly to string. in case "getFieldValue"
// returns null, this would lead to a nullpointerexception.
BibtexAbstractValue bav = be.getFieldValue(f);
if (bav != null) {
dummy = bav.toString();
}
} // in some cases, we have books, that do not have authors but editors only... in this
// case, check whether we have an editor-field instead of author-field.
else if (f.equalsIgnoreCase("author") && (be.getEntryType().equalsIgnoreCase("misc") || be.getEntryType().equalsIgnoreCase("book") || !entryTypeKnown)) {
// check whether we find an editor-field instead of author-field
if (ks.contains("editor")) {
// get abstract value. we do not convert it directly to string. in case "getFieldValue"
// returns null, this would lead to a nullpointerexception.
BibtexAbstractValue bav = be.getFieldValue("editor");
if (bav != null) {
dummy = bav.toString();
}
} // check whether we find an collaborator-field instead of author-field
else if (ks.contains("collaborator")) {
// get abstract value. we do not convert it directly to string. in case "getFieldValue"
// returns null, this would lead to a nullpointerexception.
BibtexAbstractValue bav = be.getFieldValue("collaborator");
if (bav != null) {
dummy = bav.toString();
}
}
} // in some cases, we have the field "date" in bibtex entries instead of "year".
// in this case, we use the date-field as substitute for the year-field.
else if (ks.contains("date") && !ks.contains("year") && f.equalsIgnoreCase("year")) {
// get abstract value. we do not convert it directly to string. in case "getFieldValue"
// returns null, this would lead to a nullpointerexception.
BibtexAbstractValue bav = be.getFieldValue("date");
if (bav != null) {
dummy = bav.toString();
}
}
// if it's not empty...
if (dummy != null && !dummy.isEmpty()) {
// ...get the "format template"
String app = fields.get(f);
// and replace the place holder with the associated value from the bibtex-file
if (app != null) {
// first check, whether we have an author, because we need to split this
if (f.equalsIgnoreCase("author") || f.equalsIgnoreCase("editor")) {
// retrieve single authors
String[] singleauthors = dummy.replace("{{", editorToken)
.replace("}}", "")
.replace("{", "")
.replace("}", "")
.split(Pattern.quote(" and "));
// prepare string builder
StringBuilder finalauthors = new StringBuilder("");
// iterate all found authors
for (String aunames : singleauthors) {
// add separator
finalauthors.append(", ");
// we have already removed one curly braces. if we have another one,
// we have a comolete author phrase that should not be separated
// we can completely add it as authors
if (aunames.contains(editorToken)) {
finalauthors.append(aunames.replace(editorToken, ""));
} // if author sur- and given-names are comma-separated, we assume that the
// sur-name comes first
else if (aunames.contains(",")) {
// retrieve sur and given name of author
String[] names = aunames.trim().split(",");
// check whether we have any author-field and value at all
if (names != null && names.length > 0) {
// add surname
finalauthors.append(names[0].trim());
// add name separator, depending on citatoin-style
switch (getCiteStyle()) {
case Constants.BIBTEX_CITE_STYLE_GENERAL:
finalauthors.append(" ");
break;
case Constants.BIBTEX_CITE_STYLE_CBE:
finalauthors.append(" ");
break;
case Constants.BIBTEX_CITE_STYLE_APA:
finalauthors.append(", ");
break;
}
// check whether we have any valid name-value at all, to avoid
// null-pointer-exception
if (names.length > 1 && names[1].length() > 0) {
// in case we have several givennames, separate them
// all at each space
String[] givennames = names[1].trim().split(" ");
// iterate all surnames
for (String gname : givennames) {
// check for at least one char length
if (!gname.isEmpty() && gname.length() > 0) {
switch (getCiteStyle()) {
case Constants.BIBTEX_CITE_STYLE_GENERAL:
finalauthors.append(gname.charAt(0));
break;
case Constants.BIBTEX_CITE_STYLE_CBE:
finalauthors.append(gname.charAt(0));
break;
case Constants.BIBTEX_CITE_STYLE_APA:
finalauthors.append(gname.charAt(0)).append(".");
break;
}
}
}
}
}
} // else the given name comes first, so we
// separate each full author-name at space-sign, so we can
// retieve sur- and given-name
else {
// retrieve sur and given name of author
String[] names = aunames.trim().split(" ");
// check whether we have any author-field and value at all
if (names != null && names.length > 0) {
// add sur-name
finalauthors.append(names[names.length - 1].trim());
// add sur and given-name separator
switch (getCiteStyle()) {
case Constants.BIBTEX_CITE_STYLE_GENERAL:
finalauthors.append(" ");
break;
case Constants.BIBTEX_CITE_STYLE_CBE:
finalauthors.append(" ");
break;
case Constants.BIBTEX_CITE_STYLE_APA:
finalauthors.append(", ");
break;
}
// iterate given names
for (int cnt = 0; cnt < names.length - 1; cnt++) {
// check whether we have any valid name-value at all, to avoid
// null-pointer-exception
if (names[cnt].length() > 0) {
switch (getCiteStyle()) {
case Constants.BIBTEX_CITE_STYLE_GENERAL:
finalauthors.append(names[cnt].charAt(0));
break;
case Constants.BIBTEX_CITE_STYLE_CBE:
finalauthors.append(names[cnt].charAt(0));
break;
case Constants.BIBTEX_CITE_STYLE_APA:
finalauthors.append(names[cnt].charAt(0)).append(".");
break;
}
}
}
}
}
}
// copy all formatted authors to dummy-variable
dummy = finalauthors.toString().substring(2).trim();
}
// here we replace the place holder with its content
dummy = app.replace("*", dummy.replace("{", "").replace("}", ""));
// check whether we have double periods at the end of the string...
// this may happen, if the user entered a period at the end of a title,
// while we automatically add a period after it.
if ((dummy.endsWith("..") && !dummy.endsWith("...")) || (dummy.endsWith("?.") || dummy.endsWith("!."))) {
// cut the last period.
dummy = dummy.substring(0, dummy.length() - 1);
}
}
// convert escape chars
dummy = convertEscapeChars(dummy, fromAttachedFile);
// append it to final formatted author-value
sb.append(dummy);
}
}
// create a trimmed string, where tab and new-line-chars are replaced with spaces
String trimmedString = sb.toString().replace("\t", " ").replace("\n", " ").replace("\r", "").replace("{ldots}", "...").trim();
// to avoid double spaces, replace them with a single space
return trimmedString.replace(" ", " ").trim();
}
return null;
}
/**
* This method returns a formatted string wth the author- and year-value of
* an bibtex-entry for in-text-citing. This method does the work for both
* {@link #getFormattedEntryFromAttachedFile(java.lang.String) getFormattedEntryFromAttachedFile(String)}
* and
* {@link #getFormattedEntryFromAttachedFile(int) getFormattedEntryFromAttachedFile(int)}.<br><br>
* The way an author-value from the imported bibtex-value is formatted and
* output as string is defined via the
* {@link #initStyles() initStyles()}-method. There we create linked
* hashmaps that contain the elements for different literatur-types (books,
* articles, abstracts...).<br><br>
* In this method, we first check out the <i>type</i> of the bibtex-entry
* (book, article...) and then get the related LinkedHashMap. In this
* HashMap, we have the single elements of the literatur (author, title,
* year, publisher...) in a certain order, with special values associated to
* each element. This is the formatting.<br><br>
* E.g.: The key <b>"year"</b> has the value <b>" (*):"</b>, where the
* asterisk is replaced by the year-value, if the bibtex-entry contains a
* year-value. The formatted year thus would be for instance <b>"
* (2009):"</b>.
*
* @param be the BibtexEntry that should be formatted
* @return the formatted output-string containing author and year
* information of the BibtexEntry {@code be}.
*/
private String getFormattedAuthor(BibtexEntry be) {
// if we found any entry, go on...
if (be != null) {
// get all entry fields
Map m = be.getFields();
// retrieve all keys, i.e. author, title etc.
Set<?> ks = m.keySet();
String dummy = null;
StringBuilder sb = new StringBuilder("");
// check whether the field (author, year, title...) exists, and if yes, retrieve value
if (ks.contains("author")) {
// get abstract value. we do not convert it directly to string. in case "getFieldValue"
// returns null, this would lead to a nullpointerexception.
BibtexAbstractValue bav = be.getFieldValue("author");
if (bav != null) {
dummy = bav.toString();
}
} // in some cases, we have books, that do not have authors but editors only... in this
// case, check whether we have an editor-field instead of author-field.
// check whether we find an editor-field instead of author-field
else if (ks.contains("editor")) {
// get abstract value. we do not convert it directly to string. in case "getFieldValue"
// returns null, this would lead to a nullpointerexception.
BibtexAbstractValue bav = be.getFieldValue("editor");
if (bav != null) {
dummy = bav.toString();
}
} // check whether we find an collaborator-field instead of author-field
else if (ks.contains("collaborator")) {
// get abstract value. we do not convert it directly to string. in case "getFieldValue"
// returns null, this would lead to a nullpointerexception.
BibtexAbstractValue bav = be.getFieldValue("collaborator");
if (bav != null) {
dummy = bav.toString();
}
}
// check whether we have any authors at all. if not, quit
if (null == dummy) {
return null;
}
// retrieve single authors
String[] singleauthors = dummy.replace("{{", editorToken)
.replace("}}", "")
.replace("{", "")
.replace("}", "")
.split(Pattern.quote(" and "));
// prepare string builder
List<String> finalauthors = new ArrayList<>();
// iterate all found authors
for (String aunames : singleauthors) {
// we have already removed one curly braces. if we have another one,
// we have a comolete author phrase that should not be separated
// we can completely add it as authors
if (aunames.contains(editorToken)) {
finalauthors.add(aunames.replace(editorToken, ""));
} // check how many authors we have
// if author sur- and given-names are comma-separated, we assume that the
// sur-name comes first
else if (aunames.contains(",")) {
// retrieve sur and given name of author
String[] names = aunames.trim().split(",");
// check whether we have any author-field and value at all
if (names != null && names.length > 0) {
// add surname
finalauthors.add(names[0].trim());
}
} // else the given name comes first, so we
// separate each full author-name at space-sign, so we can
// retieve sur- and given-name
else {
// retrieve sur and given name of author
String[] names = aunames.trim().split(" ");
// check whether we have any author-field and value at all
if (names != null && names.length > 0) {
// add sur-name
finalauthors.add(names[names.length - 1].trim());
}
}
}
// check how many authors we have and add them to string builder
if (1 == finalauthors.size()) {
sb.append(finalauthors.get(0));
} else if (2 == finalauthors.size()) {
sb.append(finalauthors.get(0)).append(" & ").append(finalauthors.get(1));
} else if (finalauthors.size() > 2) {
sb.append(finalauthors.get(0)).append(" et al.");
}
// retrieve year
if (ks.contains("year")) {
// get abstract value. we do not convert it directly to string. in case "getFieldValue"
// returns null, this would lead to a nullpointerexception.
BibtexAbstractValue bav = be.getFieldValue("year");
if (bav != null) {
dummy = bav.toString();
}
} // in some cases, we have the field "date" in bibtex entries instead of "year".
// in this case, we use the date-field as substitute for the year-field.
else if (ks.contains("date")) {
// get abstract value. we do not convert it directly to string. in case "getFieldValue"
// returns null, this would lead to a nullpointerexception.
BibtexAbstractValue bav = be.getFieldValue("date");
if (bav != null) {
dummy = bav.toString();
}
}
sb.append(" ").append(dummy);
// return result
return convertEscapeChars(sb.toString(), false);
}
return null;
}
/**
*
* @param dummy
* @param fromAttachedFile
* @return
*/
private String convertEscapeChars(String dummy, boolean fromAttachedFile) {
// check whether we have citavi- or mendeley-import. in such case, umlauts are "encoded" with
// backslah-quote (i.e. ä = \"a), so we have to re-cpnvert it.
if ((fromAttachedFile && getEncoding() == Constants.BIBTEX_DESC_BIBDESK_INDEX) || !fromAttachedFile) {
dummy = dummy.replace("{\\\"a}", "ä").replace("{\\\"A}", "Ä")
.replace("{\\\"o}", "ö").replace("{\\\"O}", "Ö")
.replace("{\\\"u}", "ü").replace("{\\\"U}", "Ü")
.replace("{\\ss}", "ß");
}
if ((fromAttachedFile && getEncoding() == Constants.BIBTEX_DESC_CITAVI_INDEX || getEncoding() == Constants.BIBTEX_DESC_MENDELEY_INDEX || getEncoding() == Constants.BIBTEX_DESC_BIBDESK_INDEX) || !fromAttachedFile) {
dummy = dummy.replace("\\\"a", "ä").replace("\\\"A", "Ä")
.replace("\\\"o", "ö").replace("\\\"O", "Ö")
.replace("\\\"u", "ü").replace("\\\"U", "Ü")
.replace("\\ss", "ß");
}
return dummy;
}
}