Author: desruisseaux
Date: Wed Feb 14 21:42:41 2018
New Revision: 1824269
URL: http://svn.apache.org/viewvc?rev=1824269&view=rev
Log:
More stable mapping of namespace URIs to prefixes.
Modified:
sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/xml/FilterVersion.java
sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/xml/FilteredEvent.java
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/FilteredReader.java
sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/xml/FilteredWriter.java
sis/branches/ISO-19115-3/core/sis-utility/src/test/java/org/apache/sis/xml/FilteredNamespacesTest.java
Modified:
sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/xml/FilterVersion.java
URL:
http://svn.apache.org/viewvc/sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/xml/FilterVersion.java?rev=1824269&r1=1824268&r2=1824269&view=diff
==============================================================================
---
sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/xml/FilterVersion.java
[UTF-8] (original)
+++
sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/xml/FilterVersion.java
[UTF-8] Wed Feb 14 21:42:41 2018
@@ -20,7 +20,6 @@ import java.util.Map;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Collections;
-import javax.xml.namespace.QName;
import org.apache.sis.util.Debug;
import org.apache.sis.internal.jaxb.LegacyNamespaces;
@@ -238,28 +237,6 @@ final class FilterVersion {
}
/**
- * Returns the name (prefix, namespace and local part) to write in the XML
document.
- * This method may replace the namespace, and in some case the name local
part too.
- * If there is no name change, then this method returns the given instance
as-is.
- */
- final QName export(QName name) {
- String uri = name.getNamespaceURI();
- if (uri != null && !uri.isEmpty()) { //
Optimization for a common case.
- final Replacement r = exports.get(uri);
- if (r != null) {
- uri = r.namespace;
- name = new QName(uri, r.exportProperty(name.getLocalPart()),
- Namespaces.getPreferredPrefix(uri, name.getPrefix()));
- /*
- * Note: the call for 'getPreferredPrefix' above is required:
- * JAXB seems to need the prefixes for recognizing namespaces.
- */
- }
- }
- return name;
- }
-
- /**
* Converts a namespace used in JAXB annotation to the namespace used in
XML document.
* Returns the same URI if there is no replacement.
*/
Modified:
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=1824269&r1=1824268&r2=1824269&view=diff
==============================================================================
---
sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/xml/FilteredEvent.java
[UTF-8] (original)
+++
sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/xml/FilteredEvent.java
[UTF-8] Wed Feb 14 21:42:41 2018
@@ -246,24 +246,37 @@ abstract class FilteredEvent<E extends X
}
/**
- * Gets a read-only namespace context. Default implementation is
suitable for exports
- * (i.e. write operation). Needs to be overridden for imports (read
operation).
+ * Gets the URI used by JAXB annotations for the given prefix used in
the XML document.
+ * Returns {@code null} if no unique URI can be provided for the given
prefix.
*
- * @see FilteredWriter#getNamespaceContext()
+ * <div class="note"><b>Example:</b>
+ * the {@code "gmd"} prefix from legacy ISO 19139:2007 standard can
map to the
+ * {@code "http://standards.iso.org/iso/19115/-3/mdb/1.0"}, {@code
"…/cit/1.0"}
+ * and other namespaces in ISO 19115-3:2016. Because of this ambiguity,
+ * this method returns {@code null}.</div>
+ *
+ * <p>At unmarshalling time, events are created by an arbitrary {@link
javax.xml.stream.XMLEventReader}
+ * with namespaces used in the XML document. {@link FilteredReader}
wraps those events using this class
+ * for converting the XML namespaces to the namespaces used by JAXB
annotations.</p>
+ *
+ * <p>At marshalling time, events are created by JAXB using namespaces
used in JAXB annotations.
+ * {@link FilteredWriter} wraps those events for converting those
namespaces to the ones used in
+ * the XML document. This is the opposite than the work performed by
this default implementation
+ * and must be handled by a {@code Start} subclass.</p>
*/
@Override
- public NamespaceContext getNamespaceContext() {
- return FilteredNamespaces.exportNS(event.getNamespaceContext(),
version);
+ public String getNamespaceURI(final String prefix) {
+ return version.importNS(event.getNamespaceURI(prefix));
}
/**
- * Gets the value that the prefix is bound to in the context of this
element.
- * Default implementation is suitable for export (i.e. write
operation).
- * Needs to be overridden for imports (read operation).
+ * Returns a context mapping prefixes used in XML document to
namespaces used in JAXB annotations.
+ * The {@code FilteredNamespaces.Inverse.getNamespaceURI(String)}
method in that context shall do
+ * the same work than {@link #getNamespaceURI(String)} in this event.
*/
@Override
- public String getNamespaceURI(final String prefix) {
- return version.exportNS(event.getNamespaceURI(prefix));
+ public NamespaceContext getNamespaceContext() {
+ return FilteredNamespaces.asJAXB(event.getNamespaceContext(),
version);
}
/**
@@ -280,30 +293,4 @@ abstract class FilteredEvent<E extends X
out.append('>');
}
}
-
- /**
- * Wrapper over an element emitted during the reading of an XML document.
- */
- static final class Import extends Start {
- /** Wraps the given event with potentially different name, namespaces
and attributes. */
- Import(StartElement event, QName name, List<Namespace> namespaces,
List<Attribute> attributes, FilterVersion version) {
- super(event, name, namespaces, attributes, version);
- }
-
- /**
- * Gets a read-only namespace context.
- */
- @Override
- public NamespaceContext getNamespaceContext() {
- return FilteredNamespaces.importNS(event.getNamespaceContext(),
version);
- }
-
- /**
- * Gets the value that the prefix is bound to in the context of this
element.
- */
- @Override
- public String getNamespaceURI(final String prefix) {
- return version.importNS(event.getNamespaceURI(prefix));
- }
- }
}
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=1824269&r1=1824268&r2=1824269&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 14 21:42:41 2018
@@ -26,8 +26,7 @@ import javax.xml.XMLConstants;
/**
* In the associations between prefixes and namespaces, substitutes the
namespaces used in JAXB annotations by the
* namespaces used in the XML document at marshalling time. This class is used
internally by {@link FilteredReader}
- * 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.
+ * and {@link FilteredWriter} only.
*
* <div class="section">The problem</div>
* When the XML schemas of an international standard is updated, the URL of
the namespace is often modified.
@@ -51,7 +50,7 @@ import javax.xml.XMLConstants;
*
* An alternative is to support only one version of each standard, and
transform XML documents before unmarshalling
* or after marshalling if they use different versions of standards. We could
use XSLT for that, but this is heavy.
- * A lighter approach is to use {@link javax.xml.stream.XMLStreamReader} and
{@link javax.xml.stream.XMLStreamWriter}
+ * A lighter approach is to use {@link javax.xml.stream.XMLEventReader} and
{@link javax.xml.stream.XMLEventWriter}
* as "micro-transformers".
*
* @author Martin Desruisseaux (Geomatys)
@@ -66,23 +65,19 @@ class FilteredNamespaces implements Name
/**
* Given the context for namespaces used in our JAXB annotations, returns
a context working with namespaces
* used in XML document. The returned context converts namespace arguments
from XML to JAXB namespaces, and
- * converts returned namespaces from JAXB to XML. Prefixes are left
unchanged since they can be arbitrary
- * (even if not the standard match for a given namespace).
+ * converts returned namespaces from JAXB to XML.
*
* <div class="note"><b>Example:</b>
* for a {@code "http://www.isotc211.org/2005/gmd"} namespace (legacy ISO
19139:2007) given in argument to
- * {@link #getPrefixes(String)}, the context convert that namespace to all
possible ISO 19115-3 namespaces
+ * {@link #getPrefixes(String)}, the context converts that namespace to
all possible ISO 19115-3 namespaces
* (there is many) and returns the associated prefixes: {@code "mdb"},
{@code "cit"}, <i>etc.</i>
* Conversely given a {@code "mdb"}, {@code "cit"}, <i>etc.</i>, prefix,
{@link #getNamespaceURI(String)}
* method returns the above-cited legacy GMD namespace.</div>
- *
- * This can be used when a {@link javax.xml.stream.XMLStreamWriter} has
been created for writing with JAXB
- * annotations and we want to expose an {@code XMLStreamReader} for
writing a legacy XML document.
*/
- static NamespaceContext exportNS(NamespaceContext context, final
FilterVersion version) {
+ static NamespaceContext asXML(NamespaceContext context, final
FilterVersion version) {
if (context != null) {
- if (context instanceof Import && ((Import) context).version ==
version) {
- context = ((Import) context).context;
+ if (context instanceof Inverse && ((Inverse) context).version ==
version) {
+ context = ((Inverse) context).context;
} else {
context = new FilteredNamespaces(context, version);
}
@@ -93,16 +88,17 @@ class FilteredNamespaces implements Name
/**
* Given a context for namespaces used in XML document, returns a context
working with the namespaces used
* in our JAXB annotations. The returned context converts namespace
arguments from JAXB to XML namespaces
- * before to delegate to the wrapped context, and converts returned
namespaces from XML to JAXB. This can
- * be used when a {@link javax.xml.stream.XMLStreamReader} has been
created for reading a legacy XML document
- * and we want to expose an {@code XMLStreamReader} for JAXB.
+ * before to delegate to the wrapped context, and converts returned
namespaces from XML to JAXB.
+ *
+ * <p>This can be used when a {@link javax.xml.stream.XMLEventWriter} has
been created for writing a legacy
+ * XML document and we want to expose a {@link FilteredWriter} view to
give to JAXB.</p>
*/
- static NamespaceContext importNS(NamespaceContext context, final
FilterVersion version) {
+ static NamespaceContext asJAXB(NamespaceContext context, final
FilterVersion version) {
if (context != null) {
if (context.getClass() == FilteredNamespaces.class &&
((FilteredNamespaces) context).version == version) {
context = ((FilteredNamespaces) context).context;
} else {
- context = new FilteredNamespaces.Import(context, version);
+ context = new FilteredNamespaces.Inverse(context, version);
}
}
return context;
@@ -111,8 +107,7 @@ class FilteredNamespaces implements Name
/**
* The context to wrap, given by {@link FilteredReader} or {@link
FilteredWriter}.
*
- * @see javax.xml.stream.XMLStreamReader#getNamespaceContext()
- * @see javax.xml.stream.XMLStreamWriter#getNamespaceContext()
+ * @see javax.xml.stream.XMLEventWriter#getNamespaceContext()
*/
final NamespaceContext context;
@@ -131,13 +126,14 @@ class FilteredNamespaces implements Name
/**
* Substitutes the XML namespaces used in XML documents by namespaces used
in JAXB annotations.
- * This is used at unmarshalling time for importing legacy documents,
performing the reverse of
- * {@link FilteredNamespaces}. The <i>namespace → prefix</i> mapping is
simpler because various
- * ISO 19115-3 namespaces are mapped to the same legacy {@code "gmd"}
prefix.
+ * This is used at marshalling time for exporting legacy documents,
performing the reverse of
+ * {@link FilteredNamespaces}. The <i>namespace → prefix</i> mapping is
simple because various
+ * ISO 19115-3 namespaces are mapped to the same legacy {@code "gmd"}
prefix, but the reverse
+ * operation (<i>prefix → namespace</i> mapping) can often not be resolved.
*/
- private static final class Import extends FilteredNamespaces {
+ private static final class Inverse extends FilteredNamespaces {
/** Creates a new namespaces filter for the given source version. */
- Import(final NamespaceContext context, final FilterVersion version) {
+ Inverse(final NamespaceContext context, final FilterVersion version) {
super(context, version);
}
@@ -148,6 +144,8 @@ class FilteredNamespaces implements Name
*
* <p>Except for {@code NULL_NS_URI}, this is usually an
<cite>injective</cite> function:
* each namespace can be created from at most one prefix.</p>
+ *
+ * @see FilteredEvent.Start#getNamespaceURI(String)
*/
@Override public String getNamespaceURI(final String prefix) {
return version.importNS(context.getNamespaceURI(prefix));
Modified:
sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/xml/FilteredReader.java
URL:
http://svn.apache.org/viewvc/sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/xml/FilteredReader.java?rev=1824269&r1=1824268&r2=1824269&view=diff
==============================================================================
---
sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/xml/FilteredReader.java
[UTF-8] (original)
+++
sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/xml/FilteredReader.java
[UTF-8] Wed Feb 14 21:42:41 2018
@@ -225,12 +225,21 @@ final class FilteredReader extends Filte
private final List<QName> outerElements;
/**
+ * The prefixes for namespace URIs. Keys are URIs used in JAXB annotations
and values are prefixes
+ * computed by {@link Namespaces#getPreferredPrefix(String, String)} or
any other means. We store
+ * the prefix both for performance reasons and for improving the
guarantees that the URI → prefix
+ * mapping is stable.
+ */
+ private final Map<String,String> prefixes;
+
+ /**
* Creates a new filter for the given version of the standards.
*/
FilteredReader(final XMLEventReader in, final FilterVersion version) {
super(version);
this.in = in;
outerElements = new ArrayList<>();
+ prefixes = new HashMap<>();
}
/**
@@ -289,7 +298,7 @@ final class FilteredReader extends Filte
break;
}
case NAMESPACE: {
- event = importNS((Namespace) event);
+ event = importNS((Namespace) event, null, null);
break;
}
case START_ELEMENT: {
@@ -303,9 +312,10 @@ final class FilteredReader extends Filte
changed |= (a != ae);
renamedAttributes.add(ae);
}
- final List<Namespace> namespaces = importNS(e.getNamespaces(),
changed);
+ final List<Namespace> namespaces = importNS(e.getNamespaces(),
+ originalName.getNamespaceURI(),
name.getNamespaceURI(), changed);
if (namespaces != null) {
- event = new FilteredEvent.Import(e, name, namespaces,
attributes(), version);
+ event = new FilteredEvent.Start(e, name, namespaces,
attributes(), version);
} else {
renamedAttributes.clear();
}
@@ -318,7 +328,8 @@ final class FilteredReader extends Filte
final EndElement e = event.asEndElement();
final QName originalName = e.getName();
final QName name = convert(originalName);
- final List<Namespace> namespaces = importNS(e.getNamespaces(),
name != originalName);
+ final List<Namespace> namespaces = importNS(e.getNamespaces(),
+ originalName.getNamespaceURI(),
name.getNamespaceURI(), name != originalName);
if (namespaces != null) {
event = new FilteredEvent.End(e, name, namespaces);
}
@@ -350,7 +361,7 @@ final class FilteredReader extends Filte
* @return the namespace URI for the element or attribute in the current
context (e.g. an ISO 19115-3 namespace),
* or {@code null} if the given name is unknown.
*/
- private QName convert(QName name) {
+ private QName convert(final QName name) {
String namespace = null; // In this method,
null means no change to given name.
String localPart = name.getLocalPart();
final Set<String> declaringTypes = DECLARING_TYPES.get(localPart);
@@ -395,16 +406,17 @@ final class FilteredReader extends Filte
if (namespace == null) {
namespace = version.importNS(oldNS);
}
+ if (namespace.equals(oldNS) && localPart.equals(name.getLocalPart())) {
+ return name;
+ }
/*
- * Build a new name if any component (URI or local part) changed. Do
not specify the prefix
- * because the same URI in the XML document could map to many
different prefixes after we
- * converted to the namespaces used by JAXB. Having the same prefix
for those different URI
- * may be a source of confusion.
+ * Build a new name if any component (URI or local part) changed. The
prefix should have
+ * been specified (indirectly) by a previous call to
'importNS(Namespace)', for example
+ * as a result of a NAMESPACE event. If not, we compute it now using
the same method.
*/
- if (!namespace.equals(oldNS) ||
!localPart.equals(name.getLocalPart())) {
- name = new QName(namespace, localPart);
- }
- return name;
+ final String prefix = prefixes.computeIfAbsent(namespace,
+ (ns) -> Namespaces.getPreferredPrefix(ns, name.getPrefix()));
+ return new QName(namespace, localPart, prefix);
}
/**
@@ -421,18 +433,28 @@ final class FilteredReader extends Filte
}
/**
- * Imports a namespace read from the XML document. This may imply a prefix
change.
- * If there is no namespace change, then this method returns the given
instance as-is.
+ * Converts a namespace read from the XML document to the namespace used
by JAXB annotations.
+ * This methods can convert the namespace for which there is a bijective
mapping, for example
+ * {@code "http://www.isotc211.org/2005/gco"} to {@code
"http://standards.iso.org/iso/19115/-3/gco/1.0"}.
+ * However some namespaces like {@code "http://www.isotc211.org/2005/gmd"}
may be left unchanged,
+ * because that namespace from legacy ISO 19139:2007 can be mapped to many
different namespaces
+ * in newer ISO 19115-3:2016 standard. However in some cases the context
allows us to determines
+ * which newer namespace is used. In such case, that mapping is specified
by the
+ * ({@code oldURI}, {@code newURI}) pair.
*
* @param namespace the namespace to import.
+ * @param oldURI an old URI which has been renamed as {@code newURI},
or {@code null} if none.
+ * @param newURI the new URI for {@code oldURI}, or {@code null} if
{@code newURI} is null.
*/
- private Namespace importNS(final Namespace namespace) {
+ private Namespace importNS(final Namespace namespace, final String oldURI,
final String newURI) {
String uri = namespace.getNamespaceURI();
if (uri != null && !uri.isEmpty()) {
uri = FilteredXML.removeTrailingSlash(uri);
- final String imported = version.importNS(uri);
+ final String imported = uri.equals(oldURI) ? newURI :
version.importNS(uri);
if (imported != uri) {
- return new FilteredEvent.NS(namespace,
Namespaces.getPreferredPrefix(imported, namespace.getPrefix()), imported);
+ final String prefix = prefixes.computeIfAbsent(imported,
+ (ns) -> Namespaces.getPreferredPrefix(ns,
namespace.getPrefix()));
+ return new FilteredEvent.NS(namespace, prefix, imported);
}
}
return namespace;
@@ -442,17 +464,21 @@ final class FilteredReader extends Filte
* Imports the namespaces read from the XML document.
*
* @param namespaces the namespaces to filter.
+ * @param oldURI an old URI which has been renamed as {@code
newURI}, or {@code null} if none.
+ * @param newURI the new URI for {@code oldURI}, or {@code null} if
{@code newURI} is null.
* @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> importNS(final Iterator<Namespace> namespaces,
boolean changed) {
+ private List<Namespace> importNS(final Iterator<Namespace> namespaces,
+ final String oldURI, final String newURI, boolean changed)
+ {
if (!namespaces.hasNext()) {
return changed ? Collections.emptyList() : null;
}
final List<Namespace> modified = new ArrayList<>();
do {
Namespace namespace = namespaces.next();
- changed |= (namespace != (namespace = importNS(namespace)));
+ changed |= (namespace != (namespace = importNS(namespace, oldURI,
newURI)));
modified.add(namespace);
} while (namespaces.hasNext());
return changed ? modified : null;
Modified:
sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/xml/FilteredWriter.java
URL:
http://svn.apache.org/viewvc/sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/xml/FilteredWriter.java?rev=1824269&r1=1824268&r2=1824269&view=diff
==============================================================================
---
sis/branches/ISO-19115-3/core/sis-utility/src/main/java/org/apache/sis/xml/FilteredWriter.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 14 21:42:41 2018
@@ -65,7 +65,7 @@ final class FilteredWriter extends Filte
* <li>In the namespaces of a start element.</li>
* </ul>
*/
- private final Map<String, Namespace> uniqueNamespaces;
+ private final Map<String,Namespace> uniqueNamespaces;
/**
* Creates a new filter for the given version of the standards.
@@ -77,12 +77,48 @@ final class FilteredWriter extends Filte
}
/**
+ * Returns the name (prefix, namespace and local part) to write in the XML
document.
+ * This method may replace the namespace, and in some case the name local
part too.
+ * If there is no name change, then this method returns the given instance
as-is.
+ */
+ private QName export(QName name) throws XMLStreamException {
+ String uri = name.getNamespaceURI();
+ if (uri != null && !uri.isEmpty()) { //
Optimization for a common case.
+ final FilterVersion.Replacement r = version.export(uri);
+ if (r != null) {
+ uri = r.namespace;
+ /*
+ * The wrapped XMLEventWriter maintains a mapping from
prefixes to namespace URIs.
+ * Arguments are exported URIs (e.g. from legacy ISO
19139:2007) and return values
+ * are prefixes computed by 'Namespaces.getPreferredPrefix(…)'
or any other means.
+ * We fetch those prefixes for performance reasons and for
improving the guarantees
+ * that the URI → prefix mapping is stable, since JAXB seems
to require them for
+ * writing namespaces in XML.
+ */
+ String prefix = out.getPrefix(uri);
+ if (prefix == null) {
+ prefix = Namespaces.getPreferredPrefix(uri,
name.getPrefix());
+ out.setPrefix(prefix, uri);
+ /*
+ * The above call for 'getPreferredPrefix' above is
required: JAXB seems to need the prefixes
+ * for recognizing namespaces. The prefix shall be
computed in the same way than 'exportIfNew'.
+ * We enter in this block only for the root element,
before to parse 'xmlns' attributes. For
+ * all other elements after the root elements, above call
to 'out.getPrefix(uri)' should succeed.
+ */
+ }
+ name = new QName(uri, r.exportProperty(name.getLocalPart()),
prefix);
+ }
+ }
+ return name;
+ }
+
+ /**
* 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) {
+ private Attribute export(Attribute attribute) throws XMLStreamException {
final QName originalName = attribute.getName();
- final QName name = version.export(originalName);
+ final QName name = export(originalName);
if (name != originalName) {
attribute = new FilteredEvent.Attr(attribute, name);
}
@@ -104,6 +140,10 @@ final class FilteredWriter extends Filte
if (exported != uri) {
return uniqueNamespaces.computeIfAbsent(exported, (k) -> {
return new FilteredEvent.NS(namespace,
Namespaces.getPreferredPrefix(k, namespace.getPrefix()), k);
+ /*
+ * The new prefix selected by above line will be saved by
out.add(Namespace)
+ * after this method has been invoked by the NAMESPACE
case of add(XMLEvent).
+ */
});
}
final Namespace c = uniqueNamespaces.put(uri, namespace); // No
namespace change needed. Overwrite wrapper if any.
@@ -165,7 +205,7 @@ final class FilteredWriter extends Filte
uniqueNamespaces.clear(); // Discard
entries created by NAMESPACE events.
final StartElement e = event.asStartElement();
final QName originalName = e.getName();
- final QName name = version.export(originalName);
+ final QName name = export(originalName);
boolean changed = name != originalName;
for (final Iterator<Attribute> it = e.getAttributes();
it.hasNext();) {
final Attribute a = it.next();
@@ -175,7 +215,7 @@ final class FilteredWriter extends Filte
}
final List<Namespace> namespaces = export(e.getNamespaces(),
changed);
if (namespaces != null) {
- event = new FilteredEvent.Start(e, name, namespaces,
attributes(), version);
+ event = new Event(e, name, namespaces, attributes(),
version);
} else {
renamedAttributes.clear();
}
@@ -184,7 +224,7 @@ final class FilteredWriter extends Filte
case END_ELEMENT: {
final EndElement e = event.asEndElement();
final QName originalName = e.getName();
- final QName name = version.export(originalName);
+ final QName name = export(originalName);
final List<Namespace> namespaces = export(e.getNamespaces(),
name != originalName);
if (namespaces != null) {
event = new FilteredEvent.End(e, name, namespaces);
@@ -250,18 +290,60 @@ final class FilteredWriter extends Filte
* <p>Implemented as a matter of principle, but JAXB did not invoked this
method in our tests.</p>
*/
@Override
- public void setNamespaceContext(NamespaceContext context) throws
XMLStreamException {
- out.setNamespaceContext(FilteredNamespaces.importNS(context, version));
+ public void setNamespaceContext(final NamespaceContext context) throws
XMLStreamException {
+ out.setNamespaceContext(FilteredNamespaces.asXML(context, version));
}
/**
- * Returns the context of the underlying writer wrapped in a filter that
convert the namespaces on the fly.
+ * Returns a naming context suitable for consumption by JAXB marshallers.
+ * The {@link XMLEventWriter} wrapped by this {@code FilteredWriter} has
been created for writing in a file.
+ * Consequently its naming context manages namespaces used in the XML
document. But the JAXB marshaller using
+ * this {@code FilteredWriter} facade expects the namespaces declared in
JAXB annotations. Consequently this
+ * method returns an adapter that converts namespaces on the fly.
*
- * @see FilteredEvent.Start#getNamespaceContext()
+ * @see Event#getNamespaceContext()
*/
@Override
public NamespaceContext getNamespaceContext() {
- return FilteredNamespaces.exportNS(out.getNamespaceContext(), version);
+ return FilteredNamespaces.asJAXB(out.getNamespaceContext(), version);
+ }
+
+ /**
+ * Wraps the {@link StartElement} produced by JAXB for using the
namespaces used in the XML document.
+ */
+ private static final class Event extends FilteredEvent.Start {
+ /** Wraps the given event with potentially different name, namespaces
and attributes. */
+ Event(StartElement event, QName name, List<Namespace> namespaces,
List<Attribute> attributes, FilterVersion version) {
+ super(event, name, namespaces, attributes, version);
+ }
+
+ /**
+ * Gets the URI used in the XML document for the given prefix used in
JAXB annotations.
+ * At marshalling time, events are created by JAXB using namespaces
used in JAXB annotations.
+ * {@link FilteredWriter} wraps those events for converting those
namespaces to the ones used
+ * in the XML document.
+ *
+ * <div class="note"><b>Example:</b> the {@code "cit"} prefix from ISO
19115-3:2016 standard
+ * represents the {@code
"http://standards.iso.org/iso/19115/-3/mdb/1.0"} namespace, which is
+ * mapped to {@code "http://www.isotc211.org/2005/gmd"} in the legacy
ISO 19139:2007 standard.
+ * That later URI is returned.</div>
+ */
+ @Override
+ public String getNamespaceURI(final String prefix) {
+ return version.exportNS(event.getNamespaceURI(prefix));
+ }
+
+ /**
+ * Returns a context mapping prefixes used in JAXB annotations to
namespaces used in XML document.
+ * The {@link FilteredNamespaces#getNamespaceURI(String)} method in
that context shall do the same
+ * work than {@link #getNamespaceURI(String)} in this event.
+ *
+ * @see FilteredNamespaces#getNamespaceURI(String)
+ */
+ @Override
+ public NamespaceContext getNamespaceContext() {
+ return FilteredNamespaces.asXML(event.getNamespaceContext(),
version);
+ }
}
/**
Modified:
sis/branches/ISO-19115-3/core/sis-utility/src/test/java/org/apache/sis/xml/FilteredNamespacesTest.java
URL:
http://svn.apache.org/viewvc/sis/branches/ISO-19115-3/core/sis-utility/src/test/java/org/apache/sis/xml/FilteredNamespacesTest.java?rev=1824269&r1=1824268&r2=1824269&view=diff
==============================================================================
---
sis/branches/ISO-19115-3/core/sis-utility/src/test/java/org/apache/sis/xml/FilteredNamespacesTest.java
[UTF-8] (original)
+++
sis/branches/ISO-19115-3/core/sis-utility/src/test/java/org/apache/sis/xml/FilteredNamespacesTest.java
[UTF-8] Wed Feb 14 21:42:41 2018
@@ -50,8 +50,9 @@ public final strictfp class FilteredName
* Tests {@link FilteredNamespaces#getPrefixes(String)}.
*/
@Test
+ @SuppressWarnings("unchecked") // TODO: remove with JDK9
public void testGetPrefixes() {
- final NamespaceContext fns = FilteredNamespaces.exportNS(this,
FilterVersion.ISO19139);
+ final NamespaceContext fns = FilteredNamespaces.asXML(this,
FilterVersion.ISO19139);
final Iterator<String> it = fns.getPrefixes(LegacyNamespaces.GMD);
final Set<String> prefixes = new HashSet<>();
while (it.hasNext()) {
@@ -91,7 +92,7 @@ public final strictfp class FilteredName
*/
@Test
public void testGetPrefix() {
- final NamespaceContext fns = FilteredNamespaces.exportNS(this,
FilterVersion.ISO19139);
+ final NamespaceContext fns = FilteredNamespaces.asXML(this,
FilterVersion.ISO19139);
/*
* Following tests are not really interesting since FilteredNamespaces,
* after failing to find a mapping, just delegates to
this.getPrefix(…).