001: /*
002: * GeoTools - OpenSource mapping toolkit
003: * http://geotools.org
004: * (C) 2004-2006, 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.iso;
017:
018: // J2SE dependencies
019: import java.util.Set;
020: import java.util.Map;
021: import java.util.List;
022: import java.util.Iterator;
023: import java.util.ArrayList;
024: import java.util.Collection;
025: import java.util.Collections;
026: import java.io.Serializable;
027:
028: // OpenGIS dependencies
029: import org.opengis.util.Cloneable;
030:
031: // Geotools dependencies
032: import org.geotools.metadata.MetadataStandard;
033: import org.geotools.metadata.ModifiableMetadata;
034: import org.geotools.metadata.InvalidMetadataException;
035: import org.geotools.resources.i18n.Errors;
036: import org.geotools.resources.i18n.ErrorKeys;
037:
038: /**
039: * A superclass for implementing ISO 19115 metadata interfaces. Subclasses
040: * must implement at least one of the ISO MetaData interface provided by
041: * <A HREF="http://geoapi.sourceforge.net">GeoAPI</A>.
042: *
043: * @since 2.1
044: * @source $URL: http://svn.geotools.org/geotools/tags/2.4.1/modules/library/metadata/src/main/java/org/geotools/metadata/iso/MetadataEntity.java $
045: * @version $Id: MetadataEntity.java 25189 2007-04-17 13:23:47Z desruisseaux $
046: * @author Jody Garnett
047: * @author Martin Desruisseaux
048: */
049: public class MetadataEntity extends ModifiableMetadata implements
050: Serializable {
051: /**
052: * Serial number for interoperability with different versions.
053: */
054: private static final long serialVersionUID = 5730550742604669102L;
055:
056: /**
057: * Constructs an initially empty metadata entity.
058: */
059: protected MetadataEntity() {
060: super ();
061: }
062:
063: /**
064: * Constructs a metadata entity initialized with the values from the specified metadata.
065: * The {@code source} metadata must implements the same metadata interface than this class.
066: *
067: * @param source The metadata to copy values from.
068: * @throws ClassCastException if the specified metadata don't implements the expected
069: * metadata interface.
070: *
071: * @since 2.4
072: */
073: protected MetadataEntity(final Object source)
074: throws ClassCastException {
075: super (source);
076: }
077:
078: /**
079: * Returns the metadata standard implemented by subclasses,
080: * which is {@linkplain MetadataStandard#ISO_19115 ISO 19115}.
081: *
082: * @since 2.4
083: */
084: public MetadataStandard getStandard() {
085: return MetadataStandard.ISO_19115;
086: }
087:
088: /**
089: * Returns an unmodifiable copy of the the specified object. This method is used for
090: * implementation of {@link #freeze} method by subclasses. This method performs the
091: * following heuristic tests:<br>
092: *
093: * <ul>
094: * <li>If the specified object is an instance of {@code MetadataEntity}, then
095: * {@link #unmodifiable()} is invoked on this object.</li>
096: * <li>Otherwise, if the object is a {@linkplain Cloneable cloneable}
097: * {@linkplain Collection collection}, then the collection is cloned and all its
098: * elements are replaced by their unmodifiable variant. If the collection is not
099: * cloneable, then it is assumed immutable and returned unchanged.</li>
100: * <li>Otherwise, the object is assumed immutable and returned unchanged.</li>
101: * </ul>
102: *
103: * @param object The object to convert in an immutable one.
104: * @return A presumed immutable view of the specified object.
105: *
106: * @deprecated No longuer needed, since {@link #freeze} is now implemented
107: * in the general case using Java reflections.
108: */
109: protected static Object unmodifiable(final Object object) {
110: /*
111: * CASE 1 - The object is an implementation of MetadataEntity. It may have
112: * its own algorithm for creating an unmodifiable view of metadata.
113: */
114: if (object instanceof MetadataEntity) {
115: return ((MetadataEntity) object).unmodifiable();
116: }
117: /*
118: * CASE 2 - The object is a collection. If the collection is not cloneable, it is assumed
119: * immutable and returned unchanged. Otherwise, the collection is cloned and all
120: * elements are replaced by their unmodifiable variant.
121: */
122: if (object instanceof Collection) {
123: Collection collection = (Collection) object;
124: if (collection.isEmpty()) {
125: return null;
126: }
127: if (collection instanceof Cloneable) {
128: collection = (Collection) ((Cloneable) collection)
129: .clone();
130: final List buffer;
131: if (collection instanceof List) {
132: // If the collection is an array list, we will update the element in place...
133: buffer = (List) collection;
134: } else {
135: // ...otherwise, we will copy them in a temporary buffer.
136: buffer = new ArrayList(collection.size());
137: }
138: int index = 0;
139: boolean refill = false;
140: for (final Iterator it = collection.iterator(); it
141: .hasNext(); index++) {
142: final Object original = it.next();
143: final Object copy = unmodifiable(original);
144: final boolean changed = (original != copy);
145: if (buffer == collection) {
146: if (changed) {
147: buffer.set(index, copy);
148: }
149: } else {
150: buffer.add(copy);
151: refill |= changed;
152: }
153: }
154: if (refill) {
155: collection.clear();
156: collection.addAll(buffer);
157: }
158: if (collection instanceof List) {
159: return Collections
160: .unmodifiableList((List) collection);
161: }
162: if (collection instanceof Set) {
163: return Collections
164: .unmodifiableSet((Set) collection);
165: }
166: return Collections.unmodifiableCollection(collection);
167: }
168: return collection;
169: }
170: /*
171: * CASE 3 - The object is a map. If the map is cloneable, then copy all
172: * entries in a new map.
173: */
174: if (object instanceof Map) {
175: final Map map = (Map) object;
176: if (map.isEmpty()) {
177: return null;
178: }
179: if (map instanceof Cloneable) {
180: final Map copy = (Map) ((Cloneable) map).clone();
181: copy.clear(); // The clone was for constructing an instance of the same class.
182: for (final Iterator it = map.entrySet().iterator(); it
183: .hasNext();) {
184: final Map.Entry entry = (Map.Entry) it.next();
185: copy.put(unmodifiable(entry.getKey()),
186: unmodifiable(entry.getValue()));
187: }
188: return Collections.unmodifiableMap(copy);
189: }
190: return map;
191: }
192: /*
193: * CASE 4 - Any other case. The object is assumed immutable and returned unchanged.
194: */
195: return object;
196: }
197:
198: /**
199: * Makes sure that an argument is non-null. This is used for checking if
200: * a mandatory attribute is presents.
201: *
202: * @param name Argument name.
203: * @param object User argument.
204: * @throws InvalidMetadataException if {@code object} is null.
205: *
206: * @since 2.4
207: */
208: protected static void ensureNonNull(final String name,
209: final Object object) throws InvalidMetadataException {
210: if (object == null) {
211: throw new InvalidMetadataException(Errors.format(
212: ErrorKeys.NULL_ATTRIBUTE_$1, name));
213: }
214: }
215:
216: /**
217: * Add a line separator to the given buffer, except if the buffer is empty.
218: * This convenience method is used for {@link #toString} implementations.
219: *
220: * @deprecated Not needed anymore since we now inherit a default {@link #toString}
221: * method for all metadata.
222: */
223: protected static void appendLineSeparator(final StringBuffer buffer) {
224: if (buffer.length() != 0) {
225: buffer.append(System.getProperty("line.separator", "\n"));
226: }
227: }
228: }
|