From commits-return-8781-apmail-sis-commits-archive=sis.apache.org@sis.apache.org Sat Jan 28 04:48:35 2017 Return-Path: X-Original-To: apmail-sis-commits-archive@www.apache.org Delivered-To: apmail-sis-commits-archive@www.apache.org Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by minotaur.apache.org (Postfix) with SMTP id 30C531906A for ; Sat, 28 Jan 2017 04:48:35 +0000 (UTC) Received: (qmail 93306 invoked by uid 500); 28 Jan 2017 04:48:33 -0000 Delivered-To: apmail-sis-commits-archive@sis.apache.org Received: (qmail 93276 invoked by uid 500); 28 Jan 2017 04:48:33 -0000 Mailing-List: contact commits-help@sis.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: sis-dev@sis.apache.org Delivered-To: mailing list commits@sis.apache.org Received: (qmail 93232 invoked by uid 99); 28 Jan 2017 04:48:32 -0000 Received: from Unknown (HELO svn01-us-west.apache.org) (209.188.14.144) by apache.org (qpsmtpd/0.29) with ESMTP; Sat, 28 Jan 2017 04:48:32 +0000 Received: from svn01-us-west.apache.org (localhost [127.0.0.1]) by svn01-us-west.apache.org (ASF Mail Server at svn01-us-west.apache.org) with ESMTP id C78153A1A1A for ; Sat, 28 Jan 2017 04:48:31 +0000 (UTC) Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit Subject: svn commit: r1780633 - in /sis/branches/JDK8: core/sis-metadata/src/main/java/org/apache/sis/io/wkt/ core/sis-referencing/src/test/java/org/apache/sis/referencing/report/ core/sis-utility/src/main/java/org/apache/sis/internal/util/ core/sis-utility/src... Date: Sat, 28 Jan 2017 04:48:31 -0000 To: commits@sis.apache.org From: desruisseaux@apache.org X-Mailer: svnmailer-1.0.9 Message-Id: <20170128044831.C78153A1A1A@svn01-us-west.apache.org> 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 implements Ser *

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}.

* + * @see javax.xml.bind.Marshaller#JAXB_ENCODING + * * @since 0.4 */ public static final OptionKey ENCODING = new OptionKey<>("ENCODING", Charset.class); @@ -179,6 +181,27 @@ public class OptionKey implements Ser public static final OptionKey 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. + * + *

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.

+ * + * @see org.apache.sis.io.wkt.WKTFormat#SINGLE_LINE + * @see javax.xml.bind.Marshaller#JAXB_FORMATTED_OUTPUT + * + * @since 0.8 + */ + public static final OptionKey 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: + * + *
    + *
  • JAXB endorsed JAR uses {@code "com.sun.xml.bind.*"}
  • + *
  • JAXB bundled in JDK uses {@code "com.sun.xml.internal.bind.*"}
  • + *
+ * + * @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. + * + *

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.

+ * + * @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 properties, final boolean internal) throws PropertyException { - super(internal); + PooledTemplate(final Map properties, final Implementation implementation) throws PropertyException { + super(implementation == Implementation.INTERNAL); if (properties != null) { for (final Map.Entry 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 { *
Default behavior
* 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 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 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. + * + *
Design note: + * 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).
+ * + * @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 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 entry : ((Map) 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. } /**