/* * Copyright (C) 2012 Jan Pokorsky * * 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/>. */ package cz.cas.lib.proarc.common.mods; import cz.cas.lib.proarc.common.export.mets.MetsLSResolver; import cz.cas.lib.proarc.common.xml.Transformers; import cz.cas.lib.proarc.common.xml.Transformers.Format; import cz.cas.lib.proarc.mods.ModsCollectionDefinition; import cz.cas.lib.proarc.mods.ModsDefinition; import cz.cas.lib.proarc.mods.ObjectFactory; import java.io.IOException; import java.io.InputStream; import java.io.StringReader; import java.io.StringWriter; import java.io.UnsupportedEncodingException; import java.net.URL; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.ResourceBundle; import java.util.logging.Level; import java.util.logging.Logger; import javax.xml.XMLConstants; import javax.xml.bind.DataBindingException; import javax.xml.bind.JAXBContext; import javax.xml.bind.JAXBElement; import javax.xml.bind.JAXBException; import javax.xml.bind.Marshaller; import javax.xml.bind.Unmarshaller; import javax.xml.bind.util.JAXBSource; import javax.xml.transform.Result; import javax.xml.transform.Source; import javax.xml.transform.TransformerException; import javax.xml.transform.stream.StreamResult; import javax.xml.transform.stream.StreamSource; import javax.xml.validation.Schema; import javax.xml.validation.SchemaFactory; import org.xml.sax.SAXException; /** * Utilities fro MODS version 3.4 and later. * * @author Jan Pokorsky */ public final class ModsUtils { /** * The actual MODS version. */ public static final String VERSION = "3.5"; private static JAXBContext defaultJaxbContext; private static ThreadLocal<Marshaller> defaultMarshaller = new ThreadLocal<Marshaller>(); private static ThreadLocal<Unmarshaller> defaultUnmarshaller = new ThreadLocal<Unmarshaller>(); private static Schema MODS_SCHEMA; private static final String MODS_SCHEMA_PATH = "mods-3-5.xsd"; /** * Default MODS context. Oracle JAXB RI's context should be thread safe. * @see <a href='http://jaxb.java.net/faq/index.html#threadSafety'>Are the JAXB runtime API's thread safe?</a> */ public static JAXBContext defaultJaxbContext() throws JAXBException { if (defaultJaxbContext == null) { defaultJaxbContext = JAXBContext.newInstance(ObjectFactory.class); } return defaultJaxbContext; } /** * Default MODS marshaller for current thread. */ public static Marshaller defaultMarshaller(boolean indent) throws JAXBException { Marshaller m = defaultMarshaller.get(); if (m == null) { // later we could use a pool to minimize Marshaller instances m = defaultJaxbContext().createMarshaller(); defaultMarshaller.set(m); } m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, indent); return m; } /** * Default MODS marshaller for current thread. */ public static Unmarshaller defaultUnmarshaller() throws JAXBException { Unmarshaller m = defaultUnmarshaller.get(); if (m == null) { m = defaultJaxbContext().createUnmarshaller(); defaultUnmarshaller.set(m); } return m; } /** * Dumps MODS object to XML string. */ public static String toXml(ModsDefinition mods, boolean indent) { StringWriter dump = new StringWriter(); marshal(new StreamResult(dump), mods, indent); return dump.toString(); } /** * @see cz.cas.lib.proarc.mods.package-info.java contains name space prefix mapping. */ public static void marshal(Result target, ModsDefinition mods, boolean indent) { try { Marshaller m = defaultMarshaller(indent); m.marshal(new ObjectFactory().createMods(mods), target); } catch (JAXBException ex) { throw new DataBindingException(ex); } } public static void marshal(Result target, Object modsElement, boolean indent) { try { Marshaller m = defaultMarshaller(indent); m.marshal(modsElement, target); } catch (JAXBException ex) { throw new DataBindingException(ex); } } public static <T> T unmarshal(InputStream source, Class<T> type, boolean close) { try { return unmarshal(new StreamSource(source), type); } finally { if (close) { try { source.close(); } catch (IOException ex) { Logger.getLogger(ModsUtils.class.getName()).log(Level.SEVERE, null, ex); } } } } public static <T> T unmarshal(String source, Class<T> type) { return unmarshal(new StreamSource(new StringReader(source)), type); } public static <T> T unmarshal(URL source, Class<T> type) { return unmarshal(new StreamSource(source.toExternalForm()), type); } public static <T> T unmarshal(Source source, Class<T> type) { try { JAXBElement<T> item = defaultUnmarshaller().unmarshal(source, type); return item.getValue(); } catch (JAXBException ex) { throw new DataBindingException(ex); } } public static ModsDefinition unmarshalModsType(Source source) { try { Object unmarshaled = defaultUnmarshaller().unmarshal(source); if (unmarshaled instanceof JAXBElement) { unmarshaled = ((JAXBElement) unmarshaled).getValue(); } ModsDefinition mods; if (unmarshaled instanceof ModsCollectionDefinition) { ModsCollectionDefinition mc = (ModsCollectionDefinition) unmarshaled; mods = mc.getMods().get(0); } else if (unmarshaled instanceof ModsDefinition) { mods = (ModsDefinition) unmarshaled; } else { throw new IllegalStateException(String.valueOf(unmarshaled)); } return mods; } catch (JAXBException ex) { throw new DataBindingException(ex); } } /** * Parameters for MODS/HTML transformation. * @param locale locale to use for descriptions; can be {@code null} * @return parameters map */ public static Map<String, Object> modsAsHtmlParameters(Locale locale) { if (locale == null) { return Collections.emptyMap(); } ResourceBundle.Control control = ResourceBundle.Control.getControl(ResourceBundle.Control.FORMAT_PROPERTIES); String baseName = "xml.modsDictionary"; List<Locale> candidateLocales = control.getCandidateLocales(baseName, locale); HashMap<String, Object> params = new HashMap<String, Object>(); for (Locale candidateLocale : candidateLocales) { String toBundleName = control.toBundleName(baseName, candidateLocale); String resourceName = '/' + control.toResourceName(toBundleName, "xml"); URL resource = ModsUtils.class.getResource(resourceName); if (resource != null) { params.put("MODS_DICTIONARY", resource.toExternalForm()); break; } } return params; } public static String getLabel(ModsDefinition mods, String model) { try { JAXBSource src = new JAXBSource( defaultMarshaller(false), new ObjectFactory().createMods(mods)); Transformers t = new Transformers(); byte[] contents = t.transformAsBytes(src, Format.ModsAsFedoraLabel, Collections.<String, Object>singletonMap("MODEL", model)); int threshold = Math.min(contents.length, 2000); String label = contents.length == 0 ? "?" : new String(contents, 0, threshold, "UTF-8"); return label; } catch (JAXBException ex) { throw new IllegalStateException(model, ex); } catch (TransformerException ex) { throw new IllegalStateException(model, ex); } catch (UnsupportedEncodingException ex) { throw new IllegalStateException(model, ex); } } public static Schema getSchema() throws SAXException { if (MODS_SCHEMA == null) { SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI); schemaFactory.setResourceResolver(MetsLSResolver.getInstance()); MODS_SCHEMA = schemaFactory.newSchema(ModsDefinition.class.getResource(MODS_SCHEMA_PATH)); } return MODS_SCHEMA; } }