Author: desruisseaux
Date: Sat Jan 28 04:48:30 2017
New Revision: 1780633
URL: http://svn.apache.org/viewvc?rev=1780633&view=rev
Log:
Allow formatted XML output when using XMLStreamWriter. This apply to GPX format.
Added:
sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/xml/Implementation.java (with props)
sis/branches/JDK8/storage/sis-xmlstore/src/main/java/org/apache/sis/internal/storage/xml/stream/FormattedWriter.java (with props)
Modified:
sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/io/wkt/FormattableObject.java
sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/io/wkt/Formatter.java
sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/io/wkt/WKTFormat.java
sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/report/HTMLGenerator.java
sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/util/Constants.java
sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/util/StreamWriterDelegate.java
sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/util/Utilities.java
sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/setup/OptionKey.java
sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/xml/MarshallerPool.java
sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/xml/Pooled.java
sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/xml/PooledTemplate.java
sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/xml/XML.java
sis/branches/JDK8/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Resources.java
sis/branches/JDK8/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Resources.properties
sis/branches/JDK8/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Resources_fr.properties
sis/branches/JDK8/storage/sis-netcdf/src/main/java/org/apache/sis/storage/netcdf/NetcdfStoreProvider.java
sis/branches/JDK8/storage/sis-xmlstore/src/main/java/org/apache/sis/internal/storage/xml/stream/StaxDataStore.java
sis/branches/JDK8/storage/sis-xmlstore/src/main/java/org/apache/sis/internal/storage/xml/stream/StaxStreamWriter.java
Modified: sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/io/wkt/FormattableObject.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/io/wkt/FormattableObject.java?rev=1780633&r1=1780632&r2=1780633&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/io/wkt/FormattableObject.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/io/wkt/FormattableObject.java [UTF-8] Sat Jan 28 04:48:30 2017
@@ -23,6 +23,7 @@ import javax.xml.bind.annotation.XmlTran
import org.apache.sis.util.Debug;
import org.apache.sis.util.ArgumentChecks;
import org.apache.sis.internal.util.X364;
+import org.apache.sis.internal.util.Constants;
/**
@@ -186,7 +187,7 @@ public abstract class FormattableObject
formatter.configure(convention, null, colorize ? Colors.DEFAULT : null,
convention.toUpperCase ? (byte) +1 : 0,
(convention.majorVersion() == 1) ? (byte) -1 : 0,
- WKTFormat.DEFAULT_INDENTATION);
+ Constants.DEFAULT_INDENTATION);
if (!strict) {
formatter.transliterator = Transliterator.IDENTITY;
}
Modified: sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/io/wkt/Formatter.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/io/wkt/Formatter.java?rev=1780633&r1=1780632&r2=1780633&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/io/wkt/Formatter.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/io/wkt/Formatter.java [UTF-8] Sat Jan 28 04:48:30 2017
@@ -320,7 +320,7 @@ public class Formatter implements Locali
* Creates a new formatter instance with the default configuration.
*/
public Formatter() {
- this(Convention.DEFAULT, Symbols.getDefault(), WKTFormat.DEFAULT_INDENTATION);
+ this(Convention.DEFAULT, Symbols.getDefault(), Constants.DEFAULT_INDENTATION);
}
/**
@@ -363,7 +363,7 @@ public class Formatter implements Locali
this.authority = Convention.DEFAULT.getNameAuthority();
this.symbols = symbols;
this.lineSeparator = this.symbols.lineSeparator();
- this.indentation = WKTFormat.DEFAULT_INDENTATION;
+ this.indentation = Constants.DEFAULT_INDENTATION;
this.numberFormat = numberFormat; // No clone needed.
this.dateFormat = dateFormat;
this.unitFormat = unitFormat;
Modified: sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/io/wkt/WKTFormat.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/io/wkt/WKTFormat.java?rev=1780633&r1=1780632&r2=1780633&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/io/wkt/WKTFormat.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/io/wkt/WKTFormat.java [UTF-8] Sat Jan 28 04:48:30 2017
@@ -44,6 +44,7 @@ import org.apache.sis.measure.UnitFormat
import org.apache.sis.util.CharSequences;
import org.apache.sis.util.ArgumentChecks;
import org.apache.sis.util.resources.Errors;
+import org.apache.sis.internal.util.Constants;
import org.apache.sis.internal.util.StandardDateFormat;
@@ -121,15 +122,14 @@ public class WKTFormat extends CompoundF
/**
* The indentation value to give to the {@link #setIndentation(int)}
* method for formatting the complete object on a single line.
+ *
+ * @see #getIndentation()
+ * @see #setIndentation(int)
+ * @see org.apache.sis.setup.OptionKey#INDENTATION
*/
public static final int SINGLE_LINE = -1;
/**
- * The default indentation value.
- */
- static final byte DEFAULT_INDENTATION = 2;
-
- /**
* The symbols to use for this formatter.
* The same object is also referenced in the {@linkplain #parser} and {@linkplain #formatter}.
* It appears here for serialization purpose.
@@ -240,7 +240,7 @@ public class WKTFormat extends CompoundF
symbols = Symbols.getDefault();
keywordCase = KeywordCase.DEFAULT;
keywordStyle = KeywordStyle.DEFAULT;
- indentation = DEFAULT_INDENTATION;
+ indentation = Constants.DEFAULT_INDENTATION;
}
/**
@@ -537,6 +537,8 @@ public class WKTFormat extends CompoundF
* The {@value #SINGLE_LINE} value means that the whole WKT is to be formatted on a single line.
*
* @param indentation the new indentation to use.
+ *
+ * @see org.apache.sis.setup.OptionKey#INDENTATION
*/
public void setIndentation(final int indentation) {
ArgumentChecks.ensureBetween("indentation", SINGLE_LINE, Byte.MAX_VALUE, indentation);
Modified: sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/report/HTMLGenerator.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/report/HTMLGenerator.java?rev=1780633&r1=1780632&r2=1780633&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/report/HTMLGenerator.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/report/HTMLGenerator.java [UTF-8] Sat Jan 28 04:48:30 2017
@@ -26,6 +26,7 @@ import java.io.BufferedWriter;
import java.io.OutputStreamWriter;
import java.io.Closeable;
import org.opengis.util.InternationalString;
+import org.apache.sis.internal.util.Constants;
import org.apache.sis.util.CharSequences;
import org.apache.sis.util.Deprecable;
@@ -59,7 +60,7 @@ abstract strictfp class HTMLGenerator im
* The number of space to add or remove in the {@linkplain #margin}
* when new HTML elements are opened or closed.
*/
- private static final int INDENTATION = 2;
+ private static final int INDENTATION = Constants.DEFAULT_INDENTATION;
/**
* Where to write the HTML page.
Modified: sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/util/Constants.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/util/Constants.java?rev=1780633&r1=1780632&r2=1780633&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/util/Constants.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/util/Constants.java [UTF-8] Sat Jan 28 04:48:30 2017
@@ -37,6 +37,14 @@ import org.apache.sis.util.Static;
*/
public final class Constants extends Static {
/**
+ * The default indentation value to use in various text formats (both WKT and XML).
+ * We use a small value (2 instead of 4) because OGC's XML are very verbose.
+ *
+ * @see org.apache.sis.setup.OptionKey#INDENTATION
+ */
+ public static final byte DEFAULT_INDENTATION = 2;
+
+ /**
* The {@value} code space.
*/
public static final String EPSG = "EPSG";
Modified: sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/util/StreamWriterDelegate.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/util/StreamWriterDelegate.java?rev=1780633&r1=1780632&r2=1780633&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/util/StreamWriterDelegate.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/util/StreamWriterDelegate.java [UTF-8] Sat Jan 28 04:48:30 2017
@@ -72,20 +72,20 @@ public class StreamWriterDelegate implem
/** Forwards the call verbatim. */
@Override
- public void writeEmptyElement(String namespaceURI, String localName) throws XMLStreamException {
- out.writeEmptyElement(namespaceURI, localName);
+ public void writeEmptyElement(String localName) throws XMLStreamException {
+ out.writeEmptyElement(localName);
}
/** Forwards the call verbatim. */
@Override
- public void writeEmptyElement(String prefix, String localName, String namespaceURI) throws XMLStreamException {
- out.writeEmptyElement(prefix, localName, namespaceURI);
+ public void writeEmptyElement(String namespaceURI, String localName) throws XMLStreamException {
+ out.writeEmptyElement(namespaceURI, localName);
}
/** Forwards the call verbatim. */
@Override
- public void writeEmptyElement(String localName) throws XMLStreamException {
- out.writeEmptyElement(localName);
+ public void writeEmptyElement(String prefix, String localName, String namespaceURI) throws XMLStreamException {
+ out.writeEmptyElement(prefix, localName, namespaceURI);
}
/** Forwards the call verbatim. */
Modified: sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/util/Utilities.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/util/Utilities.java?rev=1780633&r1=1780632&r2=1780633&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/util/Utilities.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/util/Utilities.java [UTF-8] Sat Jan 28 04:48:30 2017
@@ -191,8 +191,10 @@ public final class Utilities extends Sta
if (precision >= 0) {
for (int i=0,n=0; i<length; i += n) {
if (--precision < 0) {
- // Found the amount of characters to keep. The 'n' variable can be
- // zero only if precision == 0, in which case the string is empty.
+ /*
+ * Found the amount of characters to keep. The 'n' variable can be
+ * zero only if precision == 0, in which case the string is empty.
+ */
if (n == 0) {
value = "";
} else {
Modified: sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/setup/OptionKey.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/setup/OptionKey.java?rev=1780633&r1=1780632&r2=1780633&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/setup/OptionKey.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/setup/OptionKey.java [UTF-8] Sat Jan 28 04:48:30 2017
@@ -113,6 +113,8 @@ public class OptionKey<T> implements Ser
* <p>If this option is not provided, then the default value is format specific.
* That default is often, but not necessarily, the {@linkplain Charset#defaultCharset() platform default}.</p>
*
+ * @see javax.xml.bind.Marshaller#JAXB_ENCODING
+ *
* @since 0.4
*/
public static final OptionKey<Charset> ENCODING = new OptionKey<>("ENCODING", Charset.class);
@@ -179,6 +181,27 @@ public class OptionKey<T> implements Ser
public static final OptionKey<ByteBuffer> BYTE_BUFFER = new OptionKey<>("BYTE_BUFFER", ByteBuffer.class);
/**
+ * The number of spaces to use for indentation when formatting text files in WKT or XML formats.
+ * A value of {@value org.apache.sis.io.wkt.WKTFormat#SINGLE_LINE} means to format the whole WKT
+ * or XML document on a single line without line feeds or indentation.
+ *
+ * <p>If this option is not provided, then the most typical default value used in Apache SIS is 2.
+ * Such small indentation value is used because XML documents defined by OGC standards tend to be
+ * verbose.</p>
+ *
+ * @see org.apache.sis.io.wkt.WKTFormat#SINGLE_LINE
+ * @see javax.xml.bind.Marshaller#JAXB_FORMATTED_OUTPUT
+ *
+ * @since 0.8
+ */
+ public static final OptionKey<Integer> INDENTATION = new OptionKey<>("INDENTATION", Integer.class);
+
+ /*
+ * Note: we do not provide a LINE_SEPARATOR option for now because we can not control the line separator
+ * in JDK's JAXB implementation, and Apache SIS provides an org.apache.sis.io.LineAppender alternative.
+ */
+
+ /**
* The name of this key. For {@code OptionKey} instances, it shall be the name of the static constants.
* For subclasses of {@code OptionKey}, there is no restriction.
*/
Added: sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/xml/Implementation.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/xml/Implementation.java?rev=1780633&view=auto
==============================================================================
--- sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/xml/Implementation.java (added)
+++ sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/xml/Implementation.java [UTF-8] Sat Jan 28 04:48:30 2017
@@ -0,0 +1,145 @@
+/*
+ * 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 javax.xml.bind.JAXBContext;
+
+
+/**
+ * Known JAXB implementations.
+ * This enumeration allows to set vendor-specific marshaller properties.
+ *
+ * @author Martin Desruisseaux (Geomatys)
+ * @since 0.8
+ * @version 0.8
+ * @module
+ */
+enum Implementation {
+ /**
+ * JAXB implementation bundled in the JDK.
+ */
+ INTERNAL("com.sun.xml.internal.bind.indentString",
+ "com.sun.xml.internal.bind.namespacePrefixMapper",
+ "org.apache.sis.xml.OGCNamespacePrefixMapper"),
+
+ /**
+ * JAXB implementation provided in a separated JAR, used for instance by Glassfish.
+ */
+ ENDORSED("com.sun.xml.bind.indentString",
+ "com.sun.xml.bind.namespacePrefixMapper",
+ "org.apache.sis.xml.OGCNamespacePrefixMapper_Endorsed"),
+
+ /**
+ * Unrecognized implementation.
+ */
+ OTHER(null, null, null);
+
+ /**
+ * The prefix of property names which are provided in external (endorsed) implementation of JAXB.
+ * This is slightly different than the prefix used by the implementation bundled with the JDK 6,
+ * which is {@code "com.sun.xml.internal.bind"}.
+ */
+ private static final String ENDORSED_PREFIX = "com.sun.xml.bind.";
+
+ /**
+ * The prefix of property names which are provided in internal implementation of JAXB
+ * (the one bundled with the JDK 6).
+ */
+ private static final String INTERNAL_PREFIX = "com.sun.xml.internal.bind.";
+
+ /**
+ * The JAXB property for setting the indentation string, or {@code null} if none.
+ */
+ final String indentKey;
+
+ /**
+ * The JAXB property for setting the namespace prefix mapper, or {@code null} if none.
+ */
+ final String mapperKey;
+
+ /**
+ * The JAXB property for setting the namespace prefix mapper, or {@code null} if none.
+ */
+ final String mapper;
+
+ /**
+ * Creates a new enumeration value for a JAXB implementation.
+ */
+ private Implementation(final String indentKey, final String mapperKey, final String mapper) {
+ this.indentKey = indentKey;
+ this.mapperKey = mapperKey;
+ this.mapper = mapper;
+ }
+
+ /**
+ * Detects if we are using the endorsed JAXB implementation (the one provided in separated JAR files)
+ * or the one bundled in JDK. We use the JAXB context package name as a criterion:
+ *
+ * <ul>
+ * <li>JAXB endorsed JAR uses {@code "com.sun.xml.bind.*"}</li>
+ * <li>JAXB bundled in JDK uses {@code "com.sun.xml.internal.bind.*"}</li>
+ * </ul>
+ *
+ * @param context the JAXB context for which to detect the implementation.
+ * @return the implementation, or {@code OTHER} if unknown.
+ */
+ public static Implementation detect(final JAXBContext context) {
+ if (context != null) {
+ final String classname = context.getClass().getName();
+ if (classname.startsWith(INTERNAL_PREFIX)) {
+ return INTERNAL;
+ } else if (classname.startsWith(ENDORSED_PREFIX)) {
+ return ENDORSED;
+ }
+ }
+ return OTHER;
+ }
+
+ /**
+ * Returns {@code false} if the given (un)marshaller property should be silently ignored.
+ * A value of {@code true} does not necessarily mean that the given property is supported,
+ * but that the caller should either support the property or throw an exception.
+ *
+ * <p>This method excludes the {@code "com.sun.xml.bind.*"} properties if the implementation
+ * is not {@link #ENDORSED} or {@link #INTERNAL}. We do not distinguish between the endorsed
+ * and internal namespaces since Apache SIS use only the endorsed namespace and let
+ * {@code org.apache.sis.xml.Pooled} do the conversion to internal namespace if needed.</p>
+ *
+ * @param key the property key to test.
+ * @return {@code false} if the given property should be silently ignored.
+ */
+ boolean filterProperty(final String key) {
+ // We user 'mapper' as a sentinel value for identifying INTERNAL and ENDORSED cases.
+ return (mapper != null) || !key.startsWith(ENDORSED_PREFIX);
+ }
+
+ /**
+ * Converts the given key from {@code "com.sun.xml.bind.*"} to {@code "com.sun.xml.internal.bind.*"} namespace.
+ * This method is invoked when the JAXB implementation is known to be the {@link #INTERNAL} one. We perform this
+ * conversion for allowing Apache SIS to ignore the difference between internal and endorsed JAXB.
+ *
+ * @param key the key that may potentially a endorsed JAXB key.
+ * @return the key as an internal JAXB key, or the given key unchanged if it is not an endorsed JAXB key.
+ */
+ static String toInternal(String key) {
+ if (key.startsWith(ENDORSED_PREFIX)) {
+ final StringBuilder buffer = new StringBuilder(key.length() + 10);
+ key = buffer.append(INTERNAL_PREFIX).append(key, ENDORSED_PREFIX.length(), key.length()).toString();
+ }
+ return key;
+ }
+}
Propchange: sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/xml/Implementation.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/xml/Implementation.java
------------------------------------------------------------------------------
svn:mime-type = text/plain;charset=UTF-8
Modified: sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/xml/MarshallerPool.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/xml/MarshallerPool.java?rev=1780633&r1=1780632&r2=1780633&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/xml/MarshallerPool.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/xml/MarshallerPool.java [UTF-8] Sat Jan 28 04:48:30 2017
@@ -32,7 +32,9 @@ import org.apache.sis.internal.system.De
import org.apache.sis.internal.system.DefaultFactories;
import org.apache.sis.internal.jaxb.AdapterReplacement;
import org.apache.sis.internal.jaxb.TypeRegistration;
+import org.apache.sis.internal.util.Constants;
import org.apache.sis.util.ArgumentChecks;
+import org.apache.sis.util.CharSequences;
/**
@@ -66,11 +68,6 @@ import org.apache.sis.util.ArgumentCheck
*/
public class MarshallerPool {
/**
- * The indentation string, fixed to 2 spaces instead of 4 because ISO/OGC XML are very verbose.
- */
- private static final String INDENTATION = " ";
-
- /**
* Amount of nanoseconds to wait before to remove unused (un)marshallers.
* This is a very approximative value: actual timeout will not be shorter,
* but may be twice longer.
@@ -78,21 +75,19 @@ public class MarshallerPool {
private static final long TIMEOUT = 15000000000L; // 15 seconds.
/**
- * Kind of JAXB implementations.
- */
- private static final byte INTERNAL = 0, ENDORSED = 1, OTHER = 2;
-
- /**
* The JAXB context to use for creating marshaller and unmarshaller.
+ *
+ * @see #createMarshaller()
+ * @see #createUnmarshaller()
*/
- private final JAXBContext context;
+ protected final JAXBContext context;
/**
- * {@link #INTERNAL} if the JAXB implementation is the one bundled in the JDK,
- * {@link #ENDORSED} if the TAXB implementation is the endorsed JAXB (Glassfish), or
- * {@link #OTHER} if unknown.
+ * {@code INTERNAL} if the JAXB implementation is the one bundled in the JDK,
+ * {@code ENDORSED} if the TAXB implementation is the endorsed JAXB (Glassfish), or
+ * {@code null} if unknown.
*/
- private final byte implementation;
+ private final Implementation implementation;
/**
* The mapper between namespaces and prefix.
@@ -191,30 +186,12 @@ public class MarshallerPool {
ArgumentChecks.ensureNonNull("context", context);
this.context = context;
replacements = DefaultFactories.createServiceLoader(AdapterReplacement.class);
- /*
- * Detects if we are using the endorsed JAXB implementation (i.e. the one provided in
- * separated JAR files) or the one bundled in JDK 6. We use the JAXB context package
- * name as a criterion:
- *
- * JAXB endorsed JAR uses "com.sun.xml.bind"
- * JAXB bundled in JDK uses "com.sun.xml.internal.bind"
- */
- String classname = context.getClass().getName();
- if (classname.startsWith("com.sun.xml.internal.bind.")) {
- classname = "org.apache.sis.xml.OGCNamespacePrefixMapper";
- implementation = INTERNAL;
- } else if (classname.startsWith(Pooled.ENDORSED_PREFIX)) {
- classname = "org.apache.sis.xml.OGCNamespacePrefixMapper_Endorsed";
- implementation = ENDORSED;
- } else {
- classname = null;
- implementation = OTHER;
- }
+ implementation = Implementation.detect(context);
/*
* Prepares a copy of the property map (if any), then removes the
* properties which are handled especially by this constructor.
*/
- template = new PooledTemplate(properties, implementation == INTERNAL);
+ template = new PooledTemplate(properties, implementation);
final Object rootNamespace = template.remove(XML.DEFAULT_NAMESPACE, "");
/*
* Instantiates the OGCNamespacePrefixMapper appropriate for the implementation
@@ -222,6 +199,7 @@ public class MarshallerPool {
* usual ClassNotFoundException if the class was found but its parent class has
* not been found.
*/
+ final String classname = implementation.mapper;
if (classname == null) {
mapper = null;
} else try {
@@ -445,23 +423,23 @@ public class MarshallerPool {
*
* @return a new marshaller configured for formatting OGC/ISO XML.
* @throws JAXBException if an error occurred while creating and configuring the marshaller.
+ *
+ * @see #context
+ * @see #acquireMarshaller()
*/
protected Marshaller createMarshaller() throws JAXBException {
final Marshaller marshaller = context.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
- marshaller.setProperty(Marshaller.JAXB_ENCODING, "UTF-8");
- switch (implementation) {
- case INTERNAL: {
- marshaller.setProperty("com.sun.xml.internal.bind.namespacePrefixMapper", mapper);
- marshaller.setProperty("com.sun.xml.internal.bind.indentString", INDENTATION);
- break;
- }
- case ENDORSED: {
- marshaller.setProperty("com.sun.xml.bind.namespacePrefixMapper", mapper);
- marshaller.setProperty("com.sun.xml.bind.indentString", INDENTATION);
- break;
- }
- // Do nothing for the OTHER case.
+ /*
+ * Note: we do not set the Marshaller.JAXB_ENCODING property because specification
+ * said that the default value is "UTF-8", which is what we want.
+ */
+ String key;
+ if ((key = implementation.mapperKey) != null) {
+ marshaller.setProperty(key, mapper);
+ }
+ if ((key = implementation.indentKey) != null) {
+ marshaller.setProperty(key, CharSequences.spaces(Constants.DEFAULT_INDENTATION));
}
synchronized (replacements) {
for (final AdapterReplacement adapter : replacements) {
@@ -478,6 +456,9 @@ public class MarshallerPool {
*
* @return a new unmarshaller configured for parsing OGC/ISO XML.
* @throws JAXBException if an error occurred while creating and configuring the unmarshaller.
+ *
+ * @see #context
+ * @see #acquireUnmarshaller()
*/
protected Unmarshaller createUnmarshaller() throws JAXBException {
final Unmarshaller unmarshaller = context.createUnmarshaller();
Modified: sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/xml/Pooled.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/xml/Pooled.java?rev=1780633&r1=1780632&r2=1780633&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/xml/Pooled.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/xml/Pooled.java [UTF-8] Sat Jan 28 04:48:30 2017
@@ -50,7 +50,7 @@ import org.apache.sis.internal.jaxb.Type
*
* @author Martin Desruisseaux (Geomatys)
* @since 0.3
- * @version 0.3
+ * @version 0.8
* @module
*/
abstract class Pooled {
@@ -61,21 +61,12 @@ abstract class Pooled {
private static final String[] SCHEMA_KEYS = {"gmd"};
/**
- * The prefix of property names which are provided in external (endorsed) implementation of JAXB.
- * This is slightly different than the prefix used by the implementation bundled with the JDK 6,
- * which is {@code "com.sun.xml.internal.bind"}.
- *
- * @see #convertPropertyKey(String)
- */
- static final String ENDORSED_PREFIX = "com.sun.xml.bind.";
-
- /**
* {@code true} if the JAXB implementation is the one bundled in JDK 6, or {@code false}
* if this is the external implementation provided as a JAR file in the endorsed directory.
* If {@code true}, then an additional {@code "internal"} package name needs to be inserted
* in the property keys.
*
- * @see #convertPropertyKey(String)
+ * @see Implementation#toInternal(String)
*/
private final boolean internal;
@@ -179,7 +170,7 @@ abstract class Pooled {
/**
* Creates a {@link PooledTemplate}.
*
- * @param internal {@code true} if the JAXB implementation is the one bundled in JDK 6,
+ * @param internal {@code true} if the JAXB implementation is the one bundled in JDK 6,
* or {@code false} if this is the external implementation provided as a JAR file
* in the endorsed directory.
*/
@@ -313,22 +304,6 @@ abstract class Pooled {
}
/**
- * Converts a property key from the JAXB name to the underlying implementation name.
- * This applies only to property keys in the {@code "com.sun.xml.bind"} namespace.
- *
- * @param key the JAXB property key.
- * @return the property key to use.
- */
- private String convertPropertyKey(String key) {
- if (internal && key.startsWith(ENDORSED_PREFIX)) {
- final StringBuilder buffer = new StringBuilder(key.length() + 10);
- key = buffer.append("com.sun.xml.internal.bind.")
- .append(key, ENDORSED_PREFIX.length(), key.length()).toString();
- }
- return key;
- }
-
- /**
* A method which is common to both {@code Marshaller} and {@code Unmarshaller}.
* It saves the initial state if it was not already done, but subclasses will
* need to complete the work.
@@ -421,7 +396,9 @@ abstract class Pooled {
* If we reach this point, the given name is not a SIS property. Try to handle
* it as a (un)marshaller-specific property, after saving the previous value.
*/
- name = convertPropertyKey(name);
+ if (internal) {
+ name = Implementation.toInternal(name);
+ }
if (!initialProperties.containsKey(name)) {
if (initialProperties.put(name, getStandardProperty(name)) != null) {
// Should never happen, unless on concurrent changes in a backgroung thread.
@@ -435,7 +412,7 @@ abstract class Pooled {
* A method which is common to both {@code Marshaller} and {@code Unmarshaller}.
*/
@SuppressWarnings("ReturnOfCollectionOrArrayField") // Because unmodifiable.
- public final Object getProperty(final String name) throws PropertyException {
+ public final Object getProperty(String name) throws PropertyException {
switch (name) {
case XML.LOCALE: return locale;
case XML.TIMEZONE: return timezone;
@@ -462,7 +439,10 @@ abstract class Pooled {
}
case TypeRegistration.ROOT_ADAPTERS: return (rootAdapters != null) ? rootAdapters.clone() : null;
default: {
- return getStandardProperty(convertPropertyKey(name));
+ if (internal) {
+ name = Implementation.toInternal(name);
+ }
+ return getStandardProperty(name);
}
}
}
Modified: sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/xml/PooledTemplate.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/xml/PooledTemplate.java?rev=1780633&r1=1780632&r2=1780633&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/xml/PooledTemplate.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/xml/PooledTemplate.java [UTF-8] Sat Jan 28 04:48:30 2017
@@ -44,11 +44,14 @@ final class PooledTemplate extends Poole
* {@code false} if this is the external implementation provided as a JAR file
* in the endorsed directory.
*/
- PooledTemplate(final Map<String,?> properties, final boolean internal) throws PropertyException {
- super(internal);
+ PooledTemplate(final Map<String,?> properties, final Implementation implementation) throws PropertyException {
+ super(implementation == Implementation.INTERNAL);
if (properties != null) {
for (final Map.Entry<String,?> entry : properties.entrySet()) {
- setProperty(entry.getKey(), entry.getValue());
+ final String key = entry.getKey();
+ if (implementation.filterProperty(key)) {
+ setProperty(key, entry.getValue());
+ }
}
}
}
Modified: sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/xml/XML.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/xml/XML.java?rev=1780633&r1=1780632&r2=1780633&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/xml/XML.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/xml/XML.java [UTF-8] Sat Jan 28 04:48:30 2017
@@ -102,6 +102,7 @@ public final class XML extends Static {
* {@link org.apache.sis.metadata.iso.DefaultMetadata#setLanguage(Locale) setLanguage(Locale)}
* method will have precedence over this property. This behavior is compliant with INSPIRE rules.
*
+ * @see org.apache.sis.setup.OptionKey#LOCALE
* @see Marshaller#setProperty(String, Object)
* @see org.apache.sis.metadata.iso.DefaultMetadata#setLanguage(Locale)
*/
@@ -115,6 +116,8 @@ public final class XML extends Static {
* <div class="section">Default behavior</div>
* If this property is never set, then (un)marshalling will use the
* {@linkplain TimeZone#getDefault() default timezone}.
+ *
+ * @see org.apache.sis.setup.OptionKey#TIMEZONE
*/
public static final String TIMEZONE = "org.apache.sis.xml.timezone";
Modified: sis/branches/JDK8/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Resources.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Resources.java?rev=1780633&r1=1780632&r2=1780633&view=diff
==============================================================================
--- sis/branches/JDK8/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Resources.java [UTF-8] (original)
+++ sis/branches/JDK8/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Resources.java [UTF-8] Sat Jan 28 04:48:30 2017
@@ -59,6 +59,11 @@ public final class Resources extends Ind
}
/**
+ * Can not use UCAR library for NetCDF format. Fallback on Apache SIS implementation.
+ */
+ public static final short CanNotUseUCAR = 4;
+
+ /**
* Dimension “{2}” declared by attribute “{1}” is not found in the “{0}” file.
*/
public static final short DimensionNotFound_3 = 1;
Modified: sis/branches/JDK8/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Resources.properties
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Resources.properties?rev=1780633&r1=1780632&r2=1780633&view=diff
==============================================================================
--- sis/branches/JDK8/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Resources.properties [ISO-8859-1] (original)
+++ sis/branches/JDK8/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Resources.properties [ISO-8859-1] Sat Jan 28 04:48:30 2017
@@ -19,6 +19,7 @@
# Resources in this file are for "sis-netcdf" usage only and should not be used by any other module.
# For resources shared by all modules in the Apache SIS project, see "org.apache.sis.util.resources" package.
#
+CanNotUseUCAR = Can not use UCAR library for NetCDF format. Fallback on Apache SIS implementation.
DimensionNotFound_3 = Dimension \u201c{2}\u201d declared by attribute \u201c{1}\u201d is not found in the \u201c{0}\u201d file.
UnexpectedDimensionForVariable_4 = Variable \u201c{1}\u201d in file \u201c{0}\u201d has a dimension \u201c{3}\u201d while we expected \u201c{2}\u201d.
VariableNotFound_2 = Variable \u201c{1}\u201d is not found in the \u201c{0}\u201d file.
Modified: sis/branches/JDK8/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Resources_fr.properties
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Resources_fr.properties?rev=1780633&r1=1780632&r2=1780633&view=diff
==============================================================================
--- sis/branches/JDK8/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Resources_fr.properties [ISO-8859-1] (original)
+++ sis/branches/JDK8/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Resources_fr.properties [ISO-8859-1] Sat Jan 28 04:48:30 2017
@@ -24,6 +24,7 @@
# U+202F NARROW NO-BREAK SPACE before ; ! and ?
# U+00A0 NO-BREAK SPACE before :
#
+CanNotUseUCAR = Ne peut pas utiliser la biblioth\u00e8que de l\u2019UCAR pour le format NetCDF. L\u2019impl\u00e9mentation de Apache SIS sera utilis\u00e9e \u00e0 la place.
DimensionNotFound_3 = La dimension \u00ab\u202f{2}\u202f\u00bb d\u00e9clar\u00e9e par l\u2019attribut \u00ab\u202f{1}\u202f\u00bb n\u2019a pas \u00e9t\u00e9 trouv\u00e9e dans le fichier \u00ab\u202f{0}\u202f\u00bb.
UnexpectedDimensionForVariable_4 = La variable \u00ab\u202f{1}\u202f\u00bb dans le fichier \u00ab\u202f{0}\u202f\u00bb a une dimension \u00ab\u202f{3}\u202f\u00bb alors qu\u2019on attendait \u00ab\u202f{2}\u202f\u00bb.
VariableNotFound_2 = La variable \u00ab\u202f{1}\u202f\u00bb n\u2019a pas \u00e9t\u00e9 trouv\u00e9e dans le fichier \u00ab\u202f{0}\u202f\u00bb.
Modified: sis/branches/JDK8/storage/sis-netcdf/src/main/java/org/apache/sis/storage/netcdf/NetcdfStoreProvider.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/storage/sis-netcdf/src/main/java/org/apache/sis/storage/netcdf/NetcdfStoreProvider.java?rev=1780633&r1=1780632&r2=1780633&view=diff
==============================================================================
--- sis/branches/JDK8/storage/sis-netcdf/src/main/java/org/apache/sis/storage/netcdf/NetcdfStoreProvider.java [UTF-8] (original)
+++ sis/branches/JDK8/storage/sis-netcdf/src/main/java/org/apache/sis/storage/netcdf/NetcdfStoreProvider.java [UTF-8] Sat Jan 28 04:48:30 2017
@@ -16,6 +16,8 @@
*/
package org.apache.sis.storage.netcdf;
+import java.util.logging.Level;
+import java.util.logging.LogRecord;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.nio.ByteBuffer;
@@ -25,6 +27,7 @@ import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.UndeclaredThrowableException;
import org.apache.sis.internal.netcdf.Decoder;
+import org.apache.sis.internal.netcdf.Resources;
import org.apache.sis.internal.netcdf.impl.ChannelDecoder;
import org.apache.sis.internal.netcdf.ucar.DecoderWrapper;
import org.apache.sis.internal.storage.io.ChannelDataInput;
@@ -136,7 +139,7 @@ public class NetcdfStoreProvider extends
* @throws DataStoreException if an I/O error occurred.
*/
@Override
- public ProbeResult probeContent(StorageConnector connector) throws DataStoreException {
+ public ProbeResult probeContent(final StorageConnector connector) throws DataStoreException {
int version = 0;
boolean hasVersion = false;
boolean isSupported = false;
@@ -164,7 +167,7 @@ public class NetcdfStoreProvider extends
if (!isSupported) {
final String path = connector.getStorageAs(String.class);
if (path != null) {
- ensureInitialized();
+ ensureInitialized(false);
final Method method = canOpenFromPath;
if (method != null) try {
isSupported = (Boolean) method.invoke(null, path);
@@ -275,7 +278,7 @@ public class NetcdfStoreProvider extends
private static Decoder createByReflection(final WarningListeners<?> listeners, final Object input, final boolean isUCAR)
throws IOException, DataStoreException
{
- ensureInitialized();
+ ensureInitialized(true);
/*
* Get the appropriate constructor for the isUCAR argument. This constructor will be null
* if the above code failed to load the UCAR library. Otherwise, instantiate the wrapper.
@@ -300,17 +303,23 @@ public class NetcdfStoreProvider extends
if (cause instanceof DataStoreException) throw (DataStoreException) cause;
if (cause instanceof RuntimeException) throw (RuntimeException) cause;
if (cause instanceof Error) throw (Error) cause;
- throw new UndeclaredThrowableException(cause); // Should never happen actually.
+ throw new UndeclaredThrowableException(cause); // Should never happen actually.
} catch (ReflectiveOperationException e) {
- throw new AssertionError(e); // Should never happen (shall be verified by the JUnit tests).
+ throw new AssertionError(e); // Should never happen (shall be verified by the JUnit tests).
}
}
/**
* Gets the {@link java.lang.Class} that represent the {@link ucar.nc2.NetcdfFile type}.
+ *
+ * @param open {@code true} if this method is invoked (indirectly) from the {@link #open(StorageConnector)}
+ * method, or {@code false} if invoked from the {@link #probeContent(StorageConnector)} method.
+ * This is used only for logging message.
*/
- private static void ensureInitialized() {
+ private static void ensureInitialized(final boolean open) {
if (netcdfFileClass == null) {
+ Level severity = null; // Logging level to use in case of failure.
+ Throwable cause = null; // Cause of the failure (may stay null).
synchronized (NetcdfStoreProvider.class) {
/*
* No double-check because it is not a big deal if the constructors are fetched twice.
@@ -318,29 +327,50 @@ public class NetcdfStoreProvider extends
*/
try {
netcdfFileClass = Class.forName(UCAR_CLASSNAME);
+ canOpenFromPath = netcdfFileClass.getMethod("canOpen", String.class);
+ if (canOpenFromPath.getReturnType() == Boolean.TYPE) {
+ /*
+ * At this point we found the class and method from UCAR API. Now get the Apache SIS wrapper
+ * using reflection for avoiding "hard" dependency from this provider to the UCAR library.
+ */
+ final Class<? extends Decoder> wrapper =
+ Class.forName("org.apache.sis.internal.netcdf.ucar.DecoderWrapper").asSubclass(Decoder.class);
+ final Class<?>[] parameterTypes = new Class<?>[] {WarningListeners.class, netcdfFileClass};
+ createFromUCAR = wrapper.getConstructor(parameterTypes);
+ parameterTypes[1] = String.class;
+ createFromPath = wrapper.getConstructor(parameterTypes);
+ return; // Success
+ }
} catch (ClassNotFoundException e) {
- netcdfFileClass = Void.TYPE;
- return;
- }
- try {
/*
- * UCAR API.
+ * Happen if the UCAR library is not on the classpath. Log at the configuration level without
+ * reporting the exception (for avoiding scaring logs) because this is a typical use case.
*/
- canOpenFromPath = netcdfFileClass.getMethod("canOpen", String.class);
- assert canOpenFromPath.getReturnType() == Boolean.TYPE;
+ severity = Level.CONFIG;
+ } catch (NoClassDefFoundError | ReflectiveOperationException e) {
/*
- * SIS Wrapper API.
+ * NoClassDefFoundError may happen if the UCAR class has been found but one of its dependencies
+ * is missing (for example SLF4J). Log at the warning level because the user probably wanted to
+ * use the UCAR library.
+ *
+ * ReflectiveOperationException should never happen because API compatibility shall be verified
+ * by the JUnit tests. If it happen anyway (for example because the user puts on his classpath
+ * a different version of the NetCDF library than the one we tested), report a warning.
*/
- final Class<? extends Decoder> wrapper =
- Class.forName("org.apache.sis.internal.netcdf.ucar.DecoderWrapper").asSubclass(Decoder.class);
- final Class<?>[] parameterTypes = new Class<?>[] {WarningListeners.class, netcdfFileClass};
- createFromUCAR = wrapper.getConstructor(parameterTypes);
- parameterTypes[1] = String.class;
- createFromPath = wrapper.getConstructor(parameterTypes);
- } catch (ReflectiveOperationException e) {
- throw new AssertionError(e); // Should never happen (shall be verified by the JUnit tests).
+ severity = Level.WARNING;
+ cause = e;
}
+ /*
+ * At this point we failed to use the UCAR library. Remember that failure while we are still in the
+ * synchronized block, then log a message outside the synchronized block.
+ */
+ reset();
+ netcdfFileClass = Void.TYPE;
}
+ final LogRecord record = Resources.forLocale(null).getLogRecord(severity, Resources.Keys.CanNotUseUCAR);
+ record.setThrown(cause);
+ record.setLoggerName(Modules.NETCDF);
+ Logging.log(NetcdfStoreProvider.class, open ? "open" : "probeContent", record);
}
}
Added: sis/branches/JDK8/storage/sis-xmlstore/src/main/java/org/apache/sis/internal/storage/xml/stream/FormattedWriter.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/storage/sis-xmlstore/src/main/java/org/apache/sis/internal/storage/xml/stream/FormattedWriter.java?rev=1780633&view=auto
==============================================================================
--- sis/branches/JDK8/storage/sis-xmlstore/src/main/java/org/apache/sis/internal/storage/xml/stream/FormattedWriter.java (added)
+++ sis/branches/JDK8/storage/sis-xmlstore/src/main/java/org/apache/sis/internal/storage/xml/stream/FormattedWriter.java [UTF-8] Sat Jan 28 04:48:30 2017
@@ -0,0 +1,191 @@
+/*
+ * 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.internal.storage.xml.stream;
+
+import java.util.Arrays;
+import javax.xml.stream.XMLStreamWriter;
+import javax.xml.stream.XMLStreamException;
+import org.apache.sis.internal.util.StreamWriterDelegate;
+
+
+/**
+ * Adds indentation to a XML output.
+ *
+ * <div class="note"><b>Design note:</b>
+ * an alternative approach would have been to provide {@code startIdentation()} and {@code endIndentation()}
+ * convenience methods in {@link StaxStreamWriter}, and let subclasses perform their own formatting. It would
+ * reduce the need to try to guess some formatting aspects (e.g. whether to format on a single line or not).
+ * However that approach does not integrate very well with JAXB; the {@code Marshaller.JAXB_FORMATTED_OUTPUT}
+ * property seems to be ignored when marshalling a fragment using {@code XMLStreamWriter}. Even if that property
+ * was supported, there is no standard way as of JDK8 to tell to JAXB to begin the indentation at some level
+ * (for taking in account the indentation of the elements containing the fragment to marshal with JAXB).</div>
+ *
+ * @author Martin Desruisseaux (Geomatys)
+ * @since 0.8
+ * @version 0.8
+ * @module
+ */
+final class FormattedWriter extends StreamWriterDelegate {
+ /**
+ * A predefined amount of spaces, used by {@link #indent()} for writing a greater amount of
+ * spaces in one call to {@link XMLStreamWriter#writeCharacters(char[], int, int)} methods.
+ */
+ private static final char[] SPACES = new char[12];
+ static {
+ Arrays.fill(SPACES, ' ');
+ }
+
+ /**
+ * The line separator to use.
+ */
+ private final String lineSeparator;
+
+ /**
+ * Number of spaces to add for each new indentation level.
+ */
+ private final int indentation;
+
+ /**
+ * The number of spaces to write before next XML start tags. This value is incremented or decremented
+ * by the {@link #indentation} value every time a XML start element or end element is encountered.
+ */
+ private int margin;
+
+ /**
+ * {@code true} if the last {@code writeStartElement(…)} method invocation has not yet been followed by a
+ * {@code writeEndElement(…)} method invocation. In such case, the start and end tags can be written on
+ * the same line.
+ */
+ private boolean inline;
+
+ /**
+ * Creates a new XML writer with indentation.
+ *
+ * @param out where to write the XML.
+ */
+ FormattedWriter(final XMLStreamWriter out, final int indentation) throws XMLStreamException {
+ super(out);
+ this.indentation = indentation;
+ lineSeparator = System.lineSeparator();
+ }
+
+ /**
+ * Writes a line separator, then the given amount of spaces.
+ */
+ private void indent() throws XMLStreamException {
+ int n = margin;
+ out.writeCharacters(lineSeparator);
+ final int length = SPACES.length;
+ while (n > length) {
+ out.writeCharacters(SPACES, 0, length);
+ n -= length;
+ }
+ out.writeCharacters(SPACES, 0, n);
+ }
+
+ /**
+ * Appends indentation before an empty tag.
+ */
+ private void emptyIndent() throws XMLStreamException {
+ indent();
+ inline = false;
+ }
+
+ /**
+ * Increases the indentation level and appends indentation before a start tag.
+ */
+ final void startIndent() throws XMLStreamException {
+ indent();
+ inline = true;
+ margin += indentation;
+ }
+
+ /**
+ * Appends indentation, then forwards the call verbatim.
+ */
+ @Override
+ public void writeStartElement(String localName) throws XMLStreamException {
+ startIndent();
+ out.writeStartElement(localName);
+ }
+
+ /**
+ * Appends indentation, then forwards the call verbatim.
+ */
+ @Override
+ public void writeStartElement(String namespaceURI, String localName) throws XMLStreamException {
+ startIndent();
+ out.writeStartElement(namespaceURI, localName);
+ }
+
+ /**
+ * Appends indentation, then forwards the call verbatim.
+ */
+ @Override
+ public void writeStartElement(String prefix, String localName, String namespaceURI) throws XMLStreamException {
+ startIndent();
+ out.writeStartElement(prefix, localName, namespaceURI);
+ }
+
+ /**
+ * Appends indentation, then forwards the call verbatim.
+ */
+ @Override
+ public void writeEmptyElement(String namespaceURI, String localName) throws XMLStreamException {
+ emptyIndent();
+ out.writeEmptyElement(namespaceURI, localName);
+ }
+
+ /**
+ * Appends indentation, then forwards the call verbatim.
+ */
+ @Override
+ public void writeEmptyElement(String prefix, String localName, String namespaceURI) throws XMLStreamException {
+ emptyIndent();
+ out.writeEmptyElement(prefix, localName, namespaceURI);
+ }
+
+ /**
+ * Appends indentation, then forwards the call verbatim.
+ */
+ @Override
+ public void writeEmptyElement(String localName) throws XMLStreamException {
+ emptyIndent();
+ out.writeEmptyElement(localName);
+ }
+
+ /**
+ * Appends indentation if the value is not inline, then forwards the call verbatim.
+ */
+ @Override
+ public void writeEndElement() throws XMLStreamException {
+ margin -= indentation;
+ if (!inline) indent();
+ inline = false;
+ out.writeEndElement();
+ }
+
+ /**
+ * Appends a new line after the document.
+ */
+ @Override
+ public void writeEndDocument() throws XMLStreamException {
+ out.writeCharacters(lineSeparator);
+ out.writeEndDocument();
+ out.writeCharacters(lineSeparator);
+ }
+}
Propchange: sis/branches/JDK8/storage/sis-xmlstore/src/main/java/org/apache/sis/internal/storage/xml/stream/FormattedWriter.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: sis/branches/JDK8/storage/sis-xmlstore/src/main/java/org/apache/sis/internal/storage/xml/stream/FormattedWriter.java
------------------------------------------------------------------------------
svn:mime-type = text/plain;charset=UTF-8
Modified: sis/branches/JDK8/storage/sis-xmlstore/src/main/java/org/apache/sis/internal/storage/xml/stream/StaxDataStore.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/storage/sis-xmlstore/src/main/java/org/apache/sis/internal/storage/xml/stream/StaxDataStore.java?rev=1780633&r1=1780632&r2=1780633&view=diff
==============================================================================
--- sis/branches/JDK8/storage/sis-xmlstore/src/main/java/org/apache/sis/internal/storage/xml/stream/StaxDataStore.java [UTF-8] (original)
+++ sis/branches/JDK8/storage/sis-xmlstore/src/main/java/org/apache/sis/internal/storage/xml/stream/StaxDataStore.java [UTF-8] Sat Jan 28 04:48:30 2017
@@ -43,12 +43,16 @@ import org.apache.sis.internal.storage.i
import org.apache.sis.internal.storage.io.IOUtilities;
import org.apache.sis.internal.storage.io.Markable;
import org.apache.sis.internal.util.AbstractMap;
+import org.apache.sis.internal.util.Constants;
+import org.apache.sis.internal.util.Utilities;
+import org.apache.sis.io.wkt.WKTFormat;
import org.apache.sis.storage.ConcurrentReadException;
import org.apache.sis.storage.ConcurrentWriteException;
import org.apache.sis.storage.DataStoreClosedException;
import org.apache.sis.storage.ForwardOnlyStorageException;
import org.apache.sis.storage.UnsupportedStorageException;
import org.apache.sis.util.logging.WarningListener;
+import org.apache.sis.util.Debug;
/**
@@ -171,6 +175,12 @@ public abstract class StaxDataStore exte
private final ChannelFactory channelFactory;
/**
+ * The number of spaces to use in indentations, or -1 if the XML output should not be formatted.
+ * This is ignored at reading time.
+ */
+ private final byte indentation;
+
+ /**
* Whether the {@linkplain #stream} is currently in use by a {@link StaxStreamIO}. Value can be
* one of {@link #START}, {@link #READING}, {@link #WRITING} or {@link #FINISHED} constants.
*/
@@ -191,10 +201,14 @@ public abstract class StaxDataStore exte
*/
protected StaxDataStore(final StaxDataStoreProvider provider, final StorageConnector connector) throws DataStoreException {
super(provider, connector);
+ final Integer indent;
storage = connector.getStorage();
locale = connector.getOption(OptionKey.LOCALE);
timezone = connector.getOption(OptionKey.TIMEZONE);
encoding = connector.getOption(OptionKey.ENCODING);
+ indent = connector.getOption(OptionKey.INDENTATION);
+ indentation = (indent == null) ? Constants.DEFAULT_INDENTATION
+ : (byte) Math.max(WKTFormat.SINGLE_LINE, Math.min(120, indent));
configuration = new Config();
storageToWriter = OutputType.forType(storage.getClass());
storageToReader = InputType.forType(storage.getClass());
@@ -341,6 +355,15 @@ public abstract class StaxDataStore exte
public final Class<Object> getSourceClass() {
return Object.class;
}
+
+ /**
+ * Do not format all properties for avoiding a never-ending loop.
+ */
+ @Debug
+ @Override
+ public String toString() {
+ return Utilities.toString(getClass(), "locale", locale, "timezone", timezone);
+ }
}
/**
@@ -544,7 +567,10 @@ public abstract class StaxDataStore exte
mark();
}
}
- final XMLStreamWriter writer = type.create(this, outputOrFile);
+ XMLStreamWriter writer = type.create(this, outputOrFile);
+ if (indentation >= 0) {
+ writer = new FormattedWriter(writer, indentation);
+ }
target.stream = output;
state = WRITING;
return writer;
Modified: sis/branches/JDK8/storage/sis-xmlstore/src/main/java/org/apache/sis/internal/storage/xml/stream/StaxStreamWriter.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/storage/sis-xmlstore/src/main/java/org/apache/sis/internal/storage/xml/stream/StaxStreamWriter.java?rev=1780633&r1=1780632&r2=1780633&view=diff
==============================================================================
--- sis/branches/JDK8/storage/sis-xmlstore/src/main/java/org/apache/sis/internal/storage/xml/stream/StaxStreamWriter.java [UTF-8] (original)
+++ sis/branches/JDK8/storage/sis-xmlstore/src/main/java/org/apache/sis/internal/storage/xml/stream/StaxStreamWriter.java [UTF-8] Sat Jan 28 04:48:30 2017
@@ -144,7 +144,7 @@ public abstract class StaxStreamWriter e
public void writeStartDocument() throws Exception {
final Charset encoding = owner.encoding;
if (encoding != null) {
- writer.writeStartDocument(encoding.name());
+ writer.writeStartDocument(encoding.name(), null);
} else {
writer.writeStartDocument();
}
@@ -293,6 +293,11 @@ public abstract class StaxStreamWriter e
if (m == null) {
m = getMarshallerPool().acquireMarshaller();
m.setProperty(Marshaller.JAXB_FRAGMENT, Boolean.TRUE);
+ m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.FALSE); // Formatting will be done by FormattedWriter.
+ final Charset encoding = owner.encoding;
+ if (encoding != null) {
+ m.setProperty(Marshaller.JAXB_ENCODING, encoding.name());
+ }
for (final Map.Entry<String,?> entry : ((Map<String,?>) owner.configuration).entrySet()) {
m.setProperty(entry.getKey(), entry.getValue());
}
@@ -307,7 +312,7 @@ public abstract class StaxStreamWriter e
}
marshaller = null;
m.marshal(new JAXBElement<>(qn, type, object), out);
- marshaller = m; // Allow reuse or recycling only on success.
+ marshaller = m; // Allow reuse or recycling only on success.
}
/**
|