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<String,String>}. 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<String,String>}.
+ * 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>
|