package org.docx4j.openpackaging.parts.opendope;
// It would be better if this class was in org.docx4j.openpackaging.parts,
// but it is too late to move it now
import java.util.List;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import org.docx4j.XmlUtils;
import org.docx4j.jaxb.NamespacePrefixMappings;
import org.docx4j.openpackaging.exceptions.Docx4JException;
import org.docx4j.openpackaging.exceptions.InvalidFormatException;
import org.docx4j.openpackaging.parts.CustomXmlDataStoragePropertiesPart;
import org.docx4j.openpackaging.parts.CustomXmlPart;
import org.docx4j.openpackaging.parts.JaxbXmlPart;
import org.docx4j.openpackaging.parts.PartName;
import org.docx4j.openpackaging.parts.relationships.Namespaces;
import org.docx4j.relationships.Relationship;
import org.docx4j.utils.XPathFactoryUtil;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
public abstract class JaxbCustomXmlDataStoragePart<E> extends JaxbXmlPart<E> implements CustomXmlPart {
// I considered extending JaxbXmlPartXPathAware,
// but that only gives us List<Object> getJAXBNodesViaXPath
// where what we (currently) want is String or list of nodes.
// Besides, XPath doesn't really work with RI (it may with moXy?).
// The XPath methods here are copied from XmlPart.
public JaxbCustomXmlDataStoragePart(PartName partName) throws InvalidFormatException {
super(partName);
init();
}
public JaxbCustomXmlDataStoragePart(PartName partName, JAXBContext jc) throws InvalidFormatException {
super(partName);
setJAXBContext(jc);
init();
}
public void init() {
// Used if this Part is added to [Content_Types].xml
setContentType(new org.docx4j.openpackaging.contenttype.ContentType(
org.docx4j.openpackaging.contenttype.ContentTypes.OFFICEDOCUMENT_CUSTOMXML_DATASTORAGE));
// Used when this Part is added to a rels
setRelationshipType(Namespaces.CUSTOM_XML_DATA_STORAGE);
}
/**
* This part's XML contents. Not guaranteed to be up to date.
* Whether it is or not will depend on how the class which extends
* Part chooses to treat it. It may be that the class uses some
* other internal representation for its data.
*/
protected Document doc;
private static XPath xPath = XPathFactoryUtil.newXPath();
/**
* XPaths are evaluated against a DOM document representation
* of the JAXBElement. These aren't kept in sync, so you can
* use this method to update the DOM document from the JAXBElement.
*/
public void readyXPath() {
doc = XmlUtils.marshaltoW3CDomDocument(getJaxbElement(), getJAXBContext());
}
public void updateJaxbElementFromDocument() {
try {
this.unmarshal(doc.getDocumentElement() );
} catch (JAXBException e) {
log.error(e.getMessage(), e);
}
}
private NamespacePrefixMappings nsContext;
private NamespacePrefixMappings getNamespaceContext() {
if (nsContext==null) {
nsContext = new NamespacePrefixMappings();
xPath.setNamespaceContext(nsContext);
}
return nsContext;
}
public String xpathGetString(String xpathString, String prefixMappings) throws Docx4JException {
if (doc==null) {
readyXPath();
//throw new Docx4JException("You must call readyXPath() once before doing XPath stuff");
}
try {
String result;
synchronized(xPath) {
getNamespaceContext().registerPrefixMappings(prefixMappings);
result = xPath.evaluate(xpathString, doc );
}
log.debug(xpathString + " ---> " + result);
return result;
} catch (Exception e) {
throw new Docx4JException("Problems evaluating xpath '" + xpathString + "'", e);
}
}
public String cachedXPathGetString(String xpath, String prefixMappings) throws Docx4JException {
if (log.isDebugEnabled()) {
log.debug("cachedXPath not implemented for " + this.getClass().getName() + " ; falling back to xpathGetString");
}
return xpathGetString( xpath, prefixMappings);
}
public void discardCacheXPathObject() {}
public List<Node> xpathGetNodes(String xpathString, String prefixMappings) throws Docx4JException {
if (doc==null) {
readyXPath();
//throw new Docx4JException("You must call readyXPath() once before doing XPath stuff");
}
synchronized(xPath) {
getNamespaceContext().registerPrefixMappings(prefixMappings);
return XmlUtils.xpath(doc, xpathString,
getNamespaceContext() );
}
}
@Override
public boolean setNodeValueAtXPath(String xpath, String value, String prefixMappings) throws Docx4JException {
if (doc==null) {
readyXPath();
//throw new Docx4JException("You must call readyXPath() once before doing XPath stuff");
}
try {
Node node;
synchronized(xPath) {
getNamespaceContext().registerPrefixMappings(prefixMappings);
node = (Node)xPath.evaluate(xpath, doc, XPathConstants.NODE );
// System.out.println(node.getClass().getName());
// com.sun.org.apache.xerces.internal.dom.ElementNSImpl
if (node instanceof Element) {
// ((Element)node).setNodeValue(nodeValue);
((Element)node).setTextContent(value);
} else {
throw new Docx4JException("Expected element, but got " + node.getClass().getName() );
}
return true;
}
} catch (Docx4JException e) {
throw e;
} catch (Exception e) {
throw new Docx4JException("Problems evaluating xpath '" + xpath + "'", e);
}
}
/**
* @since 3.0.2
*/
public String getItemId() {
if (this.getRelationshipsPart()==null) {
return null;
} else {
// Look in its rels for rel of @Type customXmlProps (eg @Target="itemProps1.xml")
Relationship r = this.getRelationshipsPart().getRelationshipByType(
Namespaces.CUSTOM_XML_DATA_STORAGE_PROPERTIES);
if (r==null) {
log.warn(".. but that doesn't point to a customXmlProps part");
return null;
}
CustomXmlDataStoragePropertiesPart customXmlProps =
(CustomXmlDataStoragePropertiesPart)this.getRelationshipsPart().getPart(r);
if (customXmlProps==null) {
log.warn(".. but the target seems to be missing?");
return null;
} else {
return customXmlProps.getItemId().toLowerCase();
}
}
}
/**
* Remove this part from the pkg. Beware: it is up to you to make sure
* your content doesn't rely on this part being present! A symptom of
* that would be that Office now reports your file to be corrupt or in
* need of repair.
*
* @since 3.0.2
*/
@Override
public void remove() {
String itemId = this.getItemId();
if (itemId!=null) {
log.debug("removing from CustomXmlDataStorageParts " + itemId);
this.getPackage().getCustomXmlDataStorageParts().remove(itemId);
}
super.remove();
}
}