package org.jabref.logic.citationstyle;
import java.io.IOException;
import java.io.StringReader;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.NoSuchFileException;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Enumeration;
import java.util.List;
import java.util.Objects;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import org.jabref.logic.util.FileExtensions;
import de.undercouch.citeproc.helper.CSLUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.w3c.dom.CharacterData;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
/**
* Representation of a CitationStyle
* Stores its name, the filepath and the style itself
*/
public class CitationStyle {
public static final String DEFAULT = "/ieee.csl";
private static final Log LOGGER = LogFactory.getLog(CitationStyle.class);
private final String filepath;
private final String title;
private final String source;
private CitationStyle(final String filename, final String title, final String source) {
this.filepath = Objects.requireNonNull(filename);
this.title = Objects.requireNonNull(title);
this.source = Objects.requireNonNull(source);
}
/**
* Creates an CitationStyle instance out of the style string
*/
private static CitationStyle createCitationStyleFromSource(final String source, final String filename) {
try {
DocumentBuilder db = DocumentBuilderFactory.newInstance().newDocumentBuilder();
InputSource is = new InputSource();
is.setCharacterStream(new StringReader(source));
Document doc = db.parse(is);
NodeList nodes = doc.getElementsByTagName("info");
NodeList titleNode = ((Element) nodes.item(0)).getElementsByTagName("title");
String title = ((CharacterData) titleNode.item(0).getFirstChild()).getData();
return new CitationStyle(filename, title, source);
} catch (ParserConfigurationException | SAXException | IOException e) {
LOGGER.error("Error while parsing source", e);
}
return null;
}
/**
* Loads the CitationStyle from the given file
*/
public static CitationStyle createCitationStyleFromFile(final String styleFile) {
if (!isCitationStyleFile(styleFile)) {
LOGGER.error("Can only load style files: " + styleFile);
return null;
}
try {
String text;
String internalFile = (styleFile.startsWith("/") ? "" : "/") + styleFile;
URL url = CitationStyle.class.getResource(internalFile);
if (url != null) {
text = CSLUtils.readURLToString(url, StandardCharsets.UTF_8.toString());
} else {
// if the url is null then the style is located outside the classpath
text = new String(Files.readAllBytes(Paths.get(styleFile)), StandardCharsets.UTF_8);
}
return createCitationStyleFromSource(text, styleFile);
} catch (NoSuchFileException e) {
LOGGER.error("Could not find file: " + styleFile, e);
} catch (IOException e) {
LOGGER.error("Error reading source file", e);
}
return null;
}
public static CitationStyle getDefault() {
return createCitationStyleFromFile(DEFAULT);
}
/**
* THIS ONLY WORKS WHEN JabRef IS STARTED AS AN APPLICATION (JAR)
*
* Reads all available CitationStyle in the Jar
*/
public static List<CitationStyle> discoverCitationStyles() {
try {
final List<CitationStyle> citationStyles = new ArrayList<>();
String path = CitationStyle.class.getProtectionDomain().getCodeSource().getLocation().toURI().getPath();
try (JarFile file = new JarFile(path)) {
Enumeration<JarEntry> entries = file.entries();
while (entries.hasMoreElements()) {
String filename = entries.nextElement().getName();
if (!filename.startsWith("dependent") && filename.endsWith("csl")) {
CitationStyle citationStyle = CitationStyle.createCitationStyleFromFile(filename);
if (citationStyle != null) {
citationStyles.add(citationStyle);
}
}
}
}
return citationStyles;
} catch (IOException | URISyntaxException ex) {
LOGGER.error("something went wrong while searching available CitationStyles. " +
"Are you running directly from source code?", ex);
}
return Collections.emptyList();
}
/**
* Checks if the given style file is a CitationStyle
*/
public static boolean isCitationStyleFile(String styleFile) {
return Arrays.stream(FileExtensions.CITATION_STYLE.getExtensions()).anyMatch(styleFile::endsWith);
}
public String getTitle() {
return title;
}
public String getSource() {
return source;
}
public String getFilepath() {
return filepath;
}
@Override
public String toString() {
return title;
}
@Override
public boolean equals(Object other) {
if (this == other) {
return true;
}
if (other == null || getClass() != other.getClass()) {
return false;
}
CitationStyle that = (CitationStyle) other;
return source != null ? source.equals(that.source) : that.source == null;
}
}