package org.docx4j.jaxb;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;
public class NamespacePrefixMapperUtils {
private static Logger log = LoggerFactory.getLogger(NamespacePrefixMapperUtils.class);
/*
* As from 2010 08 26,
* both com.sun.xml.bind.marshaller.NamespacePrefixMapper
* and com.sun.xml.internal.bind.marshaller.NamespacePrefixMapper
* are provided in the jar JAXB-NamespacePrefixMapperInterfaces.jar
* so that people can build docx4j without needing both JAXB
* implementations.
*
* But if that jar is on their classpath, testing for either
* of these classes will always succeed.
*
* So, we have to test for something else. The following will do:
*
* com.sun.xml.bind.marshaller.MinimumEscapeHandler
* com.sun.xml.internal.bind.marshaller.MinimumEscapeHandler
*/
private static JAXBContext testContext;
private static Object prefixMapper;
private static Object prefixMapperRels;
private static boolean haveTried = false;
public static Object getPrefixMapper() throws JAXBException {
if (prefixMapper!=null) return prefixMapper;
if (haveTried) return null;
// will be true soon..
haveTried = true;
if (testContext==null) {
java.lang.ClassLoader classLoader = NamespacePrefixMapperUtils.class.getClassLoader();
testContext = JAXBContext.newInstance("org.docx4j.relationships",classLoader );
}
if (testContext==null) {
throw new JAXBException("Couldn't create context for org.docx4j.relationships. Everything is broken!");
}
Marshaller m=testContext.createMarshaller();
if (System.getProperty("java.vendor").contains("Android")) {
log.info("Android .. assuming RI."); // Avoid unwanted Android logging; art logs the full ClassNotFoundException
return tryUsingRI(m);
}
try {
// Assume use of Java 6 implementation (ie not RI)
Class c = Class.forName("org.docx4j.jaxb.NamespacePrefixMapperSunInternal");
m.setProperty("com.sun.xml.internal.bind.namespacePrefixMapper", c.newInstance() );
log.info("Using NamespacePrefixMapperSunInternal, which is suitable for Java 6");
prefixMapper = c.newInstance();
return prefixMapper;
} catch (java.lang.NoClassDefFoundError notJava6) {
log.warn(notJava6.getMessage() + " .. trying RI.");
return tryUsingRI(m);
} catch (javax.xml.bind.PropertyException notJava6) {
// OpenJDK (1.6.0_23) does this
log.warn(notJava6.getMessage() + " .. trying RI.");
return tryUsingRI(m);
} catch (ClassNotFoundException notJava6) {
// We shouldn't get here on Android, but we may using RI elsewhere
log.warn(notJava6.getMessage() + " .. trying RI.");
return tryUsingRI(m);
} catch (InstantiationException notJava6) {
// We shouldn't get here since Class.forName will have already thrown an exception
log.warn(notJava6.getMessage() + " .. trying RI.");
return tryUsingRI(m);
} catch (IllegalAccessException notJava6) {
// We shouldn't get here since Class.forName will have already thrown an exception
log.warn(notJava6.getMessage() + " .. trying RI.");
return tryUsingRI(m);
}
}
private static Object tryUsingRI(Marshaller m)
throws JAXBException {
try {
// Try RI suitable one
m.setProperty("com.sun.xml.bind.namespacePrefixMapper",
new NamespacePrefixMapper() );
log.info("Using NamespacePrefixMapper, which is suitable for the JAXB RI");
prefixMapper = new NamespacePrefixMapper();
return prefixMapper;
} catch (java.lang.NoClassDefFoundError notRIEither) {
notRIEither.printStackTrace();
log.error("JAXB: neither Reference Implementation nor Java 6 implementation present?", notRIEither);
throw new JAXBException("JAXB: neither Reference Implementation nor Java 6 implementation present?");
} catch (javax.xml.bind.PropertyException notRIEither) {
notRIEither.printStackTrace();
log.error("JAXB: neither Reference Implementation nor Java 6 implementation present?", notRIEither);
throw new JAXBException("JAXB: neither Reference Implementation nor Java 6 implementation present?");
}
}
public static Object getPrefixMapperRelationshipsPart() throws JAXBException {
if (prefixMapperRels!=null) return prefixMapperRels;
if (testContext==null) {
java.lang.ClassLoader classLoader = NamespacePrefixMapperUtils.class.getClassLoader();
testContext = JAXBContext.newInstance("org.docx4j.relationships",classLoader );
}
Marshaller m=testContext.createMarshaller();
try {
// Assume use of Java 6 implementation (ie not RI)
Class c = Class.forName("org.docx4j.jaxb.NamespacePrefixMapperRelationshipsPartSunInternal");
m.setProperty("com.sun.xml.internal.bind.namespacePrefixMapper", c.newInstance() );
log.info("Using NamespacePrefixMapperRelationshipsPartSunInternal, which is suitable for Java 6");
prefixMapperRels = c.newInstance();
return prefixMapperRels;
} catch (java.lang.NoClassDefFoundError notJava6) {
// javax.xml.bind.PropertyException
log.warn(notJava6.getMessage() + " .. trying RI.");
return tryRIforRelationshipsPart(m);
} catch (javax.xml.bind.PropertyException notJava6) {
log.warn(notJava6.getMessage() + " .. trying RI.");
return tryRIforRelationshipsPart(m);
} catch (ClassNotFoundException notJava6) {
// We shouldn't get here on Android, but we may using RI elsewhere
log.warn(notJava6.getMessage() + " .. trying RI.");
return tryRIforRelationshipsPart(m);
} catch (InstantiationException notJava6) {
// We shouldn't get here since Class.forName will have already thrown an exception
log.warn(notJava6.getMessage() + " .. trying RI.");
return tryRIforRelationshipsPart(m);
} catch (IllegalAccessException notJava6) {
// We shouldn't get here since Class.forName will have already thrown an exception
log.warn(notJava6.getMessage() + " .. trying RI.");
return tryRIforRelationshipsPart(m);
}
}
private static Object tryRIforRelationshipsPart(Marshaller m)
throws JAXBException {
try {
// Try RI suitable one
m.setProperty("com.sun.xml.bind.namespacePrefixMapper",
new NamespacePrefixMapperRelationshipsPart() );
log.info("Using NamespacePrefixMapperRelationshipsPart, which is suitable for the JAXB RI");
prefixMapperRels = new NamespacePrefixMapperRelationshipsPart();
return prefixMapperRels;
} catch (java.lang.NoClassDefFoundError notRIEither) {
notRIEither.printStackTrace();
log.error("JAXB: neither Reference Implementation nor Java 6 implementation present?", notRIEither);
throw new JAXBException("JAXB: neither Reference Implementation nor Java 6 implementation present?");
} catch (javax.xml.bind.PropertyException notRIEither) {
notRIEither.printStackTrace();
log.error("JAXB: neither Reference Implementation nor Java 6 implementation present?", notRIEither);
throw new JAXBException("JAXB: neither Reference Implementation nor Java 6 implementation present?");
}
}
/**
* setProperty on 'com.sun.xml.bind.namespacePrefixMapper' or
* 'com.sun.xml.internal.bind.namespacePrefixMapper', as appropriate,
* depending on whether JAXB reference implementation, or Java 6
* implementation is being used.
*
* @param marshaller
* @param namespacePrefixMapper
* @throws JAXBException
*/
public static void setProperty(Marshaller marshaller, Object namespacePrefixMapper) throws JAXBException {
log.debug("attempting to setProperty on marshaller " + marshaller.getClass().getName() );
try {
if ( namespacePrefixMapper.getClass().getName().equals(
"org.docx4j.jaxb.NamespacePrefixMapper")
|| namespacePrefixMapper.getClass().getName().equals(
"org.docx4j.jaxb.NamespacePrefixMapperRelationshipsPart") ) {
marshaller.setProperty("com.sun.xml.bind.namespacePrefixMapper",
namespacePrefixMapper );
// Reference implementation appears to be present (in endorsed dir?)
log.debug("setProperty: com.sun.xml.bind.namespacePrefixMapper");
// System.out.println("setProperty: com.sun.xml.bind.namespacePrefixMapper");
} else {
// Use JAXB distributed in Java 6 - note 'internal'
// Switch to other mapper
log.debug("attempting to setProperty: com.sun.xml.INTERNAL.bind.namespacePrefixMapper");
marshaller.setProperty("com.sun.xml.internal.bind.namespacePrefixMapper", namespacePrefixMapper);
// System.out.println("setProperty: com.sun.xml.INTERNAL.bind.namespacePrefixMapper");
}
} catch (javax.xml.bind.PropertyException e) {
log.error("Couldn't setProperty on marshaller " + marshaller.getClass().getName() );
log.error(e.getMessage(), e);
throw e;
}
}
public static String getPreferredPrefix(String namespaceUri, String suggestion, boolean requirePrefix) throws JAXBException {
NamespacePrefixMapperInterface namespacePrefixMapper = (NamespacePrefixMapperInterface)getPrefixMapper();
return namespacePrefixMapper.getPreferredPrefix(namespaceUri, suggestion, requirePrefix);
}
private static final String[] EMPTY_STRING = new String[0];
public static String[] getPreDeclaredNamespaceUris(String mcIgnorable) {
if (mcIgnorable==null) {
return EMPTY_STRING;
}
List<String> entries = new ArrayList<String>();
StringTokenizer st = new StringTokenizer(mcIgnorable, " ");
while (st.hasMoreTokens()) {
String prefix = (String) st.nextToken();
String uri = NamespacePrefixMappings.getNamespaceURIStatic(prefix);
if (uri==null) {
log.warn("No mapping for prefix '" + prefix + "'");
} else {
// { "prefix1", "namespace1", "prefix2", "namespace2", ... }
//entries.add(prefix);
entries.add(uri);
}
}
return entries.toArray(new String[entries.size()]);
}
public static Map<String, String> getPreDeclaredNamespaceMap(String mcIgnorable) {
Map<String, String> entries = new HashMap<String, String>();
if (mcIgnorable==null) {
return entries;
}
StringTokenizer st = new StringTokenizer(mcIgnorable, " ");
while (st.hasMoreTokens()) {
String prefix = (String) st.nextToken();
String uri = NamespacePrefixMappings.getNamespaceURIStatic(prefix);
if (uri==null) {
log.warn("No mapping for prefix '" + prefix + "'");
} else {
// { "prefix1", "namespace1", "prefix2", "namespace2", ... }
//entries.add(prefix);
entries.put(prefix, uri);
}
}
return entries;
}
/**
* Word requires all mcIgnorable prefixes to be declared at the document level.
*
* @param mcIgnorable
* @param doc
*/
public static void declareNamespaces(String mcIgnorable, Document doc) {
if (mcIgnorable==null) return;
StringTokenizer st = new StringTokenizer(mcIgnorable, " ");
while (st.hasMoreTokens()) {
String prefix = (String) st.nextToken();
String uri = NamespacePrefixMappings.getNamespaceURIStatic(prefix);
if (uri==null) {
log.warn("No mapping for prefix '" + prefix + "'");
} else {
doc.getDocumentElement().setAttributeNS("http://www.w3.org/2000/xmlns/" ,
"xmlns:" + prefix, uri);
}
}
}
}