/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.sis.xml; import java.net.URL; import java.io.File; import java.io.Reader; import java.io.InputStream; import java.io.FileInputStream; import java.io.BufferedInputStream; import java.io.IOException; import javax.xml.bind.Unmarshaller; import javax.xml.bind.JAXBElement; import javax.xml.bind.JAXBException; import javax.xml.bind.PropertyException; import javax.xml.bind.UnmarshallerHandler; import javax.xml.bind.ValidationEventHandler; import javax.xml.bind.annotation.adapters.XmlAdapter; import javax.xml.bind.attachment.AttachmentUnmarshaller; import javax.xml.stream.XMLEventReader; import javax.xml.stream.XMLStreamReader; import javax.xml.stream.XMLStreamException; import javax.xml.transform.Source; import javax.xml.validation.Schema; import org.w3c.dom.Node; import org.xml.sax.InputSource; import org.apache.sis.internal.jaxb.Context; /** * Wraps a {@link Unmarshaller} in order to have some control on the modifications applied on it. * This wrapper serves three purposes: * * <ul> * <li>Save properties before modification, in order to restore them to their original values * when the unmarshaller is recycled.</li> * <li>Constructs a SIS {@link Context} object on unmarshalling, in order to give * additional information to the SIS object being unmarshalled.</li> * <li>Wraps the input stream in a {@link TransformingReader} if the document GML version * in not the SIS native GML version.</li> * </ul> * * @author Martin Desruisseaux (Geomatys) * @version 1.0 * @since 0.3 * @module */ final class PooledUnmarshaller extends Pooled implements Unmarshaller { /** * The wrapped marshaller which does the real work. */ private final Unmarshaller unmarshaller; /** * Creates a pooled unmarshaller wrapping the given one. * Callers shall invoke {@link #reset(Pooled)} after construction for completing the initialization. * * @param unmarshaller the unmarshaller to use for the actual work. * @param template the {@link PooledTemplate} from which to get the initial values. * @throws JAXBException if an error occurred while setting a property. */ PooledUnmarshaller(final Unmarshaller unmarshaller, final Pooled template) throws JAXBException { super(template); this.unmarshaller = unmarshaller; initialize(template); } /** * Resets the given unmarshaller property to its initial state. * This method is invoked automatically by {@link #reset(Pooled)}. * * @param key the property to reset. * @param value the saved initial value to give to the property. * @throws JAXBException if an error occurred while restoring a property. */ @Override @SuppressWarnings({"unchecked","rawtypes","deprecation"}) protected void reset(final Object key, final Object value) throws JAXBException { if (key instanceof String) { unmarshaller.setProperty((String) key, value); } else if (key == AttachmentUnmarshaller.class) { unmarshaller.setAttachmentUnmarshaller((AttachmentUnmarshaller) value); } else if (key == Schema.class) { unmarshaller.setSchema((Schema) value); } else if (key == Listener.class) { unmarshaller.setListener((Listener) value); } else if (key == ValidationEventHandler.class) { unmarshaller.setEventHandler((ValidationEventHandler) value); } else if (key == Boolean.class) { unmarshaller.setValidating((Boolean) value); } else { unmarshaller.setAdapter((Class) key, (XmlAdapter) value); } } /** * Unmarshals to the given input with on-the-fly substitution of namespaces. * This method is invoked when we may marshal a different GML or metadata version than the one * supported natively by SIS, i.e. when {@link #getTransformVersion()} returns a non-null value. * * @param input the reader created by SIS (<b>not</b> the reader given by the user). * @param version identify the namespace substitutions to perform. * @return the unmarshalled object. */ private Object unmarshal(XMLEventReader input, final TransformVersion version) throws XMLStreamException, JAXBException { input = new TransformingReader(input, version); final Context context = begin(); final Object object; try { object = unmarshaller.unmarshal(input); } finally { context.finish(); } input.close(); // Despite its name, this method does not close the underlying input stream. return object; } /** * Same as {@link #unmarshal(XMLEventReader, TransformVersion)}, but delegating to the unmarshaller * methods returning a JAXB element instead than the one returning the object. */ private <T> JAXBElement<T> unmarshal(XMLEventReader input, final TransformVersion version, final Class<T> declaredType) throws XMLStreamException, JAXBException { input = new TransformingReader(input, version); final Context context = begin(); final JAXBElement<T> object; try { object = unmarshaller.unmarshal(input, declaredType); } finally { context.finish(); } input.close(); // Despite its name, this method does not close the underlying input stream. return object; } /** * Delegates the unmarshalling to the wrapped unmarshaller. */ @Override public Object unmarshal(final InputStream input) throws JAXBException { final TransformVersion version = getTransformVersion(); if (version != null) try { return unmarshal(InputFactory.createXMLEventReader(input), version); } catch (XMLStreamException e) { throw new JAXBException(e); } else { final Context context = begin(); try { return unmarshaller.unmarshal(input); } finally { context.finish(); } } } /** * Delegates the unmarshalling to the wrapped unmarshaller. */ @Override public Object unmarshal(final URL input) throws JAXBException { final TransformVersion version = getTransformVersion(); if (version != null) try { try (InputStream s = input.openStream()) { return unmarshal(InputFactory.createXMLEventReader(s), version); } } catch (IOException | XMLStreamException e) { throw new JAXBException(e); } else { final Context context = begin(); try { return unmarshaller.unmarshal(input); } finally { context.finish(); } } } /** * Delegates the unmarshalling to the wrapped unmarshaller. */ @Override public Object unmarshal(final File input) throws JAXBException { final TransformVersion version = getTransformVersion(); if (version != null) try { try (InputStream s = new BufferedInputStream(new FileInputStream(input))) { return unmarshal(InputFactory.createXMLEventReader(s), version); } } catch (IOException | XMLStreamException e) { throw new JAXBException(e); } else { final Context context = begin(); try { return unmarshaller.unmarshal(input); } finally { context.finish(); } } } /** * Delegates the unmarshalling to the wrapped unmarshaller. */ @Override public Object unmarshal(final Reader input) throws JAXBException { final TransformVersion version = getTransformVersion(); if (version != null) try { return unmarshal(InputFactory.createXMLEventReader(input), version); } catch (XMLStreamException e) { throw new JAXBException(e); } else { final Context context = begin(); try { return unmarshaller.unmarshal(input); } finally { context.finish(); } } } /** * Delegates the unmarshalling to the wrapped unmarshaller. */ @Override public Object unmarshal(final InputSource input) throws JAXBException { final TransformVersion version = getTransformVersion(); if (version != null) try { return unmarshal(InputFactory.createXMLEventReader(input), version); } catch (XMLStreamException e) { throw new JAXBException(e); } else { final Context context = begin(); try { return unmarshaller.unmarshal(input); } finally { context.finish(); } } } /** * Delegates the unmarshalling to the wrapped unmarshaller. */ @Override public Object unmarshal(final Node input) throws JAXBException { final TransformVersion version = getTransformVersion(); if (version != null) try { return unmarshal(InputFactory.createXMLEventReader(input), version); } catch (XMLStreamException e) { throw new JAXBException(e); } else { final Context context = begin(); try { return unmarshaller.unmarshal(input); } finally { context.finish(); } } } /** * Delegates the unmarshalling to the wrapped unmarshaller. */ @Override public <T> JAXBElement<T> unmarshal(final Node input, final Class<T> declaredType) throws JAXBException { final TransformVersion version = getTransformVersion(); if (version != null) try {