Author: desruisseaux
Date: Wed Feb 7 18:01:51 2018
New Revision: 1823504
URL: http://svn.apache.org/viewvc?rev=1823504&view=rev
Log:
Replace the use of XMLStreamWriter by XMLEventWriter when writing legacy ISO 19139 metadata.
The intent is to make easier to handle renaming of metadata element or their displacement.
Added:
sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/xml/FilteredEvent.java
(with props)
sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/xml/FilteredWriter.java
- copied, changed from r1823503, sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/xml/FilteredStreamWriter.java
Removed:
sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/xml/FilteredStreamWriter.java
Modified:
sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/xml/FilteredNamespaces.java
sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/xml/FilteredStreamResolver.java
sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/xml/OutputFactory.java
sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/xml/Pooled.java
sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/xml/PooledMarshaller.java
Added: sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/xml/FilteredEvent.java
URL: http://svn.apache.org/viewvc/sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/xml/FilteredEvent.java?rev=1823504&view=auto
==============================================================================
--- sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/xml/FilteredEvent.java
(added)
+++ sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/xml/FilteredEvent.java
[UTF-8] Wed Feb 7 18:01:51 2018
@@ -0,0 +1,275 @@
+/*
+ * 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.io.IOException;
+import java.io.Writer;
+import java.util.List;
+import java.util.Iterator;
+import javax.xml.XMLConstants;
+import javax.xml.stream.Location;
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.events.XMLEvent;
+import javax.xml.stream.events.Attribute;
+import javax.xml.stream.events.Characters;
+import javax.xml.stream.events.EndElement;
+import javax.xml.stream.events.StartElement;
+import javax.xml.stream.events.Namespace;
+import javax.xml.namespace.NamespaceContext;
+import javax.xml.namespace.QName;
+
+
+/**
+ * Base class of events that are wrappers over the events emitted during the reading or writing
of an XML document.
+ * Those wrappers are used for changing the namespace and sometime the name of XML elements
or attributes.
+ *
+ * @author Martin Desruisseaux (Geomatys)
+ * @version 1.0
+ * @since 1.0
+ * @module
+ */
+abstract class FilteredEvent<E extends XMLEvent> implements XMLEvent {
+ /**
+ * The event to be exported in a different namespace.
+ */
+ final E event;
+
+ /**
+ * Exported name of the attribute or element. Will often (but not necessarily) have
+ * the same local part than {@code event.getName()} but a different namespace.
+ */
+ final QName name;
+
+ /**
+ * Exports a new event.
+ *
+ * @param event the event to be exported in a different namespace.
+ * @param name the exported name of the attribute or element.
+ */
+ FilteredEvent(final E event, final QName name) {
+ this.event = event;
+ this.name = name;
+ }
+
+ @Override public boolean isStartElement() {return false;}
+ @Override public boolean isAttribute() {return false;}
+ @Override public boolean isNamespace() {return false;}
+ @Override public boolean isEndElement() {return false;}
+ @Override public boolean isEntityReference() {return false;}
+ @Override public boolean isProcessingInstruction() {return false;}
+ @Override public boolean isCharacters() {return false;}
+ @Override public boolean isStartDocument() {return false;}
+ @Override public boolean isEndDocument() {return false;}
+ @Override public StartElement asStartElement() {throw new ClassCastException();}
+ @Override public EndElement asEndElement() {throw new ClassCastException();}
+ @Override public Characters asCharacters() {throw new ClassCastException();}
+ @Override public Location getLocation() {return event.getLocation();}
+ @Override public QName getSchemaType() {return event.getSchemaType();}
+ final public QName getName() {return name;}
+
+ /**
+ * Append the name to the given output.
+ * This is a convenience method for {@link #write(Appendable)} implementations.
+ */
+ final Appendable name(final Appendable out) throws IOException {
+ final String prefix = name.getPrefix();
+ if (prefix != null && !prefix.isEmpty()) {
+ out.append(prefix).append(':');
+ }
+ return out.append(name.getLocalPart());
+ }
+
+ /**
+ * Implementation of {@link #writeAsEncodedUnicode(Writer)} and {@link #toString()}.
+ */
+ abstract void write(Appendable out) throws IOException;
+
+ /**
+ * Writes the event as per the XML 1.0 without indentation or whitespace.
+ * This implementation delegates to {@link #write(Appendable)}.
+ */
+ @Override
+ public final void writeAsEncodedUnicode(final Writer writer) throws XMLStreamException
{
+ try {
+ write(writer);
+ } catch (IOException e) {
+ throw new XMLStreamException(e);
+ }
+ }
+
+ /**
+ * Returns the event as per the XML 1.0 without indentation or whitespace.
+ * This implementation delegates to {@link #write(Appendable)}.
+ */
+ @Override
+ public final String toString() {
+ final StringBuilder out = new StringBuilder();
+ try {
+ write(out);
+ } catch (IOException e) { // Should never happen since we write to a StringBuilder.
+ return e.toString();
+ }
+ return out.toString();
+ }
+
+ /**
+ * Wrapper over a namespace emitted during the reading or writing of an XML document.
+ * This wrapper is used for changing the namespace URI.
+ */
+ static final class NS extends FilteredEvent<Namespace> implements Namespace {
+ /** The URI of the namespace. */
+ private final String namespaceURI;
+
+ /** Wraps the given event with a different prefix and URI. */
+ NS(final Namespace event, final String prefix, final String namespaceURI) {
+ super(event, new QName(XMLConstants.XMLNS_ATTRIBUTE_NS_URI, prefix, XMLConstants.XMLNS_ATTRIBUTE));
+ this.namespaceURI = namespaceURI;
+ }
+
+ @Override public boolean isNamespace() {return true;}
+ @Override public int getEventType() {return NAMESPACE;}
+ @Override public String getNamespaceURI() {return namespaceURI;}
+ @Override public String getValue() {return namespaceURI;}
+ @Override public String getDTDType() {return event.getDTDType();}
+ @Override public boolean isSpecified() {return event.isSpecified();}
+ @Override public String getPrefix() {return (name != null)
? name.getLocalPart() : null;}
+ @Override public boolean isDefaultNamespaceDeclaration() {return (name != null)
&& name.getLocalPart().isEmpty();}
+ @Override void write(final Appendable out) throws IOException {
+ name(out).append("=\"").append(namespaceURI).append('"');
+ }
+ }
+
+ /**
+ * Wrapper over an attribute emitted during the reading or writing of an XML document.
+ * This wrapper is used for changing the namespace of the attribute.
+ */
+ static final class Attr extends FilteredEvent<Attribute> implements Attribute {
+ /** Wraps the given event with a different name. */
+ Attr(final Attribute event, final QName name) {
+ super(event, name);
+ }
+
+ /** Cast or wrap the given attribute to an {@code Attr} instance. */
+ static Attr castOrWrap(final Attribute a) {
+ if (a instanceof Attr) return (Attr) a;
+ else return new Attr(a, a.getName());
+ }
+
+ @Override public boolean isAttribute() {return true;}
+ @Override public int getEventType() {return ATTRIBUTE;}
+ @Override public String getValue() {return event.getValue();}
+ @Override public String getDTDType() {return event.getDTDType();}
+ @Override public boolean isSpecified() {return event.isSpecified();}
+ @Override void write(final Appendable out) throws IOException {
+ name(out).append("=\"").append(event.getValue()).append('"');
+ }
+ }
+
+ /**
+ * Wrapper over an element emitted during the reading or writing of an XML document.
+ * This wrapper is used for changing the namespace and sometime the name of the element.
+ */
+ static final class End extends FilteredEvent<EndElement> implements EndElement
{
+ /** The namespaces, may or may not be the same than the wrapped event. */
+ private final List<Namespace> namespaces;
+
+ /** Wraps the given event with potentially different name and namespaces. */
+ End(final EndElement event, final QName name, final List<Namespace> namespaces)
{
+ super(event, name);
+ this.namespaces = namespaces;
+ }
+
+ @Override public boolean isEndElement() {return true;}
+ @Override public EndElement asEndElement() {return this;}
+ @Override public int getEventType() {return END_ELEMENT;}
+ @Override public Iterator<Namespace> getNamespaces() {return namespaces.iterator();}
+ @Override void write(final Appendable out) throws IOException {
+ name(out.append("</")).append('>');
+ }
+ }
+
+ /**
+ * Wrapper over an element emitted during the reading or writing of an XML document.
+ * This wrapper is used for changing the namespace and sometime the name of the element.
+ * The attributes may also be modified.
+ */
+ static final class Start extends FilteredEvent<StartElement> implements StartElement
{
+ /** The namespaces, may or may not be the same than the wrapped event. */
+ private final List<Namespace> namespaces;
+
+ /** The attributes, may or may not be the same than the wrapped event. */
+ private final List<Attribute> attributes;
+
+ /** The version to export, used for wrapping namespace context. */
+ private final FilterVersion version;
+
+ /** Wraps the given event with potentially different name, namespaces and attributes.
*/
+ Start(StartElement event, QName name, List<Namespace> namespaces, List<Attribute>
attributes, FilterVersion version) {
+ super(event, name);
+ this.namespaces = namespaces;
+ this.attributes = attributes;
+ this.version = version;
+ }
+
+ @Override public boolean isStartElement() {return true;}
+ @Override public StartElement asStartElement() {return this;}
+ @Override public int getEventType() {return START_ELEMENT;}
+ @Override public Iterator<Namespace> getNamespaces() {return namespaces.iterator();}
+ @Override public Iterator<Attribute> getAttributes() {return attributes.iterator();}
+
+ /**
+ * Returns the attribute referred to by the given name, or {@code null} if none.
+ * Current implementation is okay on the assumption that there is few attributes.
+ */
+ @Override
+ public Attribute getAttributeByName(final QName name) {
+ for (final Attribute attr : attributes) {
+ if (name.equals(attr.getName())) {
+ return attr;
+ }
+ }
+ return null;
+ }
+
+ /** Gets a read-only namespace context. */
+ @Override public NamespaceContext getNamespaceContext() {
+ final NamespaceContext context = event.getNamespaceContext();
+ return (context != null) ? new FilteredNamespaces(context, version, false) :
null;
+ }
+
+ /** Gets the value that the prefix is bound to in the context of this element. */
+ @Override public String getNamespaceURI(final String prefix) {
+ final NamespaceContext context = event.getNamespaceContext();
+ if (context != null) {
+ final String uri = context.getNamespaceURI(prefix);
+ return version.toView.getOrDefault(uri, uri);
+ }
+ return null;
+ }
+
+ /** Writes the event as per the XML 1.0 without indentation or whitespace. */
+ @Override void write(final Appendable out) throws IOException {
+ name(out.append('<'));
+ final int n = attributes.size();
+ for (int i=0; i<n; i++) {
+ if (i != 0) out.append(' ');
+ Attr.castOrWrap(attributes.get(i)).write(out);
+ }
+ out.append('>');
+ }
+ }
+}
Propchange: sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/xml/FilteredEvent.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/xml/FilteredEvent.java
------------------------------------------------------------------------------
svn:mime-type = text/plain;charset=UTF-8
Modified: sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/xml/FilteredNamespaces.java
URL: http://svn.apache.org/viewvc/sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/xml/FilteredNamespaces.java?rev=1823504&r1=1823503&r2=1823504&view=diff
==============================================================================
--- sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/xml/FilteredNamespaces.java
[UTF-8] (original)
+++ sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/xml/FilteredNamespaces.java
[UTF-8] Wed Feb 7 18:01:51 2018
@@ -23,7 +23,7 @@ import javax.xml.namespace.NamespaceCont
/**
* Substitutes at (un)marshalling time the XML namespaces used by SIS by the namespaces used
in the XML document.
- * This class is used internally by {@link FilteredStreamReader} and {@link FilteredStreamWriter}
only.
+ * This class is used internally by {@link FilteredStreamReader} and {@link FilteredWriter}
only.
* Current {@code FilteredNamespaces} implementation takes care of XML prefixes only;
* the stream reader and writer do the rest of the work.
*
@@ -63,7 +63,7 @@ import javax.xml.namespace.NamespaceCont
*/
final class FilteredNamespaces implements NamespaceContext {
/**
- * The context to wrap, given by {@link FilteredStreamReader} or {@link FilteredStreamWriter}.
+ * The context to wrap, given by {@link FilteredStreamReader} or {@link FilteredWriter}.
*
* @see javax.xml.stream.XMLStreamReader#getNamespaceContext()
* @see javax.xml.stream.XMLStreamWriter#getNamespaceContext()
@@ -131,7 +131,7 @@ final class FilteredNamespaces implement
* Returns all prefixes for the given namespace.
*/
@Override
- @SuppressWarnings("unchecked")
+ @SuppressWarnings("unchecked") // TODO: remove on JDK9
public Iterator<String> getPrefixes(final String namespaceURI) {
return context.getPrefixes(toImpl.getOrDefault(namespaceURI, namespaceURI));
}
Modified: sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/xml/FilteredStreamResolver.java
URL: http://svn.apache.org/viewvc/sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/xml/FilteredStreamResolver.java?rev=1823504&r1=1823503&r2=1823504&view=diff
==============================================================================
--- sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/xml/FilteredStreamResolver.java
[UTF-8] (original)
+++ sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/xml/FilteredStreamResolver.java
[UTF-8] Wed Feb 7 18:01:51 2018
@@ -87,7 +87,7 @@ final class FilteredStreamResolver exten
private static final Map<String, Map<String,String>> NAMESPACES;
static {
final Map<String, Map<String,String>> m = new HashMap<>(250);
- try (final LineNumberReader in = new LineNumberReader(new InputStreamReader(
+ try (LineNumberReader in = new LineNumberReader(new InputStreamReader(
FilteredStreamResolver.class.getResourceAsStream(FILENAME), "UTF-8")))
{
Map<String,String> attributes = null; // All attributes for a given type.
Copied: sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/xml/FilteredWriter.java
(from r1823503, sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/xml/FilteredStreamWriter.java)
URL: http://svn.apache.org/viewvc/sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/xml/FilteredWriter.java?p2=sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/xml/FilteredWriter.java&p1=sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/xml/FilteredStreamWriter.java&r1=1823503&r2=1823504&rev=1823504&view=diff
==============================================================================
--- sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/xml/FilteredStreamWriter.java
[UTF-8] (original)
+++ sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/xml/FilteredWriter.java
[UTF-8] Wed Feb 7 18:01:51 2018
@@ -16,17 +16,30 @@
*/
package org.apache.sis.xml;
-import java.util.Set;
-import java.util.HashSet;
-import javax.xml.stream.XMLStreamWriter;
+import java.util.Map;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Iterator;
+import javax.xml.stream.XMLEventReader;
+import javax.xml.stream.XMLEventWriter;
import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.events.XMLEvent;
+import javax.xml.stream.events.Attribute;
+import javax.xml.stream.events.EndElement;
+import javax.xml.stream.events.StartElement;
+import javax.xml.stream.events.Namespace;
import javax.xml.namespace.NamespaceContext;
-import org.apache.sis.internal.util.StreamWriterDelegate;
+import javax.xml.namespace.QName;
+
+import static javax.xml.stream.XMLStreamConstants.*;
/**
* A filter replacing the namespaces used by JAXB by other namespaces to be used in the XML
document
- * at marshalling time. This class forwards every method calls to the wrapped {@link XMLStreamWriter},
+ * at marshalling time. This class forwards every method calls to the wrapped {@link XMLEventWriter},
* with all {@code namespaceURI} arguments filtered before to be delegated.
*
* See {@link FilteredNamespaces} for more information.
@@ -34,10 +47,15 @@ import org.apache.sis.internal.util.Stre
* @author Martin Desruisseaux (Geomatys)
* @author Cullen Rombach (Image Matters)
* @version 1.0
- * @since 0.4
+ * @since 1.0
* @module
*/
-final class FilteredStreamWriter extends StreamWriterDelegate {
+final class FilteredWriter implements XMLEventWriter {
+ /**
+ * Where events are sent.
+ */
+ private final XMLEventWriter out;
+
/**
* The other version to marshal to.
*/
@@ -45,110 +63,226 @@ final class FilteredStreamWriter extends
/**
* Keep track of namespace URIs that have already been declared so they don't get duplicated.
+ * This map is recycled in two different contexts:
+ *
+ * <ul>
+ * <li>In a sequence of {@code NAMESPACE} events.</li>
+ * <li>In the namespaces of a start element.</li>
+ * </ul>
+ */
+ private final Map<String, Namespace> uniqueNamespaces;
+
+ /**
+ * Temporary list of attributes after their namespace change.
+ * This list is recycled for each XML element to be read.
*/
- private final Set<String> writtenNamespaceURIs;
+ private final List<Attribute> exportedAttributes;
/**
* Creates a new filter for the given version of the standards.
*/
- FilteredStreamWriter(final XMLStreamWriter out, final FilterVersion version) {
- super(out);
+ FilteredWriter(final XMLEventWriter out, final FilterVersion version) {
+ this.out = out;
this.version = version;
- writtenNamespaceURIs = new HashSet<>();
+ uniqueNamespaces = new LinkedHashMap<>();
+ exportedAttributes = new ArrayList<>();
}
/**
* Returns the URI to write in the XML document.
+ * If there is no namespace change, then this method returns the given instance as-is.
*/
- private String toView(final String uri) {
+ private String export(final String uri) {
return version.toView.getOrDefault(uri, uri);
}
- /** Replaces the given URI if needed, then forwards the call. */
- @Override
- public void writeStartElement(final String namespaceURI, final String localName) throws
XMLStreamException {
- out.writeStartElement(toView(namespaceURI), localName);
- }
-
- /** Replaces the given URI if needed, then forwards the call. */
- @Override
- public void writeStartElement(final String prefix, final String localName, final String
namespaceURI)
- throws XMLStreamException
- {
- final String view = toView(namespaceURI);
- out.writeStartElement(Namespaces.getPreferredPrefix(view, prefix), localName, view);
- }
-
- /** Replaces the given URI if needed, then forwards the call. */
- @Override
- public void writeEmptyElement(final String namespaceURI, final String localName) throws
XMLStreamException {
- out.writeEmptyElement(toView(namespaceURI), localName);
+ /**
+ * Returns the name (prefix, namespace and local part) to write in the XML document.
+ * If there is no name change, then this method returns the given instance as-is.
+ */
+ private QName export(QName name) {
+ String uri = name.getNamespaceURI();
+ if (uri != null && !uri.isEmpty()) { // Optimization
for a common case.
+ uri = version.toView.get(uri);
+ if (uri != null) {
+ name = new QName(uri, name.getLocalPart(), Namespaces.getPreferredPrefix(uri,
name.getPrefix()));
+ }
+ }
+ return name;
}
- /** Replaces the given URI if needed, then forwards the call. */
- @Override
- public void writeEmptyElement(final String prefix, final String localName, final String
namespaceURI)
- throws XMLStreamException
- {
- final String view = toView(namespaceURI);
- out.writeEmptyElement(Namespaces.getPreferredPrefix(view, prefix), localName, view);
+ /**
+ * Returns the attribute to write in the XML document.
+ * If there is no name change, then this method returns the given instance as-is.
+ */
+ private Attribute export(Attribute attribute) {
+ final QName originalName = attribute.getName();
+ final QName name = export(originalName);
+ if (name != originalName) {
+ attribute = new FilteredEvent.Attr(attribute, name);
+ }
+ return attribute;
}
- /** Replaces the given URI if needed, then forwards the call. */
- @Override
- public void writeAttribute(final String prefix, final String namespaceURI, final String
localName,
- final String value) throws XMLStreamException
- {
- final String view = toView(namespaceURI);
- out.writeAttribute(Namespaces.getPreferredPrefix(view, prefix), view, localName,
value);
+ /**
+ * Returns the namespace to write in the XML document. This may imply a prefix change.
+ * If there is no namespace change, then this method returns the given instance as-is.
+ * To test if the returned namespace is a new one, callers should check if the size of
+ * {@link #uniqueNamespaces} changed.
+ *
+ * @param namespace the namespace to export.
+ */
+ private Namespace exportIfNew(final Namespace namespace) {
+ String uri = namespace.getNamespaceURI();
+ if (uri != null && !uri.isEmpty()) {
+ final int end = uri.length() - 1;
+ if (uri.charAt(end) == '/') {
+ uri = uri.substring(0, end); // Trim trailing
'/' in URI.
+ }
+ final String exported = version.toView.get(uri);
+ if (exported != null) {
+ return uniqueNamespaces.computeIfAbsent(exported, (k) -> {
+ return new FilteredEvent.NS(namespace, Namespaces.getPreferredPrefix(k,
namespace.getPrefix()), k);
+ });
+ }
+ final Namespace c = uniqueNamespaces.put(uri, namespace); // No namespace change
needed. Overwrite wrapper if any.
+ if (c != null) return c;
+ }
+ return namespace;
}
- /** Replaces the given URI if needed, then forwards the call. */
- @Override
- public void writeAttribute(final String namespaceURI, final String localName, final String
value)
- throws XMLStreamException
- {
- out.writeAttribute(toView(namespaceURI), localName, value);
+ /**
+ * Returns the namespaces to write in the XML document, or {@code null} if there is no
change.
+ * If non-null, the result may contain less namespaces because duplicated entries are
omitted
+ * (duplication may occur as a result of replacing various ISO 19115-3 namespaces by
the legacy
+ * ISO 19139:2007 {@code "gmd"} unique namespace).
+ *
+ * @param namespaces the namespaces to filter.
+ * @param changed whether to unconditionally pretend that there is a change.
+ * @return the updated namespaces, or {@code null} if there is no changes.
+ */
+ private List<Namespace> export(final Iterator<Namespace> namespaces, boolean
changed) {
+ if (!namespaces.hasNext()) {
+ return changed ? Collections.emptyList() : null;
+ }
+ do {
+ final Namespace namespace = namespaces.next();
+ changed |= (namespace != exportIfNew(namespace));
+ } while (namespaces.hasNext());
+ if (changed) {
+ assert !uniqueNamespaces.isEmpty();
+ final Namespace[] exported = uniqueNamespaces.values().toArray(new Namespace[uniqueNamespaces.size()]);
+ uniqueNamespaces.clear();
+ return Arrays.asList(exported);
+ } else {
+ uniqueNamespaces.clear();
+ return null;
+ }
}
/**
- * Replaces the given URI if needed, then forwards the call.
- * This method does nothing if the view of given URI has already be written.
- * This filtering is applied because different URIs may be replaced by the same view.
+ * Adds an event to the output stream. This method may wrap the given event into another
event
+ * for changing the namespace and/or the prefix.
*/
@Override
- public void writeNamespace(final String prefix, final String namespaceURI) throws XMLStreamException
{
- final String view = toView(namespaceURI);
- if (writtenNamespaceURIs.add(view)) {
- out.writeNamespace(Namespaces.getPreferredPrefix(view, prefix), view);
+ @SuppressWarnings("unchecked") // TODO: remove on JDK9
+ public void add(XMLEvent event) throws XMLStreamException {
+ switch (event.getEventType()) {
+ case ATTRIBUTE: {
+ event = export((Attribute) event);
+ break;
+ }
+ case NAMESPACE: {
+ final int n = uniqueNamespaces.size();
+ event = exportIfNew((Namespace) event);
+ if (uniqueNamespaces.size() == n) {
+ return; // An event has already been
created for that namespace.
+ }
+ break;
+ }
+ case START_ELEMENT: {
+ uniqueNamespaces.clear(); // Discard entries created
by NAMESPACE events.
+ final StartElement e = event.asStartElement();
+ final QName originalName = e.getName();
+ final QName name = export(originalName);
+ boolean changed = name != originalName;
+ for (final Iterator<Attribute> it = e.getAttributes(); it.hasNext();)
{
+ final Attribute a = it.next();
+ final Attribute ae = export(a);
+ changed |= (a != ae);
+ exportedAttributes.add(ae);
+ }
+ final List<Namespace> namespaces = export(e.getNamespaces(), changed);
+ if (namespaces != null) {
+ final List<Attribute> attributes;
+ switch (exportedAttributes.size()) {
+ case 0: attributes = Collections.emptyList(); break; // Avoid
object creation for this common case.
+ case 1: attributes = Collections.singletonList(exportedAttributes.remove(0));
break;
+ default: attributes = Arrays.asList(exportedAttributes.toArray(new
Attribute[exportedAttributes.size()]));
+ exportedAttributes.clear();
+ break;
+ }
+ event = new FilteredEvent.Start(e, name, namespaces, attributes, version);
+ } else {
+ exportedAttributes.clear();
+ }
+ break;
+ }
+ case END_ELEMENT: {
+ final EndElement e = event.asEndElement();
+ final QName originalName = e.getName();
+ final QName name = export(originalName);
+ final List<Namespace> namespaces = export(e.getNamespaces(), name !=
originalName);
+ if (namespaces != null) {
+ event = new FilteredEvent.End(e, name, namespaces);
+ }
+ break;
+ }
}
+ out.add(event);
}
- /** Replaces the given URI if needed, then forwards the call. */
+ /**
+ * Adds an entire stream to an output stream.
+ */
@Override
- public void writeDefaultNamespace(final String namespaceURI) throws XMLStreamException
{
- out.writeDefaultNamespace(toView(namespaceURI));
+ public void add(final XMLEventReader reader) throws XMLStreamException {
+ while (reader.hasNext()) {
+ add(reader.nextEvent());
+ }
}
- /** Replaces the given URI if needed, then forwards the call. */
+ /**
+ * Gets the prefix the URI is bound to.
+ * This method replaces the given URI if needed, then forwards the call.
+ */
@Override
public String getPrefix(final String uri) throws XMLStreamException {
- return out.getPrefix(toView(uri));
+ return out.getPrefix(export(uri));
}
- /** Replaces the given URI if needed, then forwards the call. */
+ /**
+ * Sets the prefix the URI is bound to.
+ * This method replaces the given URI if needed, then forwards the call.
+ */
@Override
public void setPrefix(final String prefix, final String uri) throws XMLStreamException
{
- out.setPrefix(prefix, toView(uri));
+ out.setPrefix(prefix, export(uri));
}
- /** Replaces the given URI if needed, then forwards the call. */
+ /**
+ * Binds a URI to the default namespace.
+ * Replaces the given URI if needed, then forwards the call.
+ */
@Override
public void setDefaultNamespace(final String uri) throws XMLStreamException {
- out.setDefaultNamespace(toView(uri));
+ out.setDefaultNamespace(export(uri));
}
- /** Unwraps the original context and forwards the call. */
+ /**
+ * Sets the current namespace context for prefix and URI bindings.
+ * Unwraps the original context and forwards the call.
+ */
@Override
public void setNamespaceContext(NamespaceContext context) throws XMLStreamException {
if (context instanceof FilteredNamespaces) {
@@ -159,9 +293,28 @@ final class FilteredStreamWriter extends
out.setNamespaceContext(context);
}
- /** Returns the context of the underlying writer wrapped in a filter that convert the
namespaces on the fly. */
+ /**
+ * Returns the context of the underlying writer wrapped in a filter that convert the
namespaces on the fly.
+ */
@Override
public NamespaceContext getNamespaceContext() {
return new FilteredNamespaces(out.getNamespaceContext(), version, false);
}
+
+ /**
+ * Writes any cached events to the underlying output mechanism.
+ */
+ @Override
+ public void flush() throws XMLStreamException {
+ out.flush();
+ }
+
+ /**
+ * Frees any resources associated with this writer.
+ */
+ @Override
+ public void close() throws XMLStreamException {
+ uniqueNamespaces.clear();
+ out.close();
+ }
}
Modified: sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/xml/OutputFactory.java
URL: http://svn.apache.org/viewvc/sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/xml/OutputFactory.java?rev=1823504&r1=1823503&r2=1823504&view=diff
==============================================================================
--- sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/xml/OutputFactory.java
[UTF-8] (original)
+++ sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/xml/OutputFactory.java
[UTF-8] Wed Feb 7 18:01:51 2018
@@ -37,7 +37,7 @@ import org.apache.sis.util.Static;
* only when first needed, when initializing this class.
*
* @author Martin Desruisseaux (Geomatys)
- * @version 0.8
+ * @version 1.0
* @since 0.4
* @module
*/
@@ -55,7 +55,7 @@ final class OutputFactory extends Static
/*
* Do not provide convenience method for java.io.File, because the caller needs to close
the created
- * output stream himself (this is not done by XMLStreamWriter.close(), despite its method
name).
+ * output stream himself (this is not done by XMLEventWriter.close(), despite its method
name).
*/
/**
@@ -66,8 +66,8 @@ final class OutputFactory extends Static
* @return the writer.
* @throws XMLStreamException if the writer can not be created.
*/
- public static XMLStreamWriter createXMLStreamWriter(OutputStream out, String encoding)
throws XMLStreamException {
- return FACTORY.createXMLStreamWriter(out, encoding);
+ public static XMLEventWriter createXMLEventWriter(OutputStream out, String encoding)
throws XMLStreamException {
+ return FACTORY.createXMLEventWriter(out, encoding);
}
/**
@@ -77,8 +77,8 @@ final class OutputFactory extends Static
* @return the writer.
* @throws XMLStreamException if the writer can not be created.
*/
- public static XMLStreamWriter createXMLStreamWriter(final Writer out) throws XMLStreamException
{
- return FACTORY.createXMLStreamWriter(out);
+ public static XMLEventWriter createXMLEventWriter(final Writer out) throws XMLStreamException
{
+ return FACTORY.createXMLEventWriter(out);
}
/**
@@ -89,8 +89,8 @@ final class OutputFactory extends Static
* @return the writer.
* @throws XMLStreamException if the writer can not be created.
*/
- public static XMLStreamWriter createXMLStreamWriter(final ContentHandler out) throws
XMLStreamException {
- return FACTORY.createXMLStreamWriter(new SAXResult(out));
+ public static XMLEventWriter createXMLEventWriter(final ContentHandler out) throws XMLStreamException
{
+ return FACTORY.createXMLEventWriter(new SAXResult(out));
}
/**
@@ -101,8 +101,8 @@ final class OutputFactory extends Static
* @return the writer.
* @throws XMLStreamException if the writer can not be created.
*/
- public static XMLStreamWriter createXMLStreamWriter(final Node out) throws XMLStreamException
{
- return FACTORY.createXMLStreamWriter(new DOMResult(out));
+ public static XMLEventWriter createXMLEventWriter(final Node out) throws XMLStreamException
{
+ return FACTORY.createXMLEventWriter(new DOMResult(out));
}
/**
@@ -113,8 +113,8 @@ final class OutputFactory extends Static
* @return the writer.
* @throws XMLStreamException if the writer can not be created.
*/
- public static XMLStreamWriter createXMLStreamWriter(final XMLEventWriter out) throws
XMLStreamException {
- return FACTORY.createXMLStreamWriter(new StAXResult(out));
+ public static XMLEventWriter createXMLEventWriter(final XMLStreamWriter out) throws XMLStreamException
{
+ return FACTORY.createXMLEventWriter(new StAXResult(out));
}
/**
@@ -125,7 +125,7 @@ final class OutputFactory extends Static
* @return the writer.
* @throws XMLStreamException if the writer can not be created.
*/
- public static XMLStreamWriter createXMLStreamWriter(final Result out) throws XMLStreamException
{
- return FACTORY.createXMLStreamWriter(out);
+ public static XMLEventWriter createXMLEventWriter(final Result out) throws XMLStreamException
{
+ return FACTORY.createXMLEventWriter(out);
}
}
Modified: sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/xml/Pooled.java
URL: http://svn.apache.org/viewvc/sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/xml/Pooled.java?rev=1823504&r1=1823503&r2=1823504&view=diff
==============================================================================
--- sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/xml/Pooled.java
[UTF-8] (original)
+++ sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/xml/Pooled.java
[UTF-8] Wed Feb 7 18:01:51 2018
@@ -243,8 +243,8 @@ abstract class Pooled {
/**
* Returns the {@code FilterVersion} enumeration value to use for the current GML or
metadata version,
* or {@code null} if the SIS native versions are suitable. If this method returns a
non-null value,
- * then the output generated by JAXB will need to go through a {@link FilteredStreamWriter}
in order
- * to replace the namespace of versions implemented by SIS by the namespace of versions
requested by
+ * then the output generated by JAXB will need to go through a {@link FilteredWriter}
in order to
+ * replace the namespace of versions implemented by SIS by the namespace of versions
requested by
* the user.
*
* @see FilteredNamespaces
Modified: sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/xml/PooledMarshaller.java
URL: http://svn.apache.org/viewvc/sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/xml/PooledMarshaller.java?rev=1823504&r1=1823503&r2=1823504&view=diff
==============================================================================
--- sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/xml/PooledMarshaller.java
[UTF-8] (original)
+++ sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/xml/PooledMarshaller.java
[UTF-8] Wed Feb 7 18:01:51 2018
@@ -48,12 +48,12 @@ import org.apache.sis.internal.jaxb.Type
* when the marshaller is recycled.</li>
* <li>Constructs a SIS {@link Context} object on marshalling, in order to give
* additional information to the SIS object being marshalled.</li>
- * <li>Wraps the output stream in a {@link FilteredStreamWriter} if the desired GML
version
+ * <li>Wraps the output stream in a {@link FilteredWriter} if the desired GML version
* in not the SIS native GML version.</li>
* </ul>
*
* @author Martin Desruisseaux (Geomatys)
- * @version 0.8
+ * @version 1.0
* @since 0.3
* @module
*/
@@ -136,14 +136,14 @@ final class PooledMarshaller extends Poo
* than the one supported natively by SIS, i.e. when {@link #getFilterVersion()} returns
* a non-null value.
*
- * @param object the object to marshall.
+ * @param object the object to marshal.
* @param output the writer created by SIS (<b>not</b> the writer given
by the user).
* @param version identifies the namespace substitutions to perform.
*/
- private void marshal(final Object object, XMLStreamWriter output, final FilterVersion
version)
+ private void marshal(final Object object, XMLEventWriter output, final FilterVersion
version)
throws XMLStreamException, JAXBException
{
- output = new FilteredStreamWriter(output, version);
+ output = new FilteredWriter(output, version);
final Context context = begin();
try {
marshaller.marshal(toImplementation(object), output);
@@ -160,7 +160,7 @@ final class PooledMarshaller extends Poo
public void marshal(final Object object, final Result output) throws JAXBException {
final FilterVersion version = getFilterVersion();
if (version != null) try {
- marshal(object, OutputFactory.createXMLStreamWriter(output), version);
+ marshal(object, OutputFactory.createXMLEventWriter(output), version);
} catch (XMLStreamException e) {
throw new JAXBException(e);
} else {
@@ -181,7 +181,7 @@ final class PooledMarshaller extends Poo
public void marshal(final Object object, final OutputStream output) throws JAXBException
{
final FilterVersion version = getFilterVersion();
if (version != null) try {
- marshal(object, OutputFactory.createXMLStreamWriter(output, getEncoding()), version);
+ marshal(object, OutputFactory.createXMLEventWriter(output, getEncoding()), version);
} catch (XMLStreamException e) {
throw new JAXBException(e);
} else {
@@ -203,7 +203,7 @@ final class PooledMarshaller extends Poo
final FilterVersion version = getFilterVersion();
if (version != null) try {
try (OutputStream s = new BufferedOutputStream(new FileOutputStream(output)))
{
- marshal(object, OutputFactory.createXMLStreamWriter(s, getEncoding()), version);
+ marshal(object, OutputFactory.createXMLEventWriter(s, getEncoding()), version);
}
} catch (IOException | XMLStreamException e) {
throw new JAXBException(e);
@@ -225,7 +225,7 @@ final class PooledMarshaller extends Poo
public void marshal(final Object object, final Writer output) throws JAXBException {
final FilterVersion version = getFilterVersion();
if (version != null) try {
- marshal(object, OutputFactory.createXMLStreamWriter(output), version);
+ marshal(object, OutputFactory.createXMLEventWriter(output), version);
} catch (XMLStreamException e) {
throw new JAXBException(e);
} else {
@@ -246,7 +246,7 @@ final class PooledMarshaller extends Poo
public void marshal(final Object object, final ContentHandler output) throws JAXBException
{
final FilterVersion version = getFilterVersion();
if (version != null) try {
- marshal(object, OutputFactory.createXMLStreamWriter(output), version);
+ marshal(object, OutputFactory.createXMLEventWriter(output), version);
} catch (XMLStreamException e) {
throw new JAXBException(e);
} else {
@@ -267,7 +267,7 @@ final class PooledMarshaller extends Poo
public void marshal(final Object object, final Node output) throws JAXBException {
final FilterVersion version = getFilterVersion();
if (version != null) try {
- marshal(object, OutputFactory.createXMLStreamWriter(output), version);
+ marshal(object, OutputFactory.createXMLEventWriter(output), version);
} catch (XMLStreamException e) {
throw new JAXBException(e);
} else {
@@ -285,27 +285,10 @@ final class PooledMarshaller extends Poo
* Delegates the marshalling to the wrapped marshaller.
*/
@Override
- public void marshal(final Object object, XMLStreamWriter output) throws JAXBException
{
- final FilterVersion version = getFilterVersion();
- if (version != null) {
- output = new FilteredStreamWriter(output, version);
- }
- final Context context = begin();
- try {
- marshaller.marshal(toImplementation(object), output);
- } finally {
- context.finish();
- }
- }
-
- /**
- * Delegates the marshalling to the wrapped marshaller.
- */
- @Override
- public void marshal(final Object object, final XMLEventWriter output) throws JAXBException
{
+ public void marshal(final Object object, final XMLStreamWriter output) throws JAXBException
{
final FilterVersion version = getFilterVersion();
if (version != null) try {
- marshal(object, OutputFactory.createXMLStreamWriter(output), version);
+ marshal(object, OutputFactory.createXMLEventWriter(output), version);
} catch (XMLStreamException e) {
throw new JAXBException(e);
} else {
@@ -319,6 +302,23 @@ final class PooledMarshaller extends Poo
}
}
+ /**
+ * Delegates the marshalling to the wrapped marshaller.
+ */
+ @Override
+ public void marshal(final Object object, XMLEventWriter output) throws JAXBException
{
+ final FilterVersion version = getFilterVersion();
+ if (version != null) {
+ output = new FilteredWriter(output, version);
+ }
+ final Context context = begin();
+ try {
+ marshaller.marshal(toImplementation(object), output);
+ } finally {
+ context.finish();
+ }
+ }
+
/**
* Delegates the marshalling to the wrapped marshaller.
*/
|