source: osm/applications/editors/josm/plugins/opendata/includes/org/geotools/metadata/MetadataStandard.java@ 28000

Last change on this file since 28000 was 28000, checked in by donvip, 12 years ago

Import new "opendata" JOSM plugin

File size: 12.8 KB
Line 
1/*
2 * GeoTools - The Open Source Java GIS Toolkit
3 * http://geotools.org
4 *
5 * (C) 2007-2008, Open Source Geospatial Foundation (OSGeo)
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation;
10 * version 2.1 of the License.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 */
17package org.geotools.metadata;
18
19import java.util.AbstractMap;
20import java.util.HashMap;
21import java.util.Map;
22
23import org.geotools.resources.i18n.ErrorKeys;
24import org.geotools.resources.i18n.Errors;
25
26
27/**
28 * Enumeration of some metadata standards. A standard is defined by a set of Java interfaces
29 * in a specific package or subpackages. For example the {@linkplain #ISO_19115 ISO 19115}
30 * standard is defined by <A HREF="http://geoapi.sourceforge.net">GeoAPI</A> interfaces in
31 * the {@link org.opengis.metadata} package and subpackages.
32 * <p>
33 * This class provides some methods operating on metadata instances through
34 * {@linkplain java.lang.reflect Java reflection}. The following rules are
35 * assumed:
36 * <p>
37 * <ul>
38 * <li>Properties (or metadata attributes) are defined by the set of {@code get*()}
39 * (arbitrary return type) or {@code is*()} (boolean return type) methods found
40 * in the <strong>interface</strong>. Getters declared in the implementation
41 * only are ignored.</li>
42 * <li>A property is <cite>writable</cite> if a {@code set*(...)} method is defined
43 * in the implementation class for the corresponding {@code get*()} method. The
44 * setter don't need to be defined in the interface.</li>
45 * </ul>
46 *
47 * @since 2.4
48 *
49 * @source $URL: http://svn.osgeo.org/geotools/branches/2.7.x/modules/library/metadata/src/main/java/org/geotools/metadata/MetadataStandard.java $
50 * @version $Id: MetadataStandard.java 37298 2011-05-25 05:16:15Z mbedward $
51 * @author Martin Desruisseaux (Geomatys)
52 */
53public final class MetadataStandard {
54 /**
55 * An instance working on ISO 19115 standard as defined by
56 * <A HREF="http://geoapi.sourceforge.net">GeoAPI</A> interfaces
57 * in the {@link org.opengis.metadata} package and subpackages.
58 */
59 public static final MetadataStandard ISO_19115 = new MetadataStandard("org.opengis.metadata.");
60
61 /**
62 * The root packages for metadata interfaces. Must ends with {@code "."}.
63 */
64 private final String interfacePackage;
65
66 /**
67 * Accessors for the specified implementations.
68 */
69 private final Map<Class<?>,PropertyAccessor> accessors = new HashMap<Class<?>,PropertyAccessor>();
70
71 /**
72 * Shared pool of {@link PropertyTree} instances, once for each thread
73 * (in order to avoid the need for thread synchronization).
74 */
75 private final ThreadLocal<PropertyTree> treeBuilders = new ThreadLocal<PropertyTree>() {
76 @Override
77 protected PropertyTree initialValue() {
78 return new PropertyTree(MetadataStandard.this);
79 }
80 };
81
82 /**
83 * Creates a new instance working on implementation of interfaces defined
84 * in the specified package. For the ISO 19115 standard reflected by GeoAPI
85 * interfaces, it should be the {@link org.opengis.metadata} package.
86 *
87 * @param interfacePackage The root package for metadata interfaces.
88 */
89 public MetadataStandard(String interfacePackage) {
90 if (!interfacePackage.endsWith(".")) {
91 interfacePackage += '.';
92 }
93 this.interfacePackage = interfacePackage;
94 }
95
96 /**
97 * Returns the accessor for the specified implementation.
98 *
99 * @throws ClassCastException if the specified implementation class do
100 * not implements a metadata interface of the expected package.
101 */
102 private PropertyAccessor getAccessor(final Class<?> implementation)
103 throws ClassCastException
104 {
105 final PropertyAccessor accessor = getAccessorOptional(implementation);
106 if (accessor == null) {
107 throw new ClassCastException(Errors.format(ErrorKeys.UNKNOW_TYPE_$1,
108 implementation.getName()));
109 }
110 return accessor;
111 }
112
113 /**
114 * Returns the accessor for the specified implementation, or {@code null} if none.
115 */
116 final PropertyAccessor getAccessorOptional(final Class<?> implementation) {
117 synchronized (accessors) {
118 PropertyAccessor accessor = accessors.get(implementation);
119 if (accessor == null) {
120 Class<?> type = getType(implementation);
121 if (type != null) {
122 accessor = new PropertyAccessor(implementation, type);
123 accessors.put(implementation, accessor);
124 }
125 }
126 return accessor;
127 }
128 }
129
130 /**
131 * Returns the metadata interface implemented by the specified implementation.
132 * Only one metadata interface can be implemented.
133 *
134 * @param metadata The metadata implementation to wraps.
135 * @return The single interface, or {@code null} if none where found.
136 */
137 private Class<?> getType(final Class<?> implementation) {
138 return PropertyAccessor.getType(implementation, interfacePackage);
139 }
140
141 /**
142 * Returns the metadata interface implemented by the specified implementation class.
143 *
144 * @param implementation The implementation class.
145 * @return The interface implemented by the given implementation class.
146 * @throws ClassCastException if the specified implementation class do
147 * not implements a metadata interface of the expected package.
148 *
149 * @see AbstractMap#getInterface
150 */
151 public Class<?> getInterface(final Class<?> implementation) throws ClassCastException {
152 return getAccessor(implementation).type;
153 }
154
155 /**
156 * Returns a view of the specified metadata object as a {@linkplain Map map}.
157 * The map is backed by the metadata object using Java reflection, so changes
158 * in the underlying metadata object are immediately reflected in the map.
159 * The keys are the property names as determined by the list of {@code get*()}
160 * methods declared in the {@linkplain #getInterface metadata interface}.
161 * <p>
162 * The map supports the {@link Map#put put} operations if the underlying
163 * metadata object contains {@link #set*(...)} methods.
164 *
165 * @param metadata The metadata object to view as a map.
166 * @return A map view over the metadata object.
167 * @throws ClassCastException if at the metadata object don't
168 * implements a metadata interface of the expected package.
169 *
170 * @see AbstractMap#asMap
171 */
172 public Map<String,Object> asMap(final Object metadata) throws ClassCastException {
173 return new PropertyMap(metadata, getAccessor(metadata.getClass()));
174 }
175
176 /**
177 * Returns {@code true} if this metadata is modifiable. This method is not public because it
178 * uses heuristic rules. In case of doubt, this method conservatively returns {@code true}.
179 *
180 * @throws ClassCastException if the specified implementation class do
181 * not implements a metadata interface of the expected package.
182 *
183 * @see AbstractMap#isModifiable
184 */
185 final boolean isModifiable(final Class implementation) throws ClassCastException {
186 return getAccessor(implementation).isModifiable();
187 }
188
189 /**
190 * Replaces every properties in the specified metadata by their
191 * {@linkplain ModifiableMetadata#unmodifiable unmodifiable variant.
192 *
193 * @throws ClassCastException if the specified implementation class do
194 * not implements a metadata interface of the expected package.
195 *
196 * @see ModifiableMetadata#freeze()
197 */
198 final void freeze(final Object metadata) throws ClassCastException {
199 getAccessor(metadata.getClass()).freeze(metadata);
200 }
201
202 /**
203 * Copies all metadata from source to target. The source must implements the same
204 * metadata interface than the target.
205 *
206 * @param source The metadata to copy.
207 * @param target The target metadata.
208 * @param skipNulls If {@code true}, only non-null values will be copied.
209 * @throws ClassCastException if the source or target object don't
210 * implements a metadata interface of the expected package.
211 * @throws UnmodifiableMetadataException if the target metadata is unmodifiable,
212 * or if at least one setter method was required but not found.
213 *
214 * @see AbstractMap#AbstractMap(Object)
215 */
216 public void shallowCopy(final Object source, final Object target, final boolean skipNulls)
217 throws ClassCastException, UnmodifiableMetadataException
218 {
219 ensureNonNull("target", target);
220 final PropertyAccessor accessor = getAccessor(target.getClass());
221 if (!accessor.type.isInstance(source)) {
222 ensureNonNull("source", source);
223 throw new ClassCastException(Errors.format(ErrorKeys.ILLEGAL_CLASS_$2,
224 source.getClass(), accessor.type));
225 }
226 if (!accessor.shallowCopy(source, target, skipNulls)) {
227 throw new UnmodifiableMetadataException(Errors.format(ErrorKeys.UNMODIFIABLE_METADATA));
228 }
229 }
230
231 /**
232 * Compares the two specified metadata objects. The comparaison is <cite>shallow</cite>,
233 * i.e. all metadata attributes are compared using the {@link Object#equals} method without
234 * recursive call to this {@code shallowEquals(...)} method for child metadata.
235 * <p>
236 * This method can optionaly excludes null values from the comparaison. In metadata,
237 * null value often means "don't know", so in some occasion we want to consider two
238 * metadata as different only if an attribute value is know for sure to be different.
239 * <p>
240 * The first arguments must be an implementation of a metadata interface, otherwise an
241 * exception will be thrown. The two argument do not need to be the same implementation
242 * however.
243 *
244 * @param metadata1 The first metadata object to compare.
245 * @param metadata2 The second metadata object to compare.
246 * @param skipNulls If {@code true}, only non-null values will be compared.
247 * @return {@code true} if the given metadata objects are equals.
248 * @throws ClassCastException if at least one metadata object don't
249 * implements a metadata interface of the expected package.
250 *
251 * @see AbstractMetadata#equals
252 */
253 public boolean shallowEquals(final Object metadata1, final Object metadata2, final boolean skipNulls)
254 throws ClassCastException
255 {
256 if (metadata1 == metadata2) {
257 return true;
258 }
259 if (metadata1 == null || metadata2 == null) {
260 return false;
261 }
262 final PropertyAccessor accessor = getAccessor(metadata1.getClass());
263 if (!accessor.type.equals(getType(metadata2.getClass()))) {
264 return false;
265 }
266 return accessor.shallowEquals(metadata1, metadata2, skipNulls);
267 }
268
269 /**
270 * Computes a hash code for the specified metadata. The hash code is defined as the
271 * sum of hash code values of all non-null properties. This is the same contract than
272 * {@link java.util.Set#hashCode} and ensure that the hash code value is insensitive
273 * to the ordering of properties.
274 *
275 * @param metadata The metadata object to compute hash code.
276 * @return A hash code value for the specified metadata.
277 * @throws ClassCastException if at the metadata object don't
278 * implements a metadata interface of the expected package.
279 *
280 * @see AbstractMap#hashCode
281 */
282 public int hashCode(final Object metadata) throws ClassCastException {
283 return getAccessor(metadata.getClass()).hashCode(metadata);
284 }
285
286 /**
287 * Returns a string representation of the specified metadata.
288 *
289 * @param metadata The metadata object to formats as a string.
290 * @return A string representation of the specified metadata.
291 * @throws ClassCastException if at the metadata object don't
292 * implements a metadata interface of the expected package.
293 *
294 * @see AbstractMap#toString
295 */
296 public String toString(final Object metadata) throws ClassCastException {
297 final PropertyTree builder = treeBuilders.get();
298 return PropertyTree.toString(builder.asTree(metadata));
299 }
300
301 /**
302 * Ensures that the specified argument is non-null.
303 */
304 private static void ensureNonNull(final String name, final Object value) {
305 if (value == null) {
306 throw new NullPointerException(Errors.format(ErrorKeys.NULL_ARGUMENT_$1, name));
307 }
308 }
309}
Note: See TracBrowser for help on using the repository browser.