Source Code Cross Referenced for PropertyAccessor.java in  » GIS » GeoTools-2.4.1 » org » geotools » metadata » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » GIS » GeoTools 2.4.1 » org.geotools.metadata 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


001:        /*
002:         *    GeoTools - OpenSource mapping toolkit
003:         *    http://geotools.org
004:         *    (C) 2007, GeoTools Project Managment Committee (PMC)
005:         *   
006:         *    This library is free software; you can redistribute it and/or
007:         *    modify it under the terms of the GNU Lesser General Public
008:         *    License as published by the Free Software Foundation;
009:         *    version 2.1 of the License.
010:         *
011:         *    This library is distributed in the hope that it will be useful,
012:         *    but WITHOUT ANY WARRANTY; without even the implied warranty of
013:         *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
014:         *    Lesser General Public License for more details.
015:         */
016:        package org.geotools.metadata;
017:
018:        // J2SE dependencies
019:        import java.lang.reflect.Array;
020:        import java.lang.reflect.InvocationTargetException;
021:        import java.lang.reflect.Method;
022:        import java.lang.reflect.UndeclaredThrowableException;
023:        import java.util.Collection;
024:        import java.util.HashMap;
025:        import java.util.Map;
026:
027:        // Geotools implementation
028:        import org.geotools.resources.XArray;
029:        import org.geotools.resources.i18n.ErrorKeys;
030:        import org.geotools.resources.i18n.Errors;
031:        import org.geotools.resources.Utilities;
032:
033:        /**
034:         * The getters declared in a GeoAPI interface, together with setters (if any)
035:         * declared in the Geotools implementation.
036:         *
037:         * @source $URL: http://svn.geotools.org/geotools/tags/2.4.1/modules/library/metadata/src/main/java/org/geotools/metadata/PropertyAccessor.java $
038:         * @version $Id: PropertyAccessor.java 25193 2007-04-18 13:37:38Z desruisseaux $
039:         * @author Martin Desruisseaux
040:         */
041:        final class PropertyAccessor {
042:            /**
043:             * The prefix for getters on boolean values.
044:             */
045:            private static final String IS = "is";
046:
047:            /**
048:             * The prefix for getters (general case).
049:             */
050:            private static final String GET = "get";
051:
052:            /**
053:             * The prefix for setters.
054:             */
055:            private static final String SET = "set";
056:
057:            /**
058:             * Methods to exclude from {@link #getGetters}. They are method inherited from
059:             * {@link java.lang.Object}. Some of them, especially {@link Object#hashCode()}
060:             * {@link Object#toString()} and {@link Object#clone()}, may be declared explicitly
061:             * in some interface with a formal contract. Note: only no-argument methods need to
062:             * be declared in this list.
063:             */
064:            private static final String[] EXCLUDES = { "clone", "finalize",
065:                    "getClass", "hashCode", "notify", "notifyAll", "toString",
066:                    "wait" };
067:
068:            /**
069:             * Getters shared between many instances of this class. Two different implementations
070:             * may share the same getters but different setters.
071:             */
072:            private static final Map/*<Class, Method[]>*/SHARED_GETTERS = new HashMap();
073:
074:            /**
075:             * The implemented metadata interface.
076:             */
077:            final Class type;
078:
079:            /**
080:             * The implementation class. The following condition must hold:
081:             *
082:             * <blockquote><pre>
083:             * type.{@linkplain Class#isAssignableFrom isAssignableFrom}(implementation);
084:             * </pre></blockquote>
085:             */
086:            final Class implementation;
087:
088:            /**
089:             * The getter methods. This array should not contain any null element.
090:             */
091:            private final Method[] getters;
092:
093:            /**
094:             * The corresponding setter methods, or {@code null} if none. This array must have
095:             * the same length than {@link #getters}. For every {@code getters[i]} element,
096:             * {@code setters[i]} is the corresponding setter or {@code null} if there is none.
097:             */
098:            private final Method[] setters;
099:
100:            /**
101:             * Creates a new property reader for the specified metadata implementation.
102:             *
103:             * @param  metadata The metadata implementation to wrap.
104:             * @param  type The interface implemented by the metadata.
105:             *         Should be the value returned by {@link #getType}.
106:             */
107:            PropertyAccessor(final Class implementation, final Class type) {
108:                this .implementation = implementation;
109:                this .type = type;
110:                assert type.isAssignableFrom(implementation) : implementation;
111:                getters = getGetters(type);
112:                Method[] setters = null;
113:                final Class[] arguments = new Class[1];
114:                for (int i = 0; i < getters.length; i++) {
115:                    final Method getter = getters[i];
116:                    final Method setter; // To be determined later
117:                    arguments[0] = getter.getReturnType();
118:                    String name = getter.getName();
119:                    final int base = prefix(name).length();
120:                    if (name.length() > base) {
121:                        final char lo = name.charAt(base);
122:                        final char up = Character.toUpperCase(lo);
123:                        if (lo != up) {
124:                            name = SET + up + name.substring(base + 1);
125:                        } else {
126:                            name = SET + name.substring(base);
127:                        }
128:                    }
129:                    try {
130:                        setter = implementation.getMethod(name, arguments);
131:                    } catch (NoSuchMethodException e) {
132:                        continue;
133:                    }
134:                    if (setters == null) {
135:                        setters = new Method[getters.length];
136:                    }
137:                    setters[i] = setter;
138:                }
139:                this .setters = setters;
140:            }
141:
142:            /**
143:             * Returns the metadata interface implemented by the specified implementation.
144:             * Only one metadata interface can be implemented.
145:             *
146:             * @param  metadata The metadata implementation to wraps.
147:             * @param  interfacePackage The root package for metadata interfaces.
148:             * @return The single interface, or {@code null} if none where found.
149:             */
150:            static Class getType(final Class implementation,
151:                    final String interfacePackage) {
152:                if (!implementation.isInterface()) {
153:                    final Class[] interfaces = implementation.getInterfaces();
154:                    int count = 0;
155:                    for (int i = 0; i < interfaces.length; i++) {
156:                        final Class candidate = interfaces[i];
157:                        if (candidate.getName().startsWith(interfacePackage)) {
158:                            interfaces[count++] = candidate;
159:                        }
160:                    }
161:                    if (count == 1) {
162:                        return interfaces[0];
163:                    }
164:                }
165:                return null;
166:            }
167:
168:            /**
169:             * Returns the getters. The returned array should never be modified,
170:             * since it may be shared among many instances of {@code PropertyAccessor}.
171:             *
172:             * @todo Ignore deprecated methods when we will be allowed to compile for J2SE 1.5.
173:             */
174:            private static Method[] getGetters(final Class type) {
175:                synchronized (SHARED_GETTERS) {
176:                    Method[] getters = (Method[]) SHARED_GETTERS.get(type);
177:                    if (getters == null) {
178:                        getters = type.getMethods();
179:                        int count = 0;
180:                        for (int i = 0; i < getters.length; i++) {
181:                            final Method candidate = getters[i];
182:                            if (!candidate.getReturnType().equals(Void.TYPE)
183:                                    && candidate.getParameterTypes().length == 0) {
184:                                /*
185:                                 * We do not require a name starting with "get" or "is" prefix because some
186:                                 * methods do not begin with such prefix, as in "ConformanceResult.pass()".
187:                                 * Consequently we must provide special cases for no-arg methods inherited
188:                                 * from java.lang.Object because some interfaces declare explicitly the
189:                                 * contract for those methods.
190:                                 */
191:                                final String name = candidate.getName();
192:                                if (!name.startsWith(SET) && !isExcluded(name)) {
193:                                    getters[count++] = candidate;
194:                                }
195:                            }
196:                        }
197:                        getters = (Method[]) XArray.resize(getters, count);
198:                        SHARED_GETTERS.put(type, getters);
199:                    }
200:                    return getters;
201:                }
202:            }
203:
204:            /**
205:             * Returns {@code true} if the specified method is on the exclusion list.
206:             */
207:            private static boolean isExcluded(final String name) {
208:                for (int i = 0; i < EXCLUDES.length; i++) {
209:                    if (name.equals(EXCLUDES[i])) {
210:                        return true;
211:                    }
212:                }
213:                return false;
214:            }
215:
216:            /**
217:             * Returns the prefix of the specified method name. If the method name don't starts with
218:             * a prefix (for example {@link org.opengis.metadata.quality.ConformanceResult#pass()}),
219:             * then this method returns an empty string.
220:             */
221:            private static String prefix(final String name) {
222:                if (name.startsWith(GET)) {
223:                    return GET;
224:                }
225:                if (name.startsWith(IS)) {
226:                    return IS;
227:                }
228:                if (name.startsWith(SET)) {
229:                    return SET;
230:                }
231:                return "";
232:            }
233:
234:            /**
235:             * Returns the number of properties that can be read.
236:             */
237:            final int count() {
238:                return getters.length;
239:            }
240:
241:            /**
242:             * Returns the index of the specified property, or -1 if none.
243:             * The search is case-insensitive.
244:             */
245:            final int indexOf(String key) {
246:                key = key.trim();
247:                for (int i = 0; i < getters.length; i++) {
248:                    final String name = getters[i].getName();
249:                    final int base = prefix(name).length();
250:                    final int length = key.length();
251:                    if (name.length() == base + length
252:                            && name.regionMatches(true, base, key, 0, length)) {
253:                        return i;
254:                    }
255:                }
256:                return -1;
257:            }
258:
259:            /**
260:             * Returns {@code true} if the specified string starting at the specified index contains
261:             * no lower case characters. The characters don't have to be in upper case however (e.g.
262:             * non-alphabetic characters)
263:             */
264:            private static boolean isAcronym(final String name, int offset) {
265:                final int length = name.length();
266:                while (offset < length) {
267:                    if (Character.isLowerCase(name.charAt(offset++))) {
268:                        return false;
269:                    }
270:                }
271:                return true;
272:            }
273:
274:            /**
275:             * Returns the name of the property at the given index, or {@code null} if none.
276:             */
277:            final String name(final int index) {
278:                if (index >= 0 && index < getters.length) {
279:                    String name = getters[index].getName();
280:                    final int base = prefix(name).length();
281:                    /*
282:                     * Remove the "get" or "is" prefix and turn the first character after the
283:                     * prefix into lower case. For example the method name "getTitle" will be
284:                     * replaced by the property name "title". We will performs this operation
285:                     * only if there is at least 1 character after the prefix.
286:                     */
287:                    if (name.length() > base) {
288:                        if (isAcronym(name, base)) {
289:                            name = name.substring(base);
290:                        } else {
291:                            final char up = name.charAt(base);
292:                            final char lo = Character.toLowerCase(up);
293:                            if (up != lo) {
294:                                name = lo + name.substring(base + 1);
295:                            } else {
296:                                name = name.substring(base);
297:                            }
298:                        }
299:                    }
300:                    return name;
301:                }
302:                return null;
303:            }
304:
305:            /**
306:             * Returns the type of the property at the given index.
307:             */
308:            final Class type(final int index) {
309:                if (index >= 0 && index < getters.length) {
310:                    return getters[index].getReturnType();
311:                }
312:                return null;
313:            }
314:
315:            /**
316:             * Returns {@code true} if the property at the given index is writable.
317:             */
318:            final boolean isWritable(final int index) {
319:                return (index >= 0) && (index < getters.length)
320:                        && (setters != null) && (setters[index] != null);
321:            }
322:
323:            /**
324:             * Returns the value for the specified metadata, or {@code null} if none.
325:             */
326:            final Object get(final int index, final Object metadata) {
327:                return (index >= 0 && index < getters.length) ? get(
328:                        getters[index], metadata) : null;
329:            }
330:
331:            /**
332:             * Gets a value from the specified metadata. We do not expect any checked exception to
333:             * be thrown, since {@code org.opengis.metadata} do not declare any.
334:             *
335:             * @param method The method to use for the query.
336:             * @param metadata The metadata object to query.
337:             */
338:            private static Object get(final Method method, final Object metadata) {
339:                assert !method.getReturnType().equals(Void.TYPE) : method;
340:                try {
341:                    return method.invoke(metadata, (Object[]) null);
342:                } catch (IllegalAccessException e) {
343:                    // Should never happen since 'getters' should contains only public methods.
344:                    throw new AssertionError(e);
345:                } catch (InvocationTargetException e) {
346:                    final Throwable cause = e.getTargetException();
347:                    if (cause instanceof  RuntimeException) {
348:                        throw (RuntimeException) cause;
349:                    }
350:                    if (cause instanceof  Error) {
351:                        throw (Error) cause;
352:                    }
353:                    throw new UndeclaredThrowableException(cause);
354:                }
355:            }
356:
357:            /**
358:             * Set a value for the specified metadata.
359:             *
360:             * @return The old value.
361:             * @throws IllegalArgumentException if the specified property can't be set.
362:             */
363:            final Object set(final int index, final Object metadata,
364:                    final Object value) throws IllegalArgumentException {
365:                if (index >= 0 && index < getters.length && setters != null) {
366:                    final Method setter = setters[index];
367:                    if (setter != null) {
368:                        final Object old = get(getters[index], metadata);
369:                        set(setter, metadata, new Object[] { value });
370:                        return old;
371:                    }
372:                }
373:                throw new IllegalArgumentException(Errors.format(
374:                        ErrorKeys.ILLEGAL_ARGUMENT_$1, "key"));
375:            }
376:
377:            /**
378:             * Sets a value for the specified metadata. We do not expect any checked exception to
379:             * be thrown.
380:             *
381:             * @param method The method to use for the query.
382:             * @param metadata The metadata object to query.
383:             */
384:            private static void set(final Method method, final Object metadata,
385:                    final Object[] arguments) {
386:                try {
387:                    method.invoke(metadata, arguments);
388:                } catch (IllegalAccessException e) {
389:                    // Should never happen since 'setters' should contains only public methods.
390:                    throw new AssertionError(e);
391:                } catch (InvocationTargetException e) {
392:                    final Throwable cause = e.getTargetException();
393:                    if (cause instanceof  RuntimeException) {
394:                        throw (RuntimeException) cause;
395:                    }
396:                    if (cause instanceof  Error) {
397:                        throw (Error) cause;
398:                    }
399:                    throw new UndeclaredThrowableException(cause);
400:                }
401:            }
402:
403:            /**
404:             * Compares the two specified metadata objects. The comparaison is <cite>shallow</cite>,
405:             * i.e. all metadata attributes are compared using the {@link Object#equals} method without
406:             * recursive call to this {@code shallowEquals} method for other metadata.
407:             * <p>
408:             * This method can optionaly excludes null values from the comparaison. In metadata,
409:             * null value often means "don't know", so in some occasion we want to consider two
410:             * metadata as different only if an attribute value is know for sure to be different.
411:             *
412:             * @param metadata1 The first metadata object to compare.
413:             * @param metadata2 The second metadata object to compare.
414:             * @param skipNulls If {@code true}, only non-null values will be compared.
415:             */
416:            public boolean shallowEquals(final Object metadata1,
417:                    final Object metadata2, final boolean skipNulls) {
418:                assert type.isInstance(metadata1);
419:                assert type.isInstance(metadata2);
420:                for (int i = 0; i < getters.length; i++) {
421:                    final Method method = getters[i];
422:                    final Object value1 = get(method, metadata1);
423:                    final Object value2 = get(method, metadata2);
424:                    final boolean empty1 = isEmpty(value1);
425:                    final boolean empty2 = isEmpty(value2);
426:                    if (empty1 && empty2) {
427:                        continue;
428:                    }
429:                    if (!Utilities.equals(value1, value2)) {
430:                        if (!skipNulls || (!empty1 && !empty2)) {
431:                            return false;
432:                        }
433:                    }
434:                }
435:                return true;
436:            }
437:
438:            /**
439:             * Copies all metadata from source to target. The source can be any implementation of
440:             * the metadata interface, but the target must be the implementation expected by this
441:             * class.
442:             *
443:             * @param  source The metadata to copy.
444:             * @param  target The target metadata.
445:             * @param  skipNulls If {@code true}, only non-null values will be copied.
446:             * @return {@code true} in case of success, or {@code false} if at least
447:             *         one setter method was not found.
448:             * @throws UnmodifiableMetadataException if the target metadata is unmodifiable.
449:             */
450:            public boolean shallowCopy(final Object source,
451:                    final Object target, final boolean skipNulls)
452:                    throws UnmodifiableMetadataException {
453:                boolean success = true;
454:                assert type.isInstance(source);
455:                assert implementation.isInstance(target);
456:                final Object[] arguments = new Object[1];
457:                for (int i = 0; i < getters.length; i++) {
458:                    arguments[0] = get(getters[i], source);
459:                    if (!skipNulls || !isEmpty(arguments[0])) {
460:                        if (setters == null) {
461:                            return false;
462:                        }
463:                        final Method setter = setters[i];
464:                        if (setter != null) {
465:                            set(setter, target, arguments);
466:                        } else {
467:                            success = false;
468:                        }
469:                    }
470:                }
471:                return success;
472:            }
473:
474:            /**
475:             * Replaces every properties in the specified metadata by their
476:             * {@linkplain ModifiableMetadata#unmodifiable unmodifiable variant.
477:             */
478:            final void freeze(final Object metadata) {
479:                assert implementation.isInstance(metadata);
480:                if (setters != null) {
481:                    final Object[] arguments = new Object[1];
482:                    for (int i = 0; i < getters.length; i++) {
483:                        final Method setter = setters[i];
484:                        if (setter != null) {
485:                            final Object source = get(getters[i], metadata);
486:                            final Object target = ModifiableMetadata
487:                                    .unmodifiable(source);
488:                            if (source != target) {
489:                                arguments[0] = target;
490:                                set(setter, metadata, arguments);
491:                            }
492:                        }
493:                    }
494:                }
495:            }
496:
497:            /**
498:             * Returns {@code true} if the metadata is modifiable. This method is not public because it
499:             * uses heuristic rules. In case of doubt, this method conservatively returns {@code true}.
500:             */
501:            final boolean isModifiable() {
502:                if (setters != null) {
503:                    return true;
504:                }
505:                for (int i = 0; i < getters.length; i++) {
506:                    // Immutable objects usually don't need to be cloned. So if
507:                    // an object is cloneable, it is probably not immutable.
508:                    if (Cloneable.class.isAssignableFrom(getters[i]
509:                            .getReturnType())) {
510:                        return true;
511:                    }
512:                }
513:                return false;
514:            }
515:
516:            /**
517:             * Returns a hash code for the specified metadata. The hash code is defined as the
518:             * sum of hash code values of all non-null properties. This is the same contract than
519:             * {@link java.util.Set#hashCode} and ensure that the hash code value is insensitive
520:             * to the ordering of properties.
521:             */
522:            public int hashCode(final Object metadata) {
523:                assert type.isInstance(metadata);
524:                int code = 0;
525:                for (int i = 0; i < getters.length; i++) {
526:                    final Object value = get(getters[i], metadata);
527:                    if (!isEmpty(value)) {
528:                        code += value.hashCode();
529:                    }
530:                }
531:                return code;
532:            }
533:
534:            /**
535:             * Counts the number of non-null properties.
536:             */
537:            public int count(final Object metadata, final int max) {
538:                assert type.isInstance(metadata);
539:                int count = 0;
540:                for (int i = 0; i < getters.length; i++) {
541:                    if (!isEmpty(get(getters[i], metadata))) {
542:                        if (++count >= max) {
543:                            break;
544:                        }
545:                    }
546:                }
547:                return count;
548:            }
549:
550:            /**
551:             * Returns {@code true} if the specified object is null or an empty collection,
552:             * array or string.
553:             */
554:            static boolean isEmpty(final Object value) {
555:                return value == null
556:                        || ((value instanceof  Collection) && ((Collection) value)
557:                                .isEmpty())
558:                        || ((value instanceof  CharSequence) && value.toString()
559:                                .trim().length() == 0)
560:                        || (value.getClass().isArray() && Array
561:                                .getLength(value) == 0);
562:            }
563:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.