001: /*
002: * GeoTools - OpenSource mapping toolkit
003: * http://geotools.org
004: * (C) 2003-2006, Geotools Project Managment Committee (PMC)
005: * (C) 2001, Institut de Recherche pour le D�veloppement
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.resources;
018:
019: // Collections and arrays
020: import java.util.Arrays;
021: import java.util.logging.Level;
022: import java.util.logging.LogRecord;
023: import java.util.logging.Logger;
024: import org.geotools.util.logging.Logging;
025:
026: /**
027: * A set of miscellaneous methods.
028: *
029: * @since 2.0
030: * @source $URL: http://svn.geotools.org/geotools/tags/2.4.1/modules/library/metadata/src/main/java/org/geotools/resources/Utilities.java $
031: * @version $Id: Utilities.java 27862 2007-11-12 19:51:19Z desruisseaux $
032: * @author Martin Desruisseaux
033: */
034: public final class Utilities {
035: /**
036: * An array of strings containing only white spaces. Strings' lengths are equal to their
037: * index + 1 in the {@code spacesFactory} array. For example, {@code spacesFactory[4]}
038: * contains a string of length 5. Strings are constructed only when first needed.
039: */
040: private static final String[] spacesFactory = new String[20];
041:
042: /**
043: * Forbid object creation.
044: */
045: private Utilities() {
046: }
047:
048: /**
049: * Convenience method for testing two objects for equality. One or both objects may be null.
050: */
051: public static boolean equals(final Object object1,
052: final Object object2) {
053: return (object1 == object2)
054: || (object1 != null && object1.equals(object2));
055: }
056:
057: /**
058: * Returns {@code true} if the two specified objects implements exactly the same set of
059: * interfaces. Only interfaces assignable to {@code base} are compared. Declaration order
060: * doesn't matter. For example in ISO 19111, different interfaces exist for different coordinate
061: * system geometries ({@code CartesianCS}, {@code PolarCS}, etc.). We can check if two
062: * CS implementations has the same geometry with the following code:
063: *
064: * <blockquote><code>
065: * if (sameInterfaces(cs1, cs2, {@linkplain org.opengis.referencing.cs.CoordinateSystem}.class))
066: * </code></blockquote>
067: */
068: public static boolean sameInterfaces(final Class object1,
069: final Class object2, final Class base) {
070: if (object1 == object2) {
071: return true;
072: }
073: if (object1 == null || object2 == null) {
074: return false;
075: }
076: final Class[] c1 = object1.getInterfaces();
077: final Class[] c2 = object2.getInterfaces();
078: /*
079: * Trim all interfaces that are not assignable to 'base' in the 'c2' array.
080: * Doing this once will avoid to redo the same test many time in the inner
081: * loops j=[0..n].
082: */
083: int n = 0;
084: for (int i = 0; i < c2.length; i++) {
085: final Class c = c2[i];
086: if (base.isAssignableFrom(c)) {
087: c2[n++] = c;
088: }
089: }
090: /*
091: * For each interface assignable to 'base' in the 'c1' array, check if
092: * this interface exists also in the 'c2' array. Order doesn't matter.
093: */
094: compare: for (int i = 0; i < c1.length; i++) {
095: final Class c = c1[i];
096: if (base.isAssignableFrom(c)) {
097: for (int j = 0; j < n; j++) {
098: if (c.equals(c2[j])) {
099: System.arraycopy(c2, j + 1, c2, j, --n - j);
100: continue compare;
101: }
102: }
103: return false; // Interface not found in 'c2'.
104: }
105: }
106: return n == 0; // If n>0, at least one interface was not found in 'c1'.
107: }
108:
109: /**
110: * Returns a string of the specified length filled with white spaces.
111: * This method tries to return a pre-allocated string if possible.
112: *
113: * @param length The string length. Negative values are clamped to 0.
114: * @return A string of length {@code length} filled with white spaces.
115: */
116: public static String spaces(int length) {
117: // No need to synchronize. In the unlikely event of two threads
118: // calling this method at the same time and the two calls creating a
119: // new string, the String.intern() call will take care of
120: // canonicalizing the strings.
121: final int last = spacesFactory.length - 1;
122: if (length < 0)
123: length = 0;
124: if (length <= last) {
125: if (spacesFactory[length] == null) {
126: if (spacesFactory[last] == null) {
127: char[] blancs = new char[last];
128: Arrays.fill(blancs, ' ');
129: spacesFactory[last] = new String(blancs).intern();
130: }
131: spacesFactory[length] = spacesFactory[last].substring(
132: 0, length).intern();
133: }
134: return spacesFactory[length];
135: } else {
136: char[] blancs = new char[length];
137: Arrays.fill(blancs, ' ');
138: return new String(blancs);
139: }
140: }
141:
142: /**
143: * Returns a short class name for the specified class. This method will
144: * omit the package name. For example, it will return "String" instead
145: * of "java.lang.String" for a {@link String} object. It will also name
146: * array according Java language usage, for example "double[]" instead
147: * of "[D".
148: *
149: * @param classe The object class (may be {@code null}).
150: * @return A short class name for the specified object.
151: *
152: * @todo Consider replacing by {@link Class#getSimpleName} when we will
153: * be allowed to compile for J2SE 1.5.
154: */
155: public static String getShortName(Class classe) {
156: if (classe == null) {
157: return "<*>";
158: }
159: int dimension = 0;
160: Class el;
161: while ((el = classe.getComponentType()) != null) {
162: classe = el;
163: dimension++;
164: }
165: String name = classe.getName();
166: final int lower = name.lastIndexOf('.');
167: final int upper = name.length();
168: name = name.substring(lower + 1, upper).replace('$', '.');
169: if (dimension != 0) {
170: StringBuffer buffer = new StringBuffer(name);
171: do {
172: buffer.append("[]");
173: } while (--dimension != 0);
174: name = buffer.toString();
175: }
176: return name;
177: }
178:
179: /**
180: * Returns a short class name for the specified object. This method will
181: * omit the package name. For example, it will return "String" instead
182: * of "java.lang.String" for a {@link String} object.
183: *
184: * @param object The object (may be {@code null}).
185: * @return A short class name for the specified object.
186: */
187: public static String getShortClassName(final Object object) {
188: return getShortName(object != null ? object.getClass() : null);
189: }
190:
191: /**
192: * Invoked when an unexpected error occurs.
193: *
194: * @param paquet The package where the error occurred. This information
195: * may be used to fetch an appropriate {@link Logger} for
196: * logging the error.
197: * @param classe The class name where the error occurred.
198: * @param method The method name where the error occurred.
199: * @param error The error.
200: *
201: * @deprecated Replaced by {@link org.geotools.util.Logging#unexpectedException}.
202: */
203: public static void unexpectedException(final String paquet,
204: final String classe, final String method,
205: final Throwable error) {
206: final LogRecord record = getLogRecord(error);
207: record.setSourceClassName(classe);
208: record.setSourceMethodName(method);
209: record.setThrown(error);
210: Logger.getLogger(paquet).log(record);
211: }
212:
213: /**
214: * Invoked when a recoverable error occurs. This exception is similar to
215: * {@link #unexpectedException unexpectedException} except that it doesn't
216: * log the stack trace and uses a lower logging level.
217: *
218: * @param paquet The package where the error occurred. This information
219: * may be used to fetch an appropriate {@link Logger} for
220: * logging the error.
221: * @param classe The class where the error occurred.
222: * @param method The method name where the error occurred.
223: * @param error The error.
224: */
225: public static void recoverableException(final String paquet,
226: final Class classe, final String method,
227: final Throwable error) {
228: final LogRecord record = getLogRecord(error);
229: record.setLevel(Level.FINER);
230: record.setSourceClassName(classe.getName());
231: record.setSourceMethodName(method);
232: Logging.getLogger(paquet).log(record);
233: }
234:
235: /**
236: * Returns a log record for the specified exception.
237: */
238: public static LogRecord getLogRecord(final Throwable error) {
239: final StringBuffer buffer = new StringBuffer(
240: getShortClassName(error));
241: final String message = error.getLocalizedMessage();
242: if (message != null) {
243: buffer.append(": ");
244: buffer.append(message);
245: }
246: return new LogRecord(Level.WARNING, buffer.toString());
247: }
248: }
|