001: /*
002: * GeoTools - OpenSource mapping toolkit
003: * http://geotools.org
004: * (C) 2007, Geotools Project Managment Committee (PMC)
005: * (C) 2007, Geomatys
006: *
007: * This library is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU Lesser General Public
009: * License as published by the Free Software Foundation; either
010: * version 2.1 of the License, or (at your option) any later version.
011: *
012: * This library is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015: * Lesser General Public License for more details.
016: */
017: package org.geotools.util.logging;
018:
019: import java.text.AttributedCharacterIterator;
020: import java.text.FieldPosition;
021: import java.text.Format;
022: import java.text.ParseException;
023: import java.text.ParsePosition;
024: import java.util.Locale;
025: import java.util.logging.Level;
026: import java.util.logging.LogRecord;
027: import java.util.logging.Logger;
028:
029: import org.geotools.resources.Utilities;
030: import org.geotools.resources.i18n.ErrorKeys;
031: import org.geotools.resources.i18n.Errors;
032:
033: /**
034: * Wraps a {@link Format} object in order to either parse fully a string, or log a warning.
035: * This class provides a {@link #parse} method which performs the following tasks:
036: * <p>
037: * <ul>
038: * <li>Checks if the string was fully parsed and log a warning if it was not. This is
039: * different than the default {@link #parseObject(String)} behavior which check only
040: * if the <em>begining</em> of the string was parsed and ignore any remaining characters.</li>
041: * <li>Ensures that the parsed object is of some specific class specified at construction time.</li>
042: * <li>If the string can't be fully parsed or is not of the expected class, logs a warning.</li>
043: * </ul>
044: *
045: * @since 2.4
046: * @source $URL: http://svn.geotools.org/geotools/tags/2.4.1/modules/library/metadata/src/main/java/org/geotools/util/logging/LoggedFormat.java $
047: * @version $Id: LoggedFormat.java 27862 2007-11-12 19:51:19Z desruisseaux $
048: * @author Martin Desruisseaux
049: */
050: public class LoggedFormat extends Format {
051: /**
052: * For cross-version compatibility.
053: */
054: private static final long serialVersionUID = 4578880360344271325L;
055:
056: /**
057: * The wrapped format.
058: */
059: private final Format format;
060:
061: /**
062: * The expected type for the parsed values.
063: */
064: private final Class type;
065:
066: /**
067: * The logger where to log warnings, or {@code null} if none.
068: *
069: * @see #setLogger
070: */
071: private String logger;
072:
073: /**
074: * The class to declare in as the warning emitter, or {@code null} if none.
075: *
076: * @see #setCaller
077: */
078: private String className;
079:
080: /**
081: * The method to declare in as the warning emitter, or {@code null} if none.
082: *
083: * @see #setCaller
084: */
085: private String methodName;
086:
087: /**
088: * Creates a new format wrapping the specified one.
089: *
090: * @param format The format to use for parsing and formatting.
091: * @param type The expected type of parsed values.
092: */
093: protected LoggedFormat(final Format format, final Class type) {
094: this .format = format;
095: this .type = type;
096: }
097:
098: /**
099: * Creates a new format wrapping the specified one.
100: *
101: * @param format The format to use for parsing and formatting.
102: * @param type The expected type of parsed values.
103: */
104: public static LoggedFormat getInstance(final Format format,
105: final Class type) {
106: return new LoggedFormat(format, type);
107: }
108:
109: /**
110: * Sets the logger where to send the warnings eventually emitted by the {@link #parse} method.
111: *
112: * @param logger The logger where to log warnings, or {@code null} if none.
113: */
114: public void setLogger(final String logger) {
115: this .logger = logger;
116: }
117:
118: /**
119: * Sets the {@linkplain LogRecord#setSourceClassName source class name} and
120: * {@linkplain LogRecord#setSourceMethodName source method name} for the warnings
121: * eventually emitted by the {@link #parse} method.
122: *
123: * @param caller The class to declare as the warning emitter, or {@code null} if none.
124: * @param method The method to declare as the warning emitter, or {@code null} if none.
125: *
126: * @todo Use Class.getSimpleName() or something like that when we will be allowed to compile
127: * for J2SE 1.5.
128: */
129: public void setCaller(final Class caller, final String method) {
130: this .className = (caller != null) ? caller.getName() : null;
131: this .methodName = method;
132: }
133:
134: /**
135: * Parses the specified string. If the string can't be parsed, then this method returns
136: * {@code null}. If it can be parsed at least partially and is of the kind specified at
137: * construction time, then it is returned. If the string has not been fully parsed, then
138: * a {@linkplain LogRecord log record} is prepared and logged.
139: *
140: * @param text The text to parse, or {@code null}.
141: * @return The parsed object, or {@code null} if {@code text} was null or can't be parsed.
142: */
143: public Object parse(String text) {
144: if (text == null || (text = text.trim()).length() == 0) {
145: return null;
146: }
147: final ParsePosition position = new ParsePosition(0);
148: final Object value = parseObject(text, position);
149: int index = position.getIndex();
150: final int error = position.getErrorIndex();
151: if (error >= 0 && error < index) {
152: index = error;
153: }
154: if (index < text.length()) {
155: logWarning(ErrorKeys.UNPARSABLE_STRING_$2, text, text
156: .substring(index));
157: } else if (value != null && !type.isInstance(value)) {
158: logWarning(ErrorKeys.ILLEGAL_CLASS_$2, value.getClass(),
159: type);
160: return null;
161: }
162: return value;
163: }
164:
165: /**
166: * Parses text from a string to produce an object. This method delegates the work to the
167: * {@linkplain Format format} specified at construction time. This method to not perform
168: * any logging.
169: *
170: * @param text The text to parse.
171: * @return An object parsed from the string.
172: * @throws ParseException if parsing failed.
173: */
174: public Object parseObject(final String text) throws ParseException {
175: return format.parseObject(text);
176: }
177:
178: /**
179: * Parses text from a string to produce an object. This method delegates the work to the
180: * {@linkplain Format format} specified at construction time. This method to not perform
181: * any logging.
182: *
183: * @param text The text to parse.
184: * @param position Index and error index information.
185: * @return An object parsed from the string, or {@code null} in case of error.
186: */
187: public Object parseObject(final String text,
188: final ParsePosition position) {
189: return format.parseObject(text, position);
190: }
191:
192: /**
193: * Formats the specified object. This method delegates the work to the
194: * {@linkplain Format format} specified at construction time.
195: *
196: * @param value The object to format.
197: * @param toAppendTo The buffer where the text is to be appended.
198: * @param position Identifies a field in the formatted text.
199: * @return The string buffer passed in with formatted text appended.
200: */
201: public StringBuffer format(final Object value,
202: final StringBuffer toAppendTo, final FieldPosition position) {
203: return format.format(value, toAppendTo, position);
204: }
205:
206: /**
207: * Formats the specified object. This method delegates the work to the
208: * {@linkplain Format format} specified at construction time.
209: *
210: * @param value The object to format.
211: * @return The character iterator describing the formatted value.
212: */
213: public AttributedCharacterIterator formatToCharacterIterator(
214: final Object value) {
215: return format.formatToCharacterIterator(value);
216: }
217:
218: /**
219: * Logs a warning.
220: *
221: * @param key The resource key.
222: * @param arg1 First value to format in the message.
223: * @param arg1 Second value to format in the message.
224: */
225: private void logWarning(final int key, final Object arg1,
226: final Object arg2) {
227: final LogRecord warning = Errors.getResources(
228: getWarningLocale()).getLogRecord(Level.WARNING, key,
229: arg1, arg2);
230: if (className != null) {
231: warning.setSourceClassName(className);
232: }
233: if (methodName != null) {
234: warning.setSourceMethodName(methodName);
235: }
236: logWarning(warning);
237: }
238:
239: /**
240: * Logs a warning. This method is invoked automatically by the {@link #parse parse} method
241: * when a text can't be fully parsed. The default implementation logs the warning to the
242: * logger specified by the last call to the {@link #setLogger setLogger} method. Subclasses
243: * may override this method if they want to change the log record before the logging.
244: *
245: * @param warning The warning to log.
246: */
247: protected void logWarning(final LogRecord warning) {
248: if (logger != null) {
249: Logging.getLogger(logger).log(warning);
250: }
251: }
252:
253: /**
254: * Returns the locale to use for formatting warnings. The default implementation returns
255: * the {@linkplain Locale#getDefault() default locale}.
256: */
257: protected Locale getWarningLocale() {
258: return Locale.getDefault();
259: }
260:
261: /**
262: * Returns a string representation for debugging purpose.
263: */
264: public String toString() {
265: final StringBuffer buffer = new StringBuffer(Utilities
266: .getShortClassName(this )).append('[').append(
267: Utilities.getShortClassName(format));
268: if (logger != null) {
269: buffer.append(", logger=").append(logger);
270: }
271: return buffer.append(']').toString();
272: }
273: }
|