package net.sf.jabref.util; import java.io.IOException; import java.util.*; import javax.xml.transform.TransformerException; import net.sf.jabref.*; import org.jempbox.xmp.XMPMetadata; import org.jempbox.xmp.XMPSchema; import org.w3c.dom.Element; import org.w3c.dom.NamedNodeMap; import org.w3c.dom.Node; import org.w3c.dom.NodeList; public class XMPSchemaBibtex extends XMPSchema { /** * The namespace of this schema. */ public static final String NAMESPACE = "http://jabref.sourceforge.net/bibteXMP/"; public static final String KEY = "bibtex"; /** * Create a new empty XMPSchemaBibtex as a child in the given XMPMetadata. * * @param parent */ public XMPSchemaBibtex(XMPMetadata parent) { super(parent, KEY, NAMESPACE); } /** * Create schema from an existing XML element. * * @param element * The existing XML element. */ public XMPSchemaBibtex(Element e, String namespace) { super(e, KEY); } protected String makeProperty(String propertyName) { return KEY + ":" + propertyName; } /** * * @param field * @return * @derived Uses XMPSchema methods */ public List<String> getPersonList(String field) { return getSequenceList(field); } /** * * @param field * @param value * @derived Uses XMPSchema methods */ public void setPersonList(String field, String value) { AuthorList list = AuthorList.getAuthorList(value); int n = list.size(); for (int i = 0; i < n; i++) { addSequenceValue(field, list.getAuthor(i).getFirstLast(false)); } } public String getTextProperty(String field) { return super.getTextProperty(makeProperty(field)); } public void setTextProperty(String field, String value) { super.setTextProperty(makeProperty(field), value); } @SuppressWarnings("unchecked") public List<String> getBagList(String bagName) { return super.getBagList(makeProperty(bagName)); } public void removeBagValue(String bagName, String value) { super.removeBagValue(makeProperty(bagName), value); } public void addBagValue(String bagName, String value) { super.addBagValue(makeProperty(bagName), value); } @SuppressWarnings("unchecked") public List<String> getSequenceList(String seqName) { return super.getSequenceList(makeProperty(seqName)); } public void removeSequenceValue(String seqName, String value) { super.removeSequenceValue(makeProperty(seqName), value); } public void addSequenceValue(String seqName, String value) { super.addSequenceValue(makeProperty(seqName), value); } @SuppressWarnings("unchecked") public List<String> getSequenceDateList(String seqName) throws IOException { return super.getSequenceDateList(makeProperty(seqName)); } public void removeSequenceDateValue(String seqName, Calendar date) { super.removeSequenceDateValue(makeProperty(seqName), date); } public void addSequenceDateValue(String field, Calendar date) { super.addSequenceDateValue(makeProperty(field), date); } public static String getContents(NodeList seqList) { Element seqNode = (Element) seqList.item(0); StringBuffer seq = null; NodeList items = seqNode.getElementsByTagName("rdf:li"); for (int j = 0; j < items.getLength(); j++) { Element li = (Element) items.item(j); if (seq == null) { seq = new StringBuffer(); } else { seq.append(" and "); } seq.append(getTextContent(li)); } if (seq != null) { return seq.toString(); } return null; } /** * Returns a map of all properties and their values. LIs and bags in seqs * are concatenated using " and ". * * @return Map from name of textproperty (String) to value (String). For * instance: "year" => "2005". Empty map if none found. * @throws TransformerException */ public static Map<String, String> getAllProperties(XMPSchema schema, String namespaceName) { NodeList nodes = schema.getElement().getChildNodes(); Map<String, String> result = new HashMap<String, String>(); if (nodes == null) { return result; } // Check child-nodes first int n = nodes.getLength(); for (int i = 0; i < n; i++) { Node node = nodes.item(i); if (node.getNodeType() != Node.ATTRIBUTE_NODE && node.getNodeType() != Node.ELEMENT_NODE) continue; String nodeName = node.getNodeName(); String[] split = nodeName.split(":"); if (split.length == 2 && split[0].equals(namespaceName)) { NodeList seqList = ((Element) node).getElementsByTagName("rdf:Seq"); if (seqList.getLength() > 0) { String seq = getContents(seqList); if (seq != null) { result.put(split[1], seq); } } else { NodeList bagList = ((Element) node).getElementsByTagName("rdf:Bag"); if (bagList.getLength() > 0) { String seq = getContents(bagList); if (seq != null) { result.put(split[1], seq); } } else { result.put(split[1], getTextContent(node)); } } } } // Then check Attributes NamedNodeMap attrs = schema.getElement().getAttributes(); int m = attrs.getLength(); for (int j = 0; j < m; j++) { Node attr = attrs.item(j); String nodeName = attr.getNodeName(); String[] split = nodeName.split(":"); if (split.length == 2 && split[0].equals(namespaceName)) { result.put(split[1], attr.getNodeValue()); } } /* * Collapse Whitespace * * Quoting from * http://www.gerg.ca/software/btOOL/doc/bt_postprocess.html: <cite> * "The exact rules for collapsing whitespace are simple: non-space * whitespace characters (tabs and newlines mainly) are converted to * space, any strings of more than one space within are collapsed to a * single space, and any leading or trailing spaces are deleted." * </cite> */ for (Map.Entry<String, String> entry : result.entrySet()){ String key = entry.getKey(); if (preserveWhiteSpace.contains(key)) continue; entry.setValue(entry.getValue().replaceAll("\\s+", " ").trim()); } return result; } public static HashSet<String> preserveWhiteSpace = new HashSet<String>(); static { preserveWhiteSpace.add("abstract"); preserveWhiteSpace.add("note"); preserveWhiteSpace.add("review"); } public void setBibtexEntry(BibtexEntry entry) { setBibtexEntry(entry, null); } /** * * @param entry * @param database maybenull */ public void setBibtexEntry(BibtexEntry entry, BibtexDatabase database) { // Set all the values including key and entryType Set<String> fields = entry.getAllFields(); JabRefPreferences prefs = JabRefPreferences.getInstance(); if (prefs.getBoolean("useXmpPrivacyFilter")) { TreeSet<String> filters = new TreeSet<String>(Arrays.asList(prefs.getStringArray("xmpPrivacyFilters"))); fields.removeAll(filters); } for (String field : fields){ String value = BibtexDatabase.getResolvedField(field, entry, database); if (field.equals("author") || field.equals("editor")) { setPersonList(field, value); } else { setTextProperty(field, value); } } setTextProperty("entrytype", entry.getType().getName()); } public BibtexEntry getBibtexEntry() { String type = getTextProperty("entrytype"); BibtexEntryType t; if (type != null) t = BibtexEntryType.getStandardType(type); else t = BibtexEntryType.OTHER; BibtexEntry e = new BibtexEntry(Util.createNeutralId(), t); // Get Text Properties Map<String, String> text = getAllProperties(this, "bibtex"); text.remove("entrytype"); e.setField(text); return e; } /** * Taken from DOM2Utils.java: * * JBoss, the OpenSource EJB server * * Distributable under LGPL license. See terms of license at gnu.org. */ public static String getTextContent(Node node) { boolean hasTextContent = false; StringBuffer buffer = new StringBuffer(); NodeList nlist = node.getChildNodes(); for (int i = 0; i < nlist.getLength(); i++) { Node child = nlist.item(i); if (child.getNodeType() == Node.TEXT_NODE) { buffer.append(child.getNodeValue()); hasTextContent = true; } } return (hasTextContent ? buffer.toString() : ""); } }