sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject svn commit: r1493035 [2/2] - in /sis/branches/JDK6: ./ application/sis-console/src/main/java/org/apache/sis/console/ application/sis-console/src/main/resources/org/apache/sis/console/ application/sis-console/src/test/java/org/apache/sis/console/ core/s...
Date Fri, 14 Jun 2013 11:42:33 GMT
Modified: sis/branches/JDK6/core/sis-utility/src/main/java/org/apache/sis/util/logging/MonolineFormatter.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-utility/src/main/java/org/apache/sis/util/logging/MonolineFormatter.java?rev=1493035&r1=1493034&r2=1493035&view=diff
==============================================================================
--- sis/branches/JDK6/core/sis-utility/src/main/java/org/apache/sis/util/logging/MonolineFormatter.java [UTF-8] (original)
+++ sis/branches/JDK6/core/sis-utility/src/main/java/org/apache/sis/util/logging/MonolineFormatter.java [UTF-8] Fri Jun 14 11:42:32 2013
@@ -23,6 +23,7 @@ import java.text.FieldPosition;
 import java.text.SimpleDateFormat;
 import java.util.Date;
 import java.util.TimeZone;
+import java.util.Locale;
 import java.util.Arrays;
 import java.util.TreeMap;
 import java.util.SortedMap;
@@ -32,28 +33,45 @@ import net.jcip.annotations.ThreadSafe;
 import org.apache.sis.internal.system.OS;
 import org.apache.sis.internal.util.X364;
 import org.apache.sis.io.IO;
-import org.apache.sis.util.CharSequences;
 import org.apache.sis.io.LineAppender;
+import org.apache.sis.util.CharSequences;
+import org.apache.sis.util.Configuration;
+import org.apache.sis.util.Debug;
 
 // Related to JDK7
 import org.apache.sis.internal.jdk7.JDK7;
-import org.apache.sis.internal.jdk7.Objects;
 
 
 /**
- * A formatter writing log messages on a single line. Compared to {@link SimpleFormatter}, this
- * formatter uses only one line per message instead of two. For example a message formatted by
- * {@code MonolineFormatter} looks like:
+ * A formatter writing log messages on a single line. Compared to the JDK {@link SimpleFormatter},
+ * this formatter uses only one line per message instead of two. For example messages formatted by
+ * {@code MonolineFormatter} may look like:
  *
- * {@preformat text
- *     FINE   A log message logged with level FINE from the "org.apache.sis.util" logger.
- * }
+ * <blockquote><table style="color:#FFFFFF; background:black" class="compact">
+ * <tr><td><code>00:01</code></td><td style="background:blue"><code>CONFIG</code></td>
+ *     <td><code><b>[MyApplication]</b> Read configuration from “my-application/setup.xml”.</code></td></tr>
+ * <tr><td><code>00:03</code></td><td style="background:green"><code>INFO</code></td>
+ *     <td><code><b>[DirectEpsgFactory]</b> Connected to the EPSG database version 6.9 on JavaDB 10.8.</code></td></tr>
+ * <tr><td><code>00:12</code></td><td style="background:goldenrod"><code>WARNING</code></td>
+ *     <td><code><b>[DefaultTemporalExtent]</b> This operation requires the “sis-temporal” module.</code></td></tr>
+ * </table></blockquote>
+ *
+ * By default, {@code MonolineFormatter} shows only the level and the message. One or two additional
+ * fields can be inserted between the level and the message if the {@link #setTimeFormat(String)} or
+ * {@link #setSourceFormat(String)} methods are invoked with o non-null argument. Examples:
  *
- * By default, {@code MonolineFormatter} displays only the level and the message. Additional
- * fields can be formatted if {@link #setTimeFormat(String)} or {@link #setSourceFormat(String)}
- * methods are invoked with a non-null argument. The format can also be set from the
- * {@code jre/lib/logging.properties} file. For example, user can cut and paste the following
- * properties into {@code logging.properties}:
+ * <ul>
+ *   <li>{@code setTimeFormat("HH:mm:ss")} for formatting the time like {@code 00:00:04"},
+ *       as time elapsed since the {@code MonolineFormatter} creation time.</li>
+ *   <li>{@code setSourceFormat("logger:long")} for formatting the full logger name
+ *       (e.g. {@code "org.apache.sis.storage.netcdf"}).</li>
+ *   <li>{@code setSourceFormat("class:short")} for formatting the short class name,
+ *       without package (e.g. {@code "NetcdfStore"}).</li>
+ * </ul>
+ *
+ * {@section Configuration from <code>logging.properties</code>}
+ * The format can also be set from the {@code jre/lib/logging.properties} file.
+ * For example, user can cut and paste the following properties into {@code logging.properties}:
  *
  * {@preformat text
  *     ###########################################################################
@@ -74,12 +92,12 @@ import org.apache.sis.internal.jdk7.Obje
  *     org.apache.sis.util.logging.MonolineFormatter.source = class:short
  * }
  *
- * The example below sets the {@code MonolineFormatter} for the whole system with level {@code FINE}
- * and {@code "Cp850"} page encoding (which is appropriate for some DOS console on old Windows).
+ * See {@link #setTimeFormat(String)} and {@link #setSourceFormat(String)} for more information about the
+ * above {@code time} and {@code source} properties. Encoding and logging level are configured separately,
+ * typically on the JDK {@link ConsoleHandler} like below:
  *
  * {@preformat text
- *     java.util.logging.ConsoleHandler.formatter = org.apache.sis.util.logging.MonolineFormatter
- *     java.util.logging.ConsoleHandler.encoding = Cp850
+ *     java.util.logging.ConsoleHandler.encoding = UTF-8
  *     java.util.logging.ConsoleHandler.level = FINE
  * }
  *
@@ -87,27 +105,23 @@ import org.apache.sis.internal.jdk7.Obje
  * @since   0.3 (derived from geotk-2.0)
  * @version 0.3
  * @module
+ *
+ * @see SimpleFormatter
+ * @see Handler#setFormatter(Formatter)
  */
 @ThreadSafe
 public class MonolineFormatter extends Formatter {
-    /**
-     * The string to write before any log message.
-     */
-    private static final String MARGIN = "";
-
     /** Do not format source class name.       */ private static final int NO_SOURCE    = 0;
-    /** Explicit value for 'none'.             */ private static final int NO_SOURCE_EX = 1;
-    /** Format the source logger without base. */ private static final int LOGGER_SHORT = 2;
-    /** Format the source logger only.         */ private static final int LOGGER_LONG  = 3;
-    /** Format the class name without package. */ private static final int CLASS_SHORT  = 4;
-    /** Format the fully qualified class name. */ private static final int CLASS_LONG   = 5;
+    /** Format the source logger without base. */ private static final int LOGGER_SHORT = 1;
+    /** Format the source logger only.         */ private static final int LOGGER_LONG  = 2;
+    /** Format the class name without package. */ private static final int CLASS_SHORT  = 3;
+    /** Format the fully qualified class name. */ private static final int CLASS_LONG   = 4;
 
     /**
      * The label to use in the {@code logging.properties} for setting the source format.
      */
-    private static final String[] FORMAT_LABELS = new String[6];
+    private static final String[] FORMAT_LABELS = new String[5];
     static {
-        FORMAT_LABELS[NO_SOURCE_EX] = "none";
         FORMAT_LABELS[LOGGER_SHORT] = "logger:short";
         FORMAT_LABELS[LOGGER_LONG ] = "logger:long";
         FORMAT_LABELS[ CLASS_SHORT] = "class:short";
@@ -115,15 +129,14 @@ public class MonolineFormatter extends F
     }
 
     /**
-     * Logs at level below this threshold will be printed in faint color.
-     * Logs at this level or above will be printed in normal color. This
-     * threshold is set to the default level of console handlers.
+     * Log records at level below this threshold will be printed in faint color.
+     * Logs records at this level or above will be printed in normal color.
+     * This threshold is set to the default level of console handlers.
      */
     private static final Level LEVEL_THRESHOLD = Level.INFO;
 
     /**
-     * A comparator for logging level. This comparator sorts finest levels first
-     * and severe levels last.
+     * A comparator for logging level. This comparator sorts finest levels first and severe levels last.
      */
     private static final Comparator<Level> COMPARATOR = new Comparator<Level>() {
         @Override public int compare(final Level l1, final Level l2) {
@@ -138,16 +151,33 @@ public class MonolineFormatter extends F
     };
 
     /**
-     * Minimal number of stack trace elements to print before and after the "interesting".
-     * elements. The "interesting" elements are the first stack trace elements, and the
-     * element which point to the method that produced the log record.
+     * Whether the logging level should be visible or not.
+     * We do not provide the option to hide the levels for now.
+     */
+    private static final boolean SHOW_LEVEL = true;
+
+    /**
+     * Minimal number of stack trace elements to print before and after the "interesting" elements.
+     * The "interesting" elements are the first stack trace elements, and the element which point
+     * to the method that produced the log record.
      *
-     * @see #printAbridged(Throwable, PrintWriter, String, String, String)
+     * @see #printAbridged(Throwable, Appendable, String, String, String, String)
      */
     private static final int CONTEXT_STACK_TRACE_ELEMENTS = 2;
 
     /**
+     * The string to write on the left side of the first line of every log records.
+     * The default value is an empty string. This field can not be null.
+     *
+     * @see #getHeader()
+     * @see #setHeader(String)
+     */
+    private String header = "";
+
+    /**
      * The colors to apply, or {@code null} if none.
+     *
+     * @see #colors()
      */
     private SortedMap<Level,X364> colors;
 
@@ -173,8 +203,10 @@ public class MonolineFormatter extends F
      * The minimum amount of characters to use for writing logging level before the message.
      * If the logging level is shorter, remaining characters will be padded with spaces.
      * This is used in order to align the messages.
+     *
+     * @see #levelWidth(Level)
      */
-    private int levelWidth;
+    private final int levelWidth;
 
     /**
      * Time of {@code MonolineFormatter} creation, in milliseconds elapsed since January 1, 1970.
@@ -184,7 +216,7 @@ public class MonolineFormatter extends F
     /**
      * The format to use for formatting elapsed time, or {@code null} if there is none.
      */
-    private SimpleDateFormat timeFormat = null;
+    private SimpleDateFormat timeFormat;
 
     /**
      * One of the following constants: {@link #NO_SOURCE}, {@link #LOGGER_SHORT},
@@ -193,79 +225,62 @@ public class MonolineFormatter extends F
     private int sourceFormat = NO_SOURCE;
 
     /**
-     * Buffer for formatting messages. We will reuse this buffer in order to reduce memory
-     * allocations. This is the buffer used internally by {@link #writer}.
+     * Buffer for formatting messages. We will reuse this buffer in order to reduce memory allocations.
+     * This is the buffer used internally by {@link #writer}.
      */
     private final StringBuffer buffer;
 
     /**
      * The line writer. This object transforms all {@code "\r"}, {@code "\n"} or {@code "\r\n"}
      * occurrences into a single line separator. This line separator will include space for the
-     * margin, if needed.
+     * left margin, if needed.
      */
     private final LineAppender writer;
 
     /**
+     * The printer wrapping the {@link #writer}. This is used for {@link Throwable#printStackTrace(PrintWriter)} calls.
+     * We don't use the printer for other usage in order to avoid unnecessary indirections and synchronizations.
+     */
+    private final PrintWriter printer;
+
+    /**
      * Constructs a default {@code MonolineFormatter}.
      *
      * {@section Auto-configuration from the handler}
      * Formatters are often associated to a particular handler. If this handler is known, giving it at
-     * construction time can help this formatter to configure itself. This handler is only a hint - no
-     * reference to this handler will be kept.
+     * construction time can help this formatter to configure itself. This handler is only a hint - it
+     * will not be modified, and no reference to that handler will be kept by this constructor.
      *
      * @param handler The handler to be used with this formatter, or {@code null} if unknown.
+     *
+     * @see Handler#setFormatter(Formatter)
      */
     public MonolineFormatter(final Handler handler) {
         this.startMillis = System.currentTimeMillis();
         /*
-         * Sets the "levelWidth" field to the largest label that may be displayed,
-         * according current handler setting. In the case where a larger label is
-         * to be printed, this class will adjust itself but the visual alignment
-         * with previous or next record may be broken.
-         */
-        final Level level = (handler != null) ? handler.getLevel() : null;
-loop:   for (int i=0; ; i++) {
-            final Level c;
-            switch (i) {
-                case 0: c = Level.FINEST;  break;
-                case 1: c = Level.FINER;   break;
-                case 2: c = Level.FINE;    break;
-                case 3: c = Level.CONFIG;  break;
-                case 4: c = Level.INFO;    break;
-                case 5: c = Level.WARNING; break;
-                case 6: c = Level.SEVERE;  break;
-                default: break loop;
-            }
-            if (level == null || c.intValue() >= level.intValue()) {
-                final int length = c.getLocalizedName().length();
-                if (length > levelWidth) levelWidth = length;
-            }
-        }
-        /*
-         * Creates the buffer and the printer. We will expand the tabulations with 4 characters.
-         * This apply to the stack trace formatted by Throwable.printStackTrace(PrintWriter);
-         * The default (8 characters) is a bit wide...
+         * The length of the widest standard level name that may be displayed according current handler setting.
+         * If a larger label is to be printed, this class will adjust itself but the visual alignment with
+         * previous or next record may be broken.
          */
-        final StringWriter str = new StringWriter();
-        writer = new LineAppender(str, JDK7.lineSeparator(), true);
-        buffer = str.getBuffer().append(MARGIN);
-        writer.setTabulationWidth(4);
+        levelWidth = levelWidth((handler != null) ? handler.getLevel() : null);
         /*
          * Configures this formatter according the properties, if any.
          */
         final LogManager manager = LogManager.getLogManager();
         final String classname = MonolineFormatter.class.getName();
+        header = manager.getProperty(classname + ".header");
+        if (header == null) {
+            header = "";
+        }
         try {
-            setTimeFormat(manager.getProperty(classname + ".time"));
+            timeFormat(manager.getProperty(classname + ".time"));
         } catch (IllegalArgumentException exception) {
-            // Can't use the logging framework, since we are configuring it.
-            // Display the exception name only, not the trace.
-            System.err.println(exception);
+            Logging.configurationException(MonolineFormatter.class, "<init>", exception);
         }
         try {
-            setSourceFormat(manager.getProperty(classname + ".source"));
+            sourceFormat(manager.getProperty(classname + ".source"));
         } catch (IllegalArgumentException exception) {
-            System.err.println(exception);
+            Logging.configurationException(MonolineFormatter.class, "<init>", exception);
         }
         /*
          * Applies the default set of colors only if the handler is writing to the console.
@@ -275,39 +290,104 @@ loop:   for (int i=0; ; i++) {
          * printing in an other console (e.g. using the Unix "tail" command).
          */
         if (handler instanceof ConsoleHandler && X364.isAnsiSupported()) {
-            colors = new TreeMap<Level,X364>(COMPARATOR);
-            final SortedMap<Level,X364> colors = this.colors;
-            colors.put(Level.ALL,     X364.BACKGROUND_GRAY);
-            colors.put(Level.CONFIG,  X364.BACKGROUND_BLUE);
-            colors.put(Level.INFO,    X364.BACKGROUND_GREEN);
-            colors.put(Level.WARNING, X364.BACKGROUND_YELLOW);
-            colors.put(Level.SEVERE,  X364.BACKGROUND_RED);
-            colors.put(PerformanceLevel.PERFORMANCE, X364.BACKGROUND_CYAN);
+            resetLevelColors();
         }
         faintSupported = OS.current() != OS.MAC_OS;
+        /*
+         * Creates the buffer and the printer. We will expand the tabulations with 4 characters.
+         * This apply to the stack trace formatted by Throwable.printStackTrace(PrintWriter);
+         * The default (8 characters) is a little bit too wide...
+         */
+        final StringWriter str = new StringWriter();
+        writer  = new LineAppender(str, JDK7.lineSeparator(), true);
+        buffer  = str.getBuffer().append(header);
+        printer = new PrintWriter(IO.asWriter(writer));
+        writer.setTabulationWidth(4);
+    }
+
+    /**
+     * Returns the length of the widest level name, taking in account only the standard levels
+     * equals or greater then the given threshold.
+     */
+    static int levelWidth(final Level threshold) {
+        int levelWidth = 0;
+loop:   for (int i=0; ; i++) {
+            final Level c;
+            switch (i) {
+                case 0: c = Level.SEVERE;  break;
+                case 1: c = Level.WARNING; break;
+                case 2: c = Level.INFO;    break;
+                case 3: c = Level.CONFIG;  break;
+                case 4: c = Level.FINE;    break;
+                case 5: c = Level.FINER;   break;
+                case 6: c = Level.FINEST;  break;
+                default: break loop;
+            }
+            if (threshold != null && c.intValue() < threshold.intValue()) {
+                break loop;
+            }
+            final int length = c.getLocalizedName().length();
+            if (length > levelWidth) levelWidth = length;
+        }
+        return levelWidth;
+    }
+
+    /**
+     * Returns the string to write on the left side of the first line of every log records, or {@code null} if none.
+     * This is a string to be shown just before the level.
+     *
+     * @return The string to write on the left side of the first line of every log records, or {@code null} if none.
+     */
+    public synchronized String getHeader() {
+        // All other properties in MonolineFormatter are defined in such a way
+        // that null means "none", so we do the same here for consistency.
+        return header.isEmpty() ? null : header;
+    }
+
+    /**
+     * Sets the string to write on the left side of the first line of every log records.
+     *
+     * @param header The string to write on the left side of the first line of every log records,
+     *        or {@code null} if none.
+     */
+    public synchronized void setHeader(String header) {
+        if (header == null) { // See comment in getHeader().
+            header = "";
+        }
+        this.header = header;
     }
 
     /**
-     * Returns the format for displaying elapsed time. This is the pattern specified
-     * to the last call to {@link #setTimeFormat}, or the patten specified in the
+     * Returns the format for elapsed time, or {@code null} if the time is not shown.
+     * This method returns the pattern specified by the last call to the
+     * {@link #setTimeFormat(String)} method, or the patten specified by the
      * {@code org.apache.sis.util.logging.MonolineFormatter.time} property in the
      * {@code jre/lib/logging.properties} file.
      *
-     * @return The time pattern, or {@code null} if time is not formatted.
+     * @return The time pattern, or {@code null} if elapsed time is not formatted.
      */
     public synchronized String getTimeFormat() {
         return (timeFormat != null) ? timeFormat.toPattern() : null;
     }
 
     /**
-     * Sets the format for displaying elapsed time. The pattern must matches the format specified
-     * in {@link SimpleDateFormat}, but for the time part only (not the date). For example, the
-     * pattern {@code "HH:mm:ss.SSS"} will display the elapsed time in hours, minutes, seconds
-     * and milliseconds.
+     * Sets the format for elapsed time, or hides the time field. The pattern must matches the
+     * format specified in {@link SimpleDateFormat}, but for the time part only (no date).
      *
-     * @param pattern The time patter, or {@code null} to disable time formatting.
+     * {@example The <code>"HH:mm:ss.SSS"</code> pattern will display the elapsed time in hours,
+     * minutes, seconds and milliseconds.}
+     *
+     * @param  pattern The time pattern, or {@code null} to disable time formatting.
+     * @throws IllegalArgumentException If the given pattern is invalid.
+     */
+    public synchronized void setTimeFormat(final String pattern) throws IllegalArgumentException {
+        timeFormat(pattern);
+    }
+
+    /**
+     * Implementation of {@link #setTimeFormat(String)}, to be invoked also by the constructor.
      */
-    public synchronized void setTimeFormat(final String pattern) {
+    private void timeFormat(String pattern) throws IllegalArgumentException {
         if (pattern == null) {
             timeFormat = null;
         } else if (timeFormat == null) {
@@ -319,44 +399,51 @@ loop:   for (int i=0; ; i++) {
     }
 
     /**
-     * Returns the format for displaying the source. This is the pattern specified
-     * to the last call to {@link #setSourceFormat}, or the patten specified in the
+     * Returns the format for the source, or {@code null} is the source is not shown.
+     * This method returns the source format specified by the last call to the
+     * {@link #setSourceFormat(String)} method, or the format specified by the
      * {@code org.apache.sis.util.logging.MonolineFormatter.source} property in the
      * {@code jre/lib/logging.properties} file.
      *
-     * @return The source pattern, or {@code null} if source is not formatted.
+     * @return The source format, or {@code null} if source is not formatted.
      */
     public synchronized String getSourceFormat() {
         return FORMAT_LABELS[sourceFormat];
     }
 
     /**
-     * Sets the format for displaying the source. The pattern can be {@code null}, {@code "none"},
-     * {@code "logger:short"}, {@code "class:short"}, {@code "logger:long"} or {@code "class:long"}.
-     * The 4 last choices are made of two parts separated by a {@code ':'} character:
-     *
-     * <ol>
-     *   <li>{@code "logger"} for the {@linkplain LogRecord#getLoggerName logger name}, or
-     *       {@code "class"} for the {@linkplain LogRecord#getSourceClassName source class name}.
-     *       The source class name usually contains the logger name since (by convention) logger
-     *       names are package names, but this is not mandatory neither enforced.</li>
-     *
-     *   <li>{@code "long"} for the full logger or class name, or {@code "short"} for only
-     *       the part following the last dot character.</li>
-     * </ol>
-     *
-     * The difference between a {@code null} and {@code "none"} is that {@code null}
-     * may be replaced by a default value, while {@code "none"} means that the caller
-     * explicitly requested no source.
-     *
-     * @param format The format for displaying the source.
-     */
-    public synchronized void setSourceFormat(String format) {
-        if (format != null) {
-            format = format.trim().toLowerCase();
+     * Sets the format for displaying the source, or hides the source field.
+     * The given format can be any of the following values, from more verbose to less verbose:
+     *
+     * <ul>
+     *   <li>{@code null} for hiding the source field.</li>
+     *   <li>{@code "class:long"}   for the {@linkplain LogRecord#getSourceClassName() source class name}</li>
+     *   <li>{@code "logger:long"}  for the {@linkplain LogRecord#getLoggerName() logger name}</li>
+     *   <li>{@code "class:short"}  for the source class name without the package part.</li>
+     *   <li>{@code "logger:short"} for the logger name without the package part.</li>
+     * </ul>
+     *
+     * The source class name usually contains the logger name since (by convention) logger
+     * names are package names, but this is not mandatory neither enforced.
+     *
+     * @param  format The format for displaying the source, or {@code null} if the source shall not be formatted.
+     * @throws IllegalArgumentException If the given argument is not one of the recognized format names.
+     */
+    public synchronized void setSourceFormat(final String format) throws IllegalArgumentException {
+        sourceFormat(format);
+    }
+
+    /**
+     * Implementation of {@link #setSourceFormat(String)}, to be invoked also by the constructor.
+     */
+    private void sourceFormat(String format) throws IllegalArgumentException {
+        if (format == null) {
+            sourceFormat = NO_SOURCE;
+            return;
         }
+        format = CharSequences.trimWhitespaces(format).toLowerCase(Locale.US);
         for (int i=0; i<FORMAT_LABELS.length; i++) {
-            if (Objects.equals(FORMAT_LABELS[i], format)) {
+            if (format.equals(FORMAT_LABELS[i])) {
                 sourceFormat = i;
                 return;
             }
@@ -365,35 +452,41 @@ loop:   for (int i=0; ; i++) {
     }
 
     /**
-     * Returns the color used for the given level. By default there is no color for any level.
-     * Colors should be used only if this formatter is associated to a {@link Handler} writing
-     * to an ANSI X3.64 compatible terminal.
+     * Returns the color used for the given level, or {@code null} if none.
+     * The current set of supported colors are {@code "red"}, {@code "green"}, {@code "yellow"}, {@code "blue"},
+     * {@code "magenta"}, {@code "cyan"} and {@code "gray"}. This set may be extended in any future SIS version.
      *
      * @param  level The level for which to get the color.
      * @return The color for the given level, or {@code null} if none.
-     *
-     * @todo Not yet public because X364 is not a public enumeration.
      */
-    final synchronized X364 getLevelColor(final Level level) {
-        return (colors != null) ? colors.get(level) : null;
+    public synchronized String getLevelColor(final Level level) {
+        if (colors != null) {
+            final X364 code = colors.get(level);
+            if (code != null) {
+                return code.color;
+            }
+        }
+        return null;
     }
 
     /**
-     * Sets the color to use for the given level. This method should be invoked only if this
-     * formatter is associated to a {@link Handler} writing to an ANSI X3.64 compatible terminal.
+     * Sets the color to use for the given level, or {@code null} for removing colorization.
+     * This method should be invoked only if this formatter is associated to a {@link Handler}
+     * writing to a terminal supporting <cite>ANSI escape codes</cite>
+     * (a.k.a. ECMA-48, ISO/IEC 6429 and X3.64 standards).
      *
-     * @param level The level for which to set a new color.
-     * @param color The new color, or {@code null} if none.
+     * <p>The given {@code color} argument shall be one of the values documented in the
+     * {@link #getLevelColor(Level)} method.</p>
      *
-     * @todo Not yet public because X364 is not a public enumeration.
+     * @param  level The level for which to set a new color.
+     * @param  color The case-insensitive new color, or {@code null} if none.
+     * @throws IllegalArgumentException If the given color is not one of the recognized values.
      */
-    final synchronized void setLevelColor(final Level level, final X364 color) {
+    public synchronized void setLevelColor(final Level level, final String color) throws IllegalArgumentException {
         boolean changed = false;
         if (color != null) {
-            if (colors == null) {
-                colors = new TreeMap<Level,X364>(COMPARATOR);
-            }
-            changed = (colors.put(level, color) != color);
+            final X364 code = X364.forColorName(color).background();
+            changed = (colors().put(level, code) != code);
         } else if (colors != null) {
             changed = (colors.remove(level) != null);
             if (colors.isEmpty()) {
@@ -407,22 +500,58 @@ loop:   for (int i=0; ; i++) {
     }
 
     /**
-     * Clears all colors setting. If this formatter was inserting X3.64 escape sequences
-     * for colored output, invoking this method will force the formatting of plain text.
+     * Returns the {@link #colors} map, creating it if needed.
+     */
+    private SortedMap<Level,X364> colors() {
+        if (colors == null) {
+            colors = new TreeMap<Level,X364>(COMPARATOR);
+        }
+        return colors;
+    }
+
+    /**
+     * Resets the colors to the default values. This method does not check if <cite>ANSI escape codes</cite>
+     * are supported or not - this check must be done by the caller.
+     */
+    private void resetLevelColors() {
+        final SortedMap<Level,X364> colors = colors();
+        colors.clear();
+        colors.put(Level.ALL,     X364.BACKGROUND_GRAY);
+        colors.put(Level.CONFIG,  X364.BACKGROUND_BLUE);
+        colors.put(Level.INFO,    X364.BACKGROUND_GREEN);
+        colors.put(Level.WARNING, X364.BACKGROUND_YELLOW);
+        colors.put(Level.SEVERE,  X364.BACKGROUND_RED);
+        colors.put(PerformanceLevel.PERFORMANCE, X364.BACKGROUND_CYAN);
+    }
+
+    /**
+     * Resets the colors setting to its default value.
      *
-     * @todo Not yet public because X364 is not a public enumeration.
+     * <ul>
+     *   <li>If {@code enabled} is {@code true}, then this method defines a default set of colors.</li>
+     *   <li>If {@code enabled} is {@code false}, then this method resets the formatting to plain text.</li>
+     * </ul>
+     *
+     * This method does not check if <cite>ANSI escape codes</cite> are supported or not.
+     * This check must be done by the caller.
+     *
+     * @param enabled {@code true} for defining a default set of colors, or {@code false} for removing all colors.
      */
-    final synchronized void clearLevelColors() {
-        colors = null;
-        colorLevels = null;
-        colorSequences = null;
+    public synchronized void resetLevelColors(final boolean enabled) {
+        if (enabled) {
+            resetLevelColors();
+        } else {
+            colors = null;
+            colorLevels = null;
+            colorSequences = null;
+        }
     }
 
     /**
-     * Interpolates the color for the given level. If there is no color specified explicitly for the given color,
+     * Gets the color for the given level. If there is no explicit color for the given level,
      * returns the color of the first level below the given one for which a color is specified.
      */
-    private String interpolateColor(final Level level) {
+    private String colorAt(final Level level) {
         if (colorSequences == null) {
             colorSequences = new String[colors.size()];
             colorLevels = new int[colorSequences.length];
@@ -441,6 +570,7 @@ loop:   for (int i=0; ; i++) {
 
     /**
      * Formats the given log record and return the formatted string.
+     * See the <a href="#overview">class javadoc</a> for information on the log format.
      *
      * @param  record The log record to be formatted.
      * @return A formatted log record.
@@ -451,10 +581,10 @@ loop:   for (int i=0; ; i++) {
         final boolean colors  = (this.colors != null);
         final boolean emphase = !faintSupported || (level.intValue() >= LEVEL_THRESHOLD.intValue());
         final StringBuffer buffer = this.buffer;
-        buffer.setLength(MARGIN.length());
+        buffer.setLength(header.length());
         /*
-         * Formats the time (e.g. "00:00:12.365"). The time pattern can be set either
-         * programmatically by a call to setTimeFormat(...), or in logging.properties
+         * Appends the time (e.g. "00:00:12.365"). The time pattern can be set either
+         * programmatically by a call to 'setTimeFormat(…)', or in logging.properties
          * file with the "org.apache.sis.util.logging.MonolineFormatter.time" property.
          */
         if (timeFormat != null) {
@@ -463,13 +593,13 @@ loop:   for (int i=0; ; i++) {
             buffer.append(' ');
         }
         /*
-         * Formats the level (e.g. "FINE"). We do not provide
-         * the option to turn level off for now.
+         * Appends the level (e.g. "FINE"). We do not provide the option to turn level off for now.
+         * This level will be formatted with a colorized background if ANSI escape sequences are enabled.
          */
         int margin = buffer.length();
-        if (true) {
+        if (SHOW_LEVEL) {
             if (colors) {
-                buffer.append(interpolateColor(level));
+                buffer.append(colorAt(level));
             }
             final int offset = buffer.length();
             buffer.append(level.getLocalizedName());
@@ -482,7 +612,8 @@ loop:   for (int i=0; ; i++) {
             margin++;
         }
         /*
-         * Adds the source. It may be either the source logger or the source class name.
+         * Appends the logger name or source class name, in long of short form.
+         * The name may be formatted in bold characters if ANSI escape sequences are enabled.
          */
         String source;
         switch (sourceFormat) {
@@ -512,9 +643,8 @@ loop:   for (int i=0; ; i++) {
             buffer.append(' ');
         }
         /*
-         * Now format the message. We will use a line separator made of the
-         * usual EOL ("\r", "\n", or "\r\n", which is plateform specific)
-         * following by some amout of space in order to align message body.
+         * Now prepare the LineAppender for the message. We set a line separator prefixed by some
+         * amount of spaces in order to align message body on the column after the level name.
          */
         String bodyLineSeparator = writer.getLineSeparator();
         final String lineSeparator = JDK7.lineSeparator();
@@ -527,31 +657,35 @@ loop:   for (int i=0; ; i++) {
         }
         final Throwable exception = record.getThrown();
         String message = formatMessage(record);
+        int length = 0;
         if (message != null) {
-            message = message.substring(0, trim(message));
+            length = CharSequences.skipTrailingWhitespaces(message, 0, message.length());
         }
+        /*
+         * Up to this point, we wrote directly in the StringBuilder for performance reasons.
+         * Now for the message part, we need to use the LineAppender in order to replace EOL
+         * and tabulations.
+         */
         try {
+            if (message != null) {
+                writer.append(message, 0, length);
+            }
             if (exception != null) {
-                // If there is no message, print directly the exception.
                 if (message != null) {
-                    writer.append(message).append(lineSeparator);
-                    writer.append("Caused by: ");
+                    writer.append("\nCaused by: "); // LineAppender will replace '\n' by the system EOL.
                 }
                 if (level.intValue() >= LEVEL_THRESHOLD.intValue()) {
-                    exception.printStackTrace(new PrintWriter(IO.asWriter(writer)));
+                    exception.printStackTrace(printer);
                 } else {
                     printAbridged(exception, writer, record.getLoggerName(),
-                            record.getSourceClassName(), record.getSourceMethodName(), lineSeparator);
+                            record.getSourceClassName(), record.getSourceMethodName());
                 }
-            } else {
-                // If there is no message, print "null".
-                writer.append(message);
             }
             writer.flush();
         } catch (IOException e) {
             throw new AssertionError(e);
         }
-        buffer.setLength(trim(buffer));
+        buffer.setLength(CharSequences.skipTrailingWhitespaces(buffer, 0, buffer.length()));
         if (colors && !emphase) {
             buffer.append(X364.NORMAL.sequence());
         }
@@ -570,8 +704,7 @@ loop:   for (int i=0; ; i++) {
      * @param sourceMethodName  The name of the method that emitted the log.
      */
     private static void printAbridged(Throwable exception, final Appendable writer,
-            final String loggerName, final String sourceClassName, final String sourceMethodName,
-            final String lineSeparator) throws IOException
+            final String loggerName, final String sourceClassName, final String sourceMethodName) throws IOException
     {
         StackTraceElement previous = null;
         // Arbitrary limit of 10 causes to format.
@@ -623,21 +756,21 @@ loop:   for (int i=0; ; i++) {
             /*
              * Now format the exception, then redo the loop for the cause (if any).
              */
-            writer.append(String.valueOf(exception)).append(lineSeparator);
+            writer.append(String.valueOf(exception)).append('\n'); // LineAppender will replace '\n' by the system EOL.
             for (int i=0; i<stopIndex; i++) {
                 if (i == CONTEXT_STACK_TRACE_ELEMENTS) {
                     final int numToSkip = (logProducer - 2*CONTEXT_STACK_TRACE_ELEMENTS);
                     if (numToSkip > 1) {
-                        more(writer, numToSkip, true, lineSeparator);
+                        more(writer, numToSkip, true);
                         i += numToSkip;
                     }
                 }
                 if (i == logProducer) {
-                    writer.append("  \u2192"); // Right arrow
+                    writer.append("  →");
                 }
-                writer.append("\tat ").append(String.valueOf(trace[i])).append(lineSeparator);
+                writer.append("\tat ").append(String.valueOf(trace[i])).append('\n');
             }
-            more(writer, trace.length - stopIndex, false, lineSeparator);
+            more(writer, trace.length - stopIndex, false);
             exception = exception.getCause();
             if (exception == null) break;
             writer.append("Caused by: ");
@@ -647,167 +780,135 @@ loop:   for (int i=0; ; i++) {
     /**
      * Formats the number of stack trace elements that where skipped.
      */
-    private static void more(final Appendable writer, final int numToSkip, final boolean con,
-            final String lineSeparator) throws IOException
-    {
+    private static void more(final Appendable writer, final int numToSkip, final boolean con) throws IOException {
         if (numToSkip > 0) {
             writer.append("... ").append(String.valueOf(numToSkip)).append(" more");
             if (con) {
                 writer.append(" ...");
             }
-            writer.append(lineSeparator);
+            writer.append('\n'); // LineAppender will replace '\n' by the system EOL.
         }
     }
 
     /**
-     * Returns the length of the given characters sequences without the trailing spaces
-     * or line feed.
-     */
-    private static int trim(final CharSequence message) {
-        int length = message.length();
-        while (length != 0 && Character.isWhitespace(message.charAt(length-1))) {
-            length--;
-        }
-        return length;
+     * Installs a {@code MonolineFormatter} for the root logger, or returns the existing instance if any.
+     * This method performs the following choices:
+     *
+     * <ul>
+     *   <li>If a {@link ConsoleHandler} is associated to the root logger, then:
+     *     <ul>
+     *       <li>If that handler already uses a {@code MonolineFormatter}, then the existing formatter is returned.</li>
+     *       <li>Otherwise the {@code ConsoleHandler} formatter is replaced by a new {@code MonolineFormatter} instance,
+     *           and that new instance is returned. We perform this replacement in order to avoid sending twice the same
+     *           records to the console.</li>
+     *     </ul></li>
+     *   <li>Otherwise a new {@code ConsoleHandler} using a new {@code MonolineFormatter} is created and added to the
+     *       root logger.</li>
+     * </ul>
+     *
+     * {@note The current implementation does not check for duplicated <code>ConsoleHandler</code> instances,
+     *        and does not check if any child logger has a <code>ConsoleHandler</code>.}
+     *
+     * @return The new or existing {@code MonolineFormatter}. The formatter output can be configured
+     *         using the {@link #setTimeFormat(String)} and {@link #setSourceFormat(String)} methods.
+     * @throws SecurityException If this method does not have the permission to install the formatter.
+     */
+    @Configuration
+    public static MonolineFormatter install()  throws SecurityException {
+        return install(Logging.getLogger(""), null);
     }
 
     /**
-     * Setups a {@code MonolineFormatter} for the specified logger and its children. This method
-     * searches for all instances of {@link ConsoleHandler} using the {@link SimpleFormatter}. If
-     * such instances are found, they are replaced by a single instance of {@code MonolineFormatter}.
-     * If no such {@link ConsoleHandler} are found, then a new one is created with a new
-     * {@code MonolineFormatter}.
-     *
-     * <p>In addition, this method can set the handler levels. If the level is non-null, then every
-     * {@link Handler}s using the monoline formatter may be set to the specified level. Whatever
-     * the given level is used or not depends on current configuration. The choice is based on
-     * heuristic rules that may change in any future version. Developers are encouraged to avoid
-     * non-null level except for debugging purpose, since a user trying to configure his logging
-     * properties file may find confusing to see his setting ignored.</p>
+     * Installs a {@code MonolineFormatter} for the specified logger, or returns the existing instance if any.
+     * This method performs the following steps:
+     *
+     * <ul>
+     *   <li>If a {@link ConsoleHandler} is associated to the given logger, then:
+     *     <ul>
+     *       <li>If that handler already uses a {@code MonolineFormatter}, then the existing formatter is returned.</li>
+     *       <li>Otherwise the {@code ConsoleHandler} formatter is replaced by a new {@code MonolineFormatter} instance,
+     *           and that new instance is returned. We perform this replacement in order to avoid sending twice the same
+     *           records to the console.</li>
+     *     </ul></li>
+     *   <li>Otherwise:
+     *     <ul>
+     *       <li>The {@link Logger#setUseParentHandlers(boolean)} flag is set to {@code false} for avoiding duplicated
+     *           loggings if a {@code ConsoleHandler} instance exists in the parent handlers.</li>
+     *       <li>Parent handlers that are not {@code ConsoleHandler} instances are added to the given logger in
+     *           order to preserve similar behavior as before the call to {@code setUseParentHandlers(false)}.</li>
+     *       <li>A new {@code ConsoleHandler} using a new {@code MonolineFormatter} is created and added to the
+     *           given logger.</li>
+     *     </ul></li>
+     * </ul>
+     *
+     * {@note The current implementation does not check for duplicated <code>ConsoleHandler</code> instances,
+     *        and does not check if any child logger has a <code>ConsoleHandler</code>.}
+     *
+     * {@section Specifying a log level}
+     * This method can opportunistically set the handler level. If the given level is non-null,
+     * then the {@link ConsoleHandler} using the {@code MonolineFormatter} will be set to that level.
+     * This is mostly a convenience for temporary increase of logging verbosity for debugging purpose.
+     * This functionality should not be used in production environment, since it overwrite user's level setting.
      *
      * @param  logger The base logger to apply the change on.
      * @param  level The desired level, or {@code null} if no level should be set.
-     * @return The registered {@code MonolineFormatter}, or {@code null} if the registration failed.
-     *         If non-null, the formatter output can be configured using the {@link #setTimeFormat}
-     *         and {@link #setSourceFormat} methods.
-     */
-    public static MonolineFormatter configureConsoleHandler(final Logger logger, final Level level) {
+     * @return The new or existing {@code MonolineFormatter}. The formatter output can be configured
+     *         using the {@link #setTimeFormat(String)} and {@link #setSourceFormat(String)} methods.
+     * @throws SecurityException If this method does not have the permission to install the formatter.
+     */
+    @Debug
+    @Configuration
+    public static MonolineFormatter install(final Logger logger, final Level level) throws SecurityException {
         MonolineFormatter monoline = null;
-        boolean foundConsoleHandler = false;
-        Handler[] handlers = logger.getHandlers();
-        for (int i=0; i<handlers.length; i++) {
-            final Handler handler = handlers[i];
-            if (handler.getClass() == ConsoleHandler.class) {
-                foundConsoleHandler = true;
+        for (final Handler handler : logger.getHandlers()) {
+            if (handler instanceof ConsoleHandler) {
+                /*
+                 * Get or replace the formatter of the first ConsoleHandler found, then stop the search.
+                 * We do not search for duplicated ConsoleHandler instances. If such duplicated values exist,
+                 * we presume that the user know what he is doing and will avoid messing more with his configuration.
+                 */
                 final Formatter formatter = handler.getFormatter();
                 if (formatter instanceof MonolineFormatter) {
-                    /*
-                     * A MonolineFormatter already existed. Sets the level only for the first
-                     * instance (only one instance should exists anyway) for consistency with
-                     * the fact that this method returns only one MonolineFormatter for further
-                     * configuration.
-                     */
-                    if (monoline == null) {
-                        monoline = (MonolineFormatter) formatter;
-                        setLevel(handler, level);
-                    }
-                } else if (formatter.getClass() == SimpleFormatter.class) {
-                    /*
-                     * A ConsoleHandler using the SimpleFormatter has been found. Replaces
-                     * the SimpleFormatter by MonolineFormatter, creating it if necessary.
-                     * If the handler setting fail with an exception, then we will continue
-                     * to use the old J2SE handler instead.
-                     */
-                    try {
-                        setLevel(handler, level);
-                    } catch (SecurityException exception) {
-                        unexpectedException(exception);
-                    }
-                    if (monoline == null) {
-                        monoline = new MonolineFormatter(handler);
-                    }
-                    try {
-                        handler.setFormatter(monoline);
-                    } catch (SecurityException exception) {
-                        unexpectedException(exception);
-                    }
+                    monoline = (MonolineFormatter) formatter;
+                } else {
+                    monoline = new MonolineFormatter(handler);
+                    handler.setFormatter(monoline);
                 }
+                if (level != null) {
+                    handler.setLevel(level);
+                }
+                break;
             }
         }
         /*
-         * If the logger uses parent handlers, copy them to the logger that we are initializing,
-         * because we will not use parent handlers anymore at the end of this method.
+         * If we didn't found any ConsoleHandler, then we will need to create a new one. This usually happen if
+         * the logger given in argument to this method was not the root logger. For example the user may want to
+         * configure only the "org.apache.sis" logger. But before to create the new ConsoleHandler, we will need
+         * to stop using the parent handlers because we don't want to inherit the original ConsoleHandler which
+         * is likely to exist in the root package. In order to preserve functionalities of other loggers, we copy
+         * a snapshot of all other handlers.
          */
-        for (Logger parent=logger; parent.getUseParentHandlers();) {
-            parent = parent.getParent();
-            if (parent == null) {
-                break;
-            }
-            handlers = parent.getHandlers();
-            for (int i=0; i<handlers.length; i++) {
-                Handler handler = handlers[i];
-                if (handler.getClass() == ConsoleHandler.class) {
-                    if (!foundConsoleHandler) {
-                        // We have already set a ConsoleHandler and we don't want a second one.
-                        continue;
-                    }
-                    foundConsoleHandler = true;
-                    final Formatter formatter = handler.getFormatter();
-                    if (formatter.getClass() == SimpleFormatter.class) {
-                        monoline = addHandler(logger, level);
-                        continue;
+        if (monoline == null) {
+            logger.setUseParentHandlers(false);
+            for (Logger parent=logger; parent.getUseParentHandlers();) {
+                parent = parent.getParent();
+                if (parent == null) {
+                    break;
+                }
+                for (final Handler handler : parent.getHandlers()) {
+                    if (!(handler instanceof ConsoleHandler)) {
+                        logger.addHandler(handler);
                     }
                 }
-                logger.addHandler(handler);
             }
-        }
-        logger.setUseParentHandlers(false);
-        if (!foundConsoleHandler) {
-            monoline = addHandler(logger, level);
-        }
-        return monoline;
-    }
-
-    /**
-     * Adds to the specified logger a {@link Handler} using a {@code MonolineFormatter}
-     * set at the specified level. The formatter is returned for convenience.
-     */
-    private static MonolineFormatter addHandler(final Logger logger, final Level level) {
-        MonolineFormatter monoline = null;
-        try {
             final Handler handler = new ConsoleHandler();
+            if (level != null) {
+                handler.setLevel(level); // Shall be before MonolineFormatter creation.
+            }
             monoline = new MonolineFormatter(handler);
             handler.setFormatter(monoline);
-            setLevel(handler, level);
             logger.addHandler(handler);
-        } catch (SecurityException exception) {
-            unexpectedException(exception);
-            /*
-             * Returns without any change to the J2SE configuration. It will not
-             * prevent to program to work; just produces different logging outputs.
-             */
         }
         return monoline;
     }
-
-    /**
-     * Sets the level of the given handler. This method tries to find a balance between user's
-     * setting and desired level using heuristic rules that may change in any future version.
-     */
-    private static void setLevel(final Handler handler, final Level level) {
-        if (level != null) {
-            final int desired = level.intValue();
-            final int current = handler.getLevel().intValue();
-            if (desired < LEVEL_THRESHOLD.intValue() ? desired < current : desired > current) {
-                handler.setLevel(level);
-            }
-        }
-    }
-
-    /**
-     * Invoked when an error occurs during the initialization.
-     */
-    private static void unexpectedException(final Exception exception) {
-        Logging.unexpectedException(MonolineFormatter.class, "configureConsoleHandler", exception);
-    }
 }

Modified: sis/branches/JDK6/core/sis-utility/src/main/java/org/apache/sis/util/logging/PerformanceLevel.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-utility/src/main/java/org/apache/sis/util/logging/PerformanceLevel.java?rev=1493035&r1=1493034&r2=1493035&view=diff
==============================================================================
--- sis/branches/JDK6/core/sis-utility/src/main/java/org/apache/sis/util/logging/PerformanceLevel.java [UTF-8] (original)
+++ sis/branches/JDK6/core/sis-utility/src/main/java/org/apache/sis/util/logging/PerformanceLevel.java [UTF-8] Fri Jun 14 11:42:32 2013
@@ -44,7 +44,7 @@ import static org.apache.sis.util.Argume
  *   <li>The {@link Logger#setLevel(Level)} can be invoked, together with
  *       {@link java.util.logging.Handler#setLevel(Level)} on all relevant logging targets
  *       (console or file, <i>etc.</i>).</li>
- *   <li>The {@link MonolineFormatter#configureConsoleHandler(Logger, Level)} convenience
+ *   <li>The {@link MonolineFormatter#install(Logger, Level)} convenience
  *       method can be invoked.</li>
  * </ul>
  *

Modified: sis/branches/JDK6/core/sis-utility/src/main/java/org/apache/sis/xml/MarshallerPool.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-utility/src/main/java/org/apache/sis/xml/MarshallerPool.java?rev=1493035&r1=1493034&r2=1493035&view=diff
==============================================================================
--- sis/branches/JDK6/core/sis-utility/src/main/java/org/apache/sis/xml/MarshallerPool.java [UTF-8] (original)
+++ sis/branches/JDK6/core/sis-utility/src/main/java/org/apache/sis/xml/MarshallerPool.java [UTF-8] Fri Jun 14 11:42:32 2013
@@ -18,7 +18,6 @@ package org.apache.sis.xml;
 
 import java.util.Map;
 import java.util.Deque;
-import java.util.Collections;
 import java.util.concurrent.LinkedBlockingDeque;
 import java.util.concurrent.atomic.AtomicBoolean;
 import javax.xml.bind.JAXBContext;
@@ -30,6 +29,8 @@ import org.apache.sis.util.logging.Loggi
 import org.apache.sis.internal.util.DelayedExecutor;
 import org.apache.sis.internal.util.DelayedRunnable;
 import org.apache.sis.internal.jaxb.AdapterReplacement;
+import org.apache.sis.internal.jaxb.TypeRegistration;
+import org.apache.sis.util.ArgumentChecks;
 
 
 /**
@@ -54,6 +55,7 @@ import org.apache.sis.internal.jaxb.Adap
  * @module
  *
  * @see XML
+ * @see <a href="http://jaxb.java.net/guide/Performance_and_thread_safety.html">JAXB Performance and thread-safety</a>
  */
 @ThreadSafe
 public class MarshallerPool {
@@ -70,12 +72,6 @@ public class MarshallerPool {
     private static final byte INTERNAL = 0, ENDORSED = 1, OTHER = 2;
 
     /**
-     * The key to be used in the map given to the constructors for specifying the default namespace.
-     * An example of value for this key is {@code "http://www.isotc211.org/2005/gmd"}.
-     */
-    public static final String DEFAULT_NAMESPACE_KEY = "org.apache.sis.xml.defaultNamespace";
-
-    /**
      * The JAXB context to use for creating marshaller and unmarshaller.
      */
     private final JAXBContext context;
@@ -127,68 +123,42 @@ public class MarshallerPool {
     private final AtomicBoolean isRemovalScheduled;
 
     /**
-     * Creates a new factory for the given class to be bound, with a default empty namespace.
-     *
-     * @param  classesToBeBound The classes to be bound, for example {@code DefaultMetadata.class}.
-     * @throws JAXBException    If the JAXB context can not be created.
-     */
-    public MarshallerPool(final Class<?>... classesToBeBound) throws JAXBException {
-        this(Collections.<String,String>emptyMap(), classesToBeBound);
-    }
-
-    /**
-     * Creates a new factory for the given class to be bound. The keys in the {@code properties} map
-     * shall be one or many of the constants defined in this class like {@link #DEFAULT_NAMESPACE_KEY}.
+     * Creates a new factory using the SIS default {@code JAXBContext} instance.
+     * The keys in the {@code properties} map can be one or many of following constants:
      *
-     * @param  properties       The set of properties to be given to the pool.
-     * @param  classesToBeBound The classes to be bound, for example {@code DefaultMetadata.class}.
-     * @throws JAXBException    If the JAXB context can not be created.
-     */
-    public MarshallerPool(final Map<String,String> properties, final Class<?>... classesToBeBound) throws JAXBException {
-        this(properties, JAXBContext.newInstance(classesToBeBound));
-    }
-
-    /**
-     * Creates a new factory for the given packages, with a default empty namespace.
-     * The separator character for the packages is the colon. Example:
-     *
-     * {@preformat text
-     *     "org.apache.sis.metadata.iso:org.apache.sis.metadata.iso.citation"
-     * }
+     * <ul>
+     *   <li>{@link XML#DEFAULT_NAMESPACE} for specifying the default namespace of the XML document to write.</li>
+     * </ul>
      *
-     * @param  packages      The colon-separated list of packages in which JAXB will search for annotated classes.
+     * @param  properties    The set of properties to be given to the pool, or {@code null} if none.
      * @throws JAXBException If the JAXB context can not be created.
      */
-    public MarshallerPool(final String packages) throws JAXBException {
-        this(Collections.<String,String>emptyMap(), packages);
+    public MarshallerPool(final Map<String,String> properties) throws JAXBException {
+        this(TypeRegistration.getSharedContext(), properties);
     }
 
     /**
-     * Creates a new factory for the given packages. The separator character for the packages is the
-     * colon. The keys in the {@code properties} map shall be one or many of the constants defined
-     * in this class like {@link #DEFAULT_NAMESPACE_KEY}.
+     * Creates a new factory using the given JAXB context.
+     * The keys in the {@code properties} map can be one or many of following constants:
      *
-     * @param  properties    The set of properties to be given to the pool.
-     * @param  packages      The colon-separated list of packages in which JAXB will search for annotated classes.
-     * @throws JAXBException If the JAXB context can not be created.
-     */
-    public MarshallerPool(final Map<String,String> properties, final String packages) throws JAXBException {
-        this(properties, JAXBContext.newInstance(packages));
-    }
-
-    /**
-     * Creates a new factory for the given packages.
+     * <ul>
+     *   <li>{@link XML#DEFAULT_NAMESPACE} for specifying the default namespace of the XML document to write.</li>
+     * </ul>
      *
-     * @param  properties    The set of properties to be given to the pool.
      * @param  context       The JAXB context.
-     * @throws JAXBException If the OGC namespace prefix mapper can not be created.
+     * @param  properties    The set of properties to be given to the pool, or {@code null} if none.
+     * @throws JAXBException If the marshaller pool can not be created.
      */
     @SuppressWarnings({"unchecked", "rawtypes"}) // Generic array creation
-    private MarshallerPool(final Map<String,String> properties, final JAXBContext context) throws JAXBException {
+    public MarshallerPool(final JAXBContext context, final Map<String,String> properties) throws JAXBException {
+        ArgumentChecks.ensureNonNull("context", context);
         this.context = context;
-        String rootNamespace = properties.get(DEFAULT_NAMESPACE_KEY);
-        if (rootNamespace == null) {
-            rootNamespace = "";
+        String rootNamespace = "";
+        if (properties != null) {
+            rootNamespace = properties.get(XML.DEFAULT_NAMESPACE);
+            if (rootNamespace == null) {
+                rootNamespace = "";
+            }
         }
         /*
          * Detects if we are using the endorsed JAXB implementation (i.e. the one provided in
@@ -370,11 +340,21 @@ public class MarshallerPool {
 
     /**
      * Declares a marshaller as available for reuse.
-     * The caller should not use anymore the given marshaller after this method call.
+     * The caller should not use anymore the given marshaller after this method call,
+     * since the marshaller may be re-used by another thread at any time after recycle.
+     *
+     * {@section Cautions}
+     * <ul>
+     *   <li>Do not invoke this method if the marshaller threw an exception, since the
+     *       marshaller may be in an invalid state. In particular, this method should not
+     *       be invoked in a {@code finally} block.</li>
+     *   <li>Do not invoke this method twice for the same marshaller, unless the marshaller
+     *       has been obtained by a new call to {@link #acquireMarshaller()}.
+     *       In case of doubt, it is better to not recycle the marshaller at all.</li>
+     * </ul>
      *
-     * <p>Do not invoke this method if the marshaller threw an exception, since the
-     * marshaller may be in an invalid state. In particular, this method should not
-     * be invoked in a {@code finally} block.</p>
+     * Note that this method does not close any output stream.
+     * Closing the marshaller stream is caller's or JAXB responsibility.
      *
      * @param marshaller The marshaller to return to the pool.
      */
@@ -384,11 +364,21 @@ public class MarshallerPool {
 
     /**
      * Declares a unmarshaller as available for reuse.
-     * The caller should not use anymore the given unmarshaller after this method call.
+     * The caller should not use anymore the given unmarshaller after this method call,
+     * since the unmarshaller may be re-used by another thread at any time after recycle.
+     *
+     * {@section Cautions}
+     * <ul>
+     *   <li>Do not invoke this method if the unmarshaller threw an exception, since the
+     *       unmarshaller may be in an invalid state. In particular, this method should not
+     *       be invoked in a {@code finally} block.</li>
+     *   <li>Do not invoke this method twice for the same unmarshaller, unless the unmarshaller
+     *       has been obtained by a new call to {@link #acquireUnmarshaller()}.
+     *       In case of doubt, it is better to not recycle the unmarshaller at all.</li>
+     * </ul>
      *
-     * <p>Do not invoke this method if the marshaller threw an exception, since the
-     * marshaller may be in an invalid state. In particular, this method should not
-     * be invoked in a {@code finally} block.</p>
+     * Note that this method does not close any input stream.
+     * Closing the unmarshaller stream is caller's or JAXB responsibility.
      *
      * @param unmarshaller The unmarshaller to return to the pool.
      */

Modified: sis/branches/JDK6/core/sis-utility/src/main/java/org/apache/sis/xml/XML.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-utility/src/main/java/org/apache/sis/xml/XML.java?rev=1493035&r1=1493034&r2=1493035&view=diff
==============================================================================
--- sis/branches/JDK6/core/sis-utility/src/main/java/org/apache/sis/xml/XML.java [UTF-8] (original)
+++ sis/branches/JDK6/core/sis-utility/src/main/java/org/apache/sis/xml/XML.java [UTF-8] Fri Jun 14 11:42:32 2013
@@ -39,6 +39,7 @@ import org.apache.sis.internal.jaxb.Type
  *   <li>{@link #LOCALE} for specifying the locale to use for international strings and code lists.</li>
  *   <li>{@link #TIMEZONE} for specifying the timezone to use for dates and times.</li>
  *   <li>{@link #SCHEMAS} for specifying the root URL of metadata schemas to use.</li>
+ *   <li>{@link #DEFAULT_NAMESPACE} for specifying the default namespace of the XML document to write.</li>
  *   <li>{@link #RESOLVER} for replacing {@code xlink} or {@code uuidref} attributes by the actual object to use.</li>
  *   <li>{@link #CONVERTER} for controlling the conversion of URL, UUID, Units or similar objects.</li>
  *   <li>{@link #STRING_SUBSTITUTES} for specifying which code lists to replace by simpler {@code <gco:CharacterString>} elements.</li>
@@ -52,7 +53,7 @@ import org.apache.sis.internal.jaxb.Type
  */
 public final class XML extends Static {
     /**
-     * Allows client code to specify the locale to use for marshalling
+     * Specifies the locale to use for marshalling
      * {@link org.opengis.util.InternationalString} and {@link org.opengis.util.CodeList}
      * instances. The value for this property shall be an instance of {@link Locale}.
      *
@@ -82,7 +83,7 @@ public final class XML extends Static {
     public static final String LOCALE = "org.apache.sis.xml.locale";
 
     /**
-     * The timezone to use for marshalling dates and times.
+     * Specifies the timezone to use for marshalling dates and times.
      *
      * {@section Default behavior}
      * If this property is never set, then (un)marshalling will use the
@@ -91,9 +92,9 @@ public final class XML extends Static {
     public static final String TIMEZONE = "org.apache.sis.xml.timezone";
 
     /**
-     * Allows client code to specify the root URL of schemas. The value for this property shall
-     * be an instance of {@link java.util.Map Map&lt;String,String&gt;}. This property controls
-     * the URL to be used when marshalling the following elements:
+     * Specifies the root URL of schemas. The value for this property shall
+     * be an instance of {@link java.util.Map Map&lt;String,String&gt;}.
+     * This property controls the URL to be used when marshalling the following elements:
      *
      * <ul>
      *   <li>The value of the {@code codeList} attribute when marshalling subclasses of
@@ -120,6 +121,17 @@ public final class XML extends Static {
     // If more keys are documented, update the Pooled.SCHEMAS_KEY array.
 
     /**
+     * Specifies the default namespace of the XML document to write.
+     * An example of value for this key is {@code "http://www.isotc211.org/2005/gmd"}.
+     *
+     * {@section Current limitation}
+     * In current SIS implementation, this property is honored only by the {@link MarshallerPool} constructors.
+     * Specifying this property to {@link javax.xml.bind.Marshaller#setProperty(String, Object)} is too late.
+     * This limitation may be fixed in a future SIS version.
+     */
+    public static final String DEFAULT_NAMESPACE = "org.apache.sis.xml.defaultNamespace";
+
+    /**
      * Specifies the GML version to be marshalled or unmarshalled. The GML version may affect the
      * set of XML elements to be marshalled. Newer versions typically have more elements, but not
      * always. For example in {@code gml:VerticalDatum}, the {@code gml:verticalDatumType} property
@@ -156,9 +168,8 @@ public final class XML extends Static {
     public static final String RESOLVER = "org.apache.sis.xml.resolver";
 
     /**
-     * Allows client code to control the behavior of the (un)marshalling process when an element
-     * can not be processed, or alter the element values. The value for this property shall be an
-     * instance of {@link ValueConverter}.
+     * Control the behaviors of the (un)marshalling process when an element can not be processed,
+     * or alter the element values. The value for this property shall be an instance of {@link ValueConverter}.
      *
      * <p>If an element in a XML document can not be parsed (for example if a {@linkplain java.net.URL}
      * string is not valid), the default behavior is to throw an exception which cause the
@@ -279,7 +290,7 @@ public final class XML extends Static {
             synchronized (XML.class) {
                 pool = POOL; // Double-check idiom: see javadoc.
                 if (pool == null) {
-                    POOL = pool = new MarshallerPool(TypeRegistration.defaultClassesToBeBound());
+                    POOL = pool = new MarshallerPool(null);
                 }
             }
         }

Modified: sis/branches/JDK6/core/sis-utility/src/test/java/org/apache/sis/internal/util/X364Test.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-utility/src/test/java/org/apache/sis/internal/util/X364Test.java?rev=1493035&r1=1493034&r2=1493035&view=diff
==============================================================================
--- sis/branches/JDK6/core/sis-utility/src/test/java/org/apache/sis/internal/util/X364Test.java [UTF-8] (original)
+++ sis/branches/JDK6/core/sis-utility/src/test/java/org/apache/sis/internal/util/X364Test.java [UTF-8] Fri Jun 14 11:42:32 2013
@@ -36,6 +36,18 @@ import static org.apache.sis.internal.ut
 @DependsOn(org.apache.sis.util.CharSequencesTest.class)
 public final strictfp class X364Test extends TestCase {
     /**
+     * Tests {@link X364#forColorName(String)}.
+     */
+    @Test
+    public void testForColorName() {
+        for (final X364 value : X364.values()) {
+            if (value.color != null) {
+                assertSame(value.color, value.foreground(), X364.forColorName(value.color));
+            }
+        }
+    }
+
+    /**
      * Tests the {@link X364#plain(String)} method.
      */
     @Test

Modified: sis/branches/JDK6/core/sis-utility/src/test/java/org/apache/sis/test/suite/UtilityTestSuite.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-utility/src/test/java/org/apache/sis/test/suite/UtilityTestSuite.java?rev=1493035&r1=1493034&r2=1493035&view=diff
==============================================================================
--- sis/branches/JDK6/core/sis-utility/src/test/java/org/apache/sis/test/suite/UtilityTestSuite.java [UTF-8] (original)
+++ sis/branches/JDK6/core/sis-utility/src/test/java/org/apache/sis/test/suite/UtilityTestSuite.java [UTF-8] Fri Jun 14 11:42:32 2013
@@ -50,6 +50,7 @@ import org.junit.BeforeClass;
     org.apache.sis.util.resources.IndexedResourceBundleTest.class,
     org.apache.sis.util.logging.PerformanceLevelTest.class,
     org.apache.sis.util.logging.WarningListenersTest.class,
+    org.apache.sis.util.logging.MonolineFormatterTest.class,
     org.apache.sis.math.MathFunctionsTest.class,
     org.apache.sis.math.StatisticsTest.class,
     org.apache.sis.math.StatisticsFormatTest.class,

Copied: sis/branches/JDK6/core/sis-utility/src/test/java/org/apache/sis/util/logging/MonolineFormatterTest.java (from r1493029, sis/branches/JDK7/core/sis-utility/src/test/java/org/apache/sis/util/logging/MonolineFormatterTest.java)
URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-utility/src/test/java/org/apache/sis/util/logging/MonolineFormatterTest.java?p2=sis/branches/JDK6/core/sis-utility/src/test/java/org/apache/sis/util/logging/MonolineFormatterTest.java&p1=sis/branches/JDK7/core/sis-utility/src/test/java/org/apache/sis/util/logging/MonolineFormatterTest.java&r1=1493029&r2=1493035&rev=1493035&view=diff
==============================================================================
--- sis/branches/JDK7/core/sis-utility/src/test/java/org/apache/sis/util/logging/MonolineFormatterTest.java [UTF-8] (original)
+++ sis/branches/JDK6/core/sis-utility/src/test/java/org/apache/sis/util/logging/MonolineFormatterTest.java [UTF-8] Fri Jun 14 11:42:32 2013
@@ -58,9 +58,9 @@ public final strictfp class MonolineForm
      * The given string shall use tabulation before each line of the message.
      */
     private static String localize(final Level level, final String expected) {
-        final String name = level.getName();
-        return expected.replace(name, level.getLocalizedName())
-                .replace("\t", CharSequences.spaces(MonolineFormatter.levelWidth(null) - name.length()));
+        final String label = level.getLocalizedName();
+        return expected.replace(level.getName(), label)
+                .replace("\t", CharSequences.spaces(MonolineFormatter.levelWidth(null) - label.length()));
     }
 
     /**

Modified: sis/branches/JDK6/core/sis-utility/src/test/java/org/apache/sis/xml/MarshallerPoolTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-utility/src/test/java/org/apache/sis/xml/MarshallerPoolTest.java?rev=1493035&r1=1493034&r2=1493035&view=diff
==============================================================================
--- sis/branches/JDK6/core/sis-utility/src/test/java/org/apache/sis/xml/MarshallerPoolTest.java [UTF-8] (original)
+++ sis/branches/JDK6/core/sis-utility/src/test/java/org/apache/sis/xml/MarshallerPoolTest.java [UTF-8] Fri Jun 14 11:42:32 2013
@@ -17,6 +17,7 @@
 package org.apache.sis.xml;
 
 import javax.xml.bind.Marshaller;
+import javax.xml.bind.JAXBContext;
 import javax.xml.bind.JAXBException;
 import org.apache.sis.test.DependsOn;
 import org.apache.sis.test.TestCase;
@@ -44,7 +45,7 @@ public final strictfp class MarshallerPo
      */
     @Test
     public void testAcquireRelease() throws JAXBException {
-        final MarshallerPool pool = new MarshallerPool(new Class<?>[0]);
+        final MarshallerPool pool = new MarshallerPool(JAXBContext.newInstance(new Class<?>[0]), null);
         final Marshaller marshaller = pool.acquireMarshaller();
         assertNotNull(marshaller);
         /*

Modified: sis/branches/JDK6/ide-project/NetBeans/build.xml
URL: http://svn.apache.org/viewvc/sis/branches/JDK6/ide-project/NetBeans/build.xml?rev=1493035&r1=1493034&r2=1493035&view=diff
==============================================================================
--- sis/branches/JDK6/ide-project/NetBeans/build.xml (original)
+++ sis/branches/JDK6/ide-project/NetBeans/build.xml Fri Jun 14 11:42:32 2013
@@ -23,6 +23,9 @@
       <fileset dir="${project.root}/core/sis-utility/src/main/resources">
         <include name="META-INF/services/*"/>
       </fileset>
+      <fileset dir="${project.root}/core/sis-metadata/src/main/resources">
+        <include name="META-INF/services/*"/>
+      </fileset>
     </copy>
   </target>
 </project>



Mime
View raw message