001: /*
002: * GeoTools - OpenSource mapping toolkit
003: * http://geotools.org
004: * (C) 2002-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.feature;
017:
018: import java.net.URI;
019: import java.util.Collections;
020: import java.util.HashSet;
021: import java.util.Map;
022: import java.util.Set;
023:
024: import org.geotools.factory.CommonFactoryFinder;
025: import org.geotools.factory.Factory;
026: import org.geotools.factory.FactoryConfigurationError;
027: import org.geotools.factory.GeoTools;
028: import org.geotools.factory.Hints;
029:
030: /**
031: * A schema builder, because FeatureTypes are meant to be immutable, this
032: * object is mutable.
033: *
034: * <p>
035: * The basic idea for usage is that you configure the builder to whatever state
036: * is desired, setting properties and adding AttributeTypes. When the desired
037: * state is acheived, the expected FeatureType can be retrieved by calling<br>
038: * <code>getFeatureType()</code>
039: * </p>
040: * <p>
041: * Repeated calls to getFeatureType will return the <i>same</i> FeatureType
042: * given that no calls which modify the state of the factory are made.
043: * </p>
044: *
045: * <p>
046: * Here's an example of how to use this:
047: * <code><pre>
048: * FeatureTypeBuilder build = FeatureTypeFactory.newInstance();
049: * build.addType(...);
050: * build.setName(...);
051: * build.setNamespace(...);
052: * FeatureType type = build.getFeatureType();
053: * </pre></code>
054: * There are also a set of convenience methods for creation of FeatureTypes.
055: * These are the various newFeatureType methods.
056: * </p>
057: *
058: * </p>
059: *
060: * @author Ian Schneider
061: * @source $URL: http://svn.geotools.org/geotools/tags/2.4.1/modules/library/main/src/main/java/org/geotools/feature/FeatureTypeBuilder.java $
062: * @version $Id: FeatureTypeBuilder.java 25008 2007-04-04 02:17:19Z jgarnett $
063: */
064: public abstract class FeatureTypeBuilder extends FeatureTypes implements
065: Factory {
066: /** The types that all features have. */
067: private static Set builtInTypes = null;
068:
069: /** If the base types have been initialized */
070: private static boolean initialized;
071:
072: /** The name to give the FeatureType to be created. */
073: private String name;
074:
075: /** The namespace to give the FeatureType to be created. */
076: private URI namespace;
077:
078: /** If something in the factory has changed. */
079: private boolean dirty = true;
080:
081: /** The type created. */
082: private FeatureType type = null;
083:
084: /** The current defaultGeometry of the FeatureType returned. */
085: private GeometryAttributeType defaultGeometry = null;
086:
087: /** If the type is abstract. */
088: private boolean abstractType = false;
089:
090: /** The types that this is derived from. */
091: private java.util.Set super Types;
092:
093: /**
094: * Implementation hints - since this is a builder all
095: * hints are passed onto the FeatureType.
096: */
097: Map hints;
098:
099: /**
100: * An empty public constructor. Subclasses should not provide a
101: * constructor.
102: * @deprecated
103: */
104: public FeatureTypeBuilder() {
105: this (Collections.EMPTY_MAP);
106: }
107:
108: /**
109: * An empty public constructor. Subclasses should not provide a
110: * constructor.
111: */
112: public FeatureTypeBuilder(Map hints) {
113: this .hints = hints;
114: }
115:
116: /**
117: * Returns the implementation hints. The default implementation returns en empty map.
118: * <p>
119: * Since the building of a FeatureType involves the collaboration of may
120: * Factory classes (that may be discovered over the course of the build process)
121: * we are forced to indicate that *all* hints are used.
122: * </p>
123: * <p>
124: * Strictly this is a Builder (not a factory) and has no need declair which
125: * hints are used (as one can never *keep* this builder in a factory registery.
126: * (It is stateful and cannot be used concurrently for example).
127: */
128: public Map getImplementationHints() {
129: return hints;
130: }
131:
132: /**
133: * Create a new FeatureTypeFactory with the given typeName.
134: *
135: * @param name The typeName of the feature to create.
136: *
137: * @return A new FeatureTypeFactory instance.
138: *
139: * @throws FactoryConfigurationError If there exists a configuration error.
140: */
141: public static FeatureTypeFactory newInstance(String typeName)
142: throws FactoryConfigurationError {
143:
144: // warning not sure if CommonFactoryFinder is going to cache the instance or not?
145: //
146: Hints hints = GeoTools.getDefaultHints();
147: if (hints == null) {
148: hints = new Hints(Hints.FEATURE_TYPE_FACTORY_NAME, typeName);
149: } else {
150: hints.put(Hints.FEATURE_TYPE_FACTORY_NAME, typeName);
151: }
152: hints.put(Hints.FEATURE_TYPE_FACTORY_NAME, typeName);
153: return CommonFactoryFinder.getFeatureTypeFactory(hints);
154: }
155:
156: /**
157: * Import all of the AttributeTypes from the given FeatureType into this
158: * factory.
159: *
160: * <p>
161: * If strict is true, non-uniquely named AttributeTypes will throw an
162: * exception.
163: * </p>
164: *
165: * <p>
166: * If strict is false, these will be silently ignored, but not added.
167: * </p>
168: *
169: * <p>
170: * No other information is imported.
171: * </p>
172: *
173: * @param type The FeatureType to import from.
174: * @param strict Enforce namespace restrictions.
175: *
176: * @throws IllegalArgumentException If strict is true and there are naming
177: * problems.
178: */
179: public void importType(FeatureType type, boolean strict)
180: throws IllegalArgumentException {
181: for (int i = 0, ii = type.getAttributeCount(); i < ii; i++) {
182: try {
183: addType(type.getAttributeType(i));
184: } catch (IllegalArgumentException iae) {
185: if (strict) {
186: throw iae;
187: }
188: }
189: }
190: }
191:
192: /**
193: * Set the super types of this factory. The types will be copied into a
194: * Set.
195: *
196: * @param types A Collection of types.
197: */
198: public final void setSuperTypes(java.util.Collection types) {
199: super Types = new java.util.LinkedHashSet(types);
200: }
201:
202: /**
203: * Obtain the super types of this factory. Any user types will be appended
204: * to the built in types of this factory.
205: *
206: * @return A Collection representing the super types of the FeatureType
207: * this factory will create.
208: */
209: public final java.util.Collection getSuperTypes() {
210: Set super s = (super Types == null) ? new HashSet() : super Types;
211: Set builtin = getBuiltinTypes();
212:
213: if (builtin != null) {
214: super s.addAll(builtin);
215: }
216:
217: return super s;
218: }
219:
220: /**
221: * A convienence method for importing AttributeTypes, simply calls<br>
222: * <code> importType(type,false) </code>
223: *
224: * @param type The type to import.
225: */
226: public void importType(FeatureType type) {
227: importType(type, false);
228: }
229:
230: /**
231: * Set the name of the FeatureType this factory will produce.
232: *
233: * @param name The new name. May be null.
234: */
235: public void setName(String name) {
236: dirty |= isDifferent(name, this .name);
237: this .name = name;
238: }
239:
240: /**
241: * Get the current configuration of the name of this factory.
242: *
243: * @return The current name. May be null.
244: */
245: public final String getName() {
246: return name;
247: }
248:
249: /**
250: * Set the namespace of the FeatureType this factory will produce.
251: *
252: * @param namespace The new namespace. May be null.
253: */
254: public void setNamespace(URI namespace) {
255: dirty |= isDifferent(namespace, this .namespace);
256: this .namespace = namespace;
257: }
258:
259: /**
260: * Get the current configuration of the namespace of this factory.
261: *
262: * @return The current namespace. May be null.
263: */
264: public final URI getNamespace() {
265: return namespace;
266: }
267:
268: /**
269: * Is this factory configured to be abstract?
270: *
271: * @return True if it is, false if it aint.
272: */
273: public final boolean isAbstract() {
274: return abstractType;
275: }
276:
277: /**
278: * Configure this factory to produce an abstract type.
279: *
280: * @param a True or false.
281: */
282: public final void setAbstract(boolean a) {
283: dirty = true;
284: this .abstractType = a;
285: }
286:
287: private boolean isDifferent(String s1, String s2) {
288: if (s1 != null) {
289: return !s1.equals(s2);
290: }
291:
292: if (s2 != null) {
293: return !s2.equals(s1);
294: }
295:
296: return s1 != s2;
297: }
298:
299: private boolean isDifferent(URI u1, URI u2) {
300: if (u1 != null) {
301: return !u1.equals(u2);
302: }
303:
304: if (u2 != null) {
305: return !u2.equals(u1);
306: }
307:
308: return u1 != u2;
309: }
310:
311: /**
312: * Remove all the AttributeTypes in this factory.
313: */
314: public final void removeAll() {
315: int cnt = getAttributeCount();
316:
317: for (int i = cnt; i > 0; i++) {
318: removeType(i - 1);
319: }
320: }
321:
322: /**
323: * Add an array of AttributeTypes to this factory.
324: *
325: * @param types The types or a null array.
326: *
327: * @throws NullPointerException If any of the types are null.
328: * @throws IllegalArgumentException If there are naming problems.
329: */
330: public final void addTypes(AttributeType[] types)
331: throws NullPointerException, IllegalArgumentException {
332: if (types == null) {
333: return;
334: }
335:
336: for (int i = 0; i < types.length; i++) {
337: addType(types[i]);
338: }
339: }
340:
341: /**
342: * A the given AttributeType to this factory.
343: *
344: * @param type The type to add.
345: *
346: * @throws NullPointerException If the type is null.
347: * @throws IllegalArgumentException If another type exists with the same
348: * name.
349: */
350: public final void addType(AttributeType type)
351: throws NullPointerException, IllegalArgumentException {
352: if (type == null) {
353: throw new NullPointerException("type");
354: }
355:
356: dirty = true;
357: check(type);
358: add(type);
359: }
360:
361: /**
362: * Remove the given type from this factory.
363: *
364: * @param type The type to remove.
365: *
366: * @throws NullPointerException If the type is null.
367: */
368: public final void removeType(AttributeType type)
369: throws NullPointerException {
370: if (type == null) {
371: throw new NullPointerException("type");
372: }
373:
374: dirty = true;
375:
376: AttributeType removed = remove(type);
377:
378: if (removed == defaultGeometry) {
379: defaultGeometry = null;
380: }
381: }
382:
383: /**
384: * Insert the given type at the index specified.
385: *
386: * @param idx The index to insert at.
387: * @param type The AttributeType to insert.
388: *
389: * @throws NullPointerException If the type is null.
390: * @throws IllegalArgumentException If the AttributeType is not allowed.
391: * @throws ArrayIndexOutOfBoundsException If the index is out of range.
392: */
393: public final void addType(int idx, AttributeType type)
394: throws NullPointerException, IllegalArgumentException,
395: ArrayIndexOutOfBoundsException {
396: if (type == null) {
397: throw new NullPointerException("type");
398: }
399:
400: dirty = true;
401: check(type);
402: add(idx, type);
403: }
404:
405: /**
406: * Remove the AttributeType at the given index.
407: *
408: * @param idx The index to remove at.
409: *
410: * @throws ArrayIndexOutOfBoundsException If the index is out of bounds.
411: */
412: public final void removeType(int idx)
413: throws ArrayIndexOutOfBoundsException {
414: dirty = true;
415:
416: AttributeType removed = remove(idx);
417:
418: if (removed == defaultGeometry) {
419: defaultGeometry = null;
420: }
421: }
422:
423: /**
424: * Set the AttributeType at the given index. Overwrites the existing type.
425: *
426: * @param idx The index to use.
427: * @param type The type to use.
428: *
429: * @throws IllegalArgumentException If the type is not good.
430: * @throws NullPointerException if they type passed in is null
431: * @throws ArrayIndexOutOfBoundsException if the index is out of bounds.
432: */
433: public final void setType(int idx, AttributeType type)
434: throws IllegalArgumentException, NullPointerException,
435: ArrayIndexOutOfBoundsException {
436: if (type == null) {
437: throw new NullPointerException("type");
438: }
439:
440: dirty = true;
441: check(type);
442:
443: AttributeType removed = set(idx, type);
444:
445: if (removed == defaultGeometry) {
446: defaultGeometry = null;
447: }
448: }
449:
450: /**
451: * Swap the AttributeTypes at the given locations.
452: *
453: * @param idx1 The index of the first.
454: * @param idx2 The index of the second.
455: *
456: * @throws ArrayIndexOutOfBoundsException if either index is not in the
457: * array bounds.
458: */
459: public final void swap(int idx1, int idx2)
460: throws ArrayIndexOutOfBoundsException {
461: // implementation note:
462: // we must rely on the subclass implementation, which, hopefully does
463: // not do any checking. If we used setType, there is a name overlap.
464: AttributeType tmp = get(idx1);
465: set(idx1, get(idx2));
466: set(idx2, tmp);
467:
468: // must do this!
469: dirty = true;
470: }
471:
472: /**
473: * Return the AttributeType currently used as the defaultGeometry property
474: * for the FeatureType this factory will create.
475: *
476: * @return The AttributeType representing the defaultGeometry or null.
477: */
478: public final GeometryAttributeType getDefaultGeometry() {
479: return defaultGeometry;
480: }
481:
482: /**
483: * Sets the defaultGeometry of this factory. If the defaultGeometry
484: * AttributeType does not exist as an AttributeType within this factory,
485: * it is added. This will overwrite the existing defaultGeometry, yet not
486: * remove it from the existing AttributeTypes.
487: *
488: * @param defaultGeometry The AttributeType to use as the defaultGeometry.
489: * May be null.
490: *
491: * @throws IllegalArgumentException if the type is not a geometry.
492: */
493: public final void setDefaultGeometry(
494: GeometryAttributeType defaultGeometry)
495: throws IllegalArgumentException {
496: // check if Geometry
497: if ((defaultGeometry != null) && !defaultGeometry.isGeometry()) {
498: String mess = "Attempted to set a non-geometry type as "
499: + "defaultGeometry: ";
500: throw new IllegalArgumentException(mess + defaultGeometry);
501: }
502:
503: dirty = true; // do this!
504: this .defaultGeometry = defaultGeometry;
505:
506: // if the defaultGeometry hasn't been added, add it!
507: if ((defaultGeometry != null) && !contains(defaultGeometry)) {
508: addType(defaultGeometry);
509: }
510: }
511:
512: /**
513: * Get a FeatureType which reflects the state of this factory. Any
514: * modifications to the state of the factory (adding, removing, or
515: * reordering any AttributeTypes or changing any other properties -
516: * isNillable,name,etc.), will cause the factory to "retool" itself. If
517: * the factory has not changed since a call to this method, the return
518: * value will be the same FeatureType which the previous method returned.
519: * Otherwise, a new FeatureType will be created.
520: *
521: * @return The featureType reflecting the current factory state.
522: *
523: * @throws SchemaException if name is null or blank
524: */
525: public final FeatureType getFeatureType() throws SchemaException {
526: // we're dirty, recreate the FeatureType
527: if (dirty || (type == null)) {
528: // no defaultGeometry assigned, search for one.
529: if (defaultGeometry == null) {
530: for (int i = 0, ii = getAttributeCount(); i < ii; i++) {
531: if (get(i) instanceof GeometryAttributeType) {
532: defaultGeometry = (GeometryAttributeType) get(i);
533: break;
534: }
535: }
536: }
537:
538: if ((name == null) || (name.trim().length() == 0)) {
539: throw new SchemaException(
540: "Cannot create FeatureType with null or blank name");
541: }
542:
543: type = createFeatureType();
544:
545: // oops, the subclass messed up...
546: if (type == null) {
547: throw new NullPointerException(getClass().getName()
548: + ".createFeatureType()");
549: }
550:
551: if (isAbstract() && !type.isAbstract()) {
552: throw new RuntimeException(
553: "FeatureTypeFactory poorly implemented, "
554: + "expected abstract type, received "
555: + type);
556: }
557:
558: // not dirty anymore.
559: dirty = false;
560: }
561:
562: return type;
563: }
564:
565: /**
566: * Returns a string representation of this factory.
567: *
568: * @return The string representing this factory.
569: */
570: public String toString() {
571: String types = "";
572:
573: for (int i = 0, ii = getAttributeCount(); i < ii; i++) {
574: types += get(i);
575:
576: if (i < ii) {
577: types += " , ";
578: }
579: }
580:
581: return "FeatureTypeFactory(" + getClass().getName() + ") [ "
582: + types + " ]";
583: }
584:
585: /**
586: * Check to see if this factory contains the given AttributeType. The
587: * comparison is done by name.
588: *
589: * @param type The AttributeType to search for by name.
590: *
591: * @return <tt>true</tt> if a like-named AttributeType exists,
592: * <tt>false</tt> otherwise.
593: */
594: public final boolean contains(AttributeType type) {
595: for (int i = 0, ii = getAttributeCount(); i < ii; i++) {
596: if (get(i).getName().equals(type.getName())) {
597: return true;
598: }
599: }
600:
601: return false;
602: }
603:
604: /**
605: * Checks to see if this factory already contains the type.
606: *
607: * @param type
608: *
609: * @throws IllegalArgumentException DOCUMENT ME!
610: */
611: protected void check(AttributeType type) {
612: if (contains(type)) {
613: throw new IllegalArgumentException(
614: "Duplicate AttributeTypes " + type);
615: }
616: }
617:
618: protected final Set getBuiltinTypes() {
619: if ((builtInTypes == null) && !initialized) {
620: builtInTypes = new HashSet();
621:
622: try {
623: builtInTypes.add(FeatureTypes.newFeatureType(null,
624: "Feature",
625: new URI("http://www.opengis.net/gml"), true));
626: initialized = true;
627: } catch (Exception e) {
628: throw new RuntimeException(e);
629: }
630:
631: addBaseTypes(builtInTypes);
632: }
633:
634: return builtInTypes;
635: }
636:
637: protected void addBaseTypes(Set types) {
638: // base class hook
639: }
640:
641: /**
642: * DOCUMENT ME!
643: *
644: */
645: protected abstract FeatureType createFeatureType()
646: throws SchemaException;
647:
648: /**
649: * DOCUMENT ME!
650: *
651: * @param type
652: *
653: * @throws IllegalArgumentException
654: */
655: protected abstract void add(AttributeType type)
656: throws IllegalArgumentException;
657:
658: /**
659: * DOCUMENT ME!
660: *
661: * @param type
662: *
663: */
664: protected abstract AttributeType remove(AttributeType type);
665:
666: /**
667: * DOCUMENT ME!
668: *
669: * @param idx
670: * @param type
671: *
672: * @throws ArrayIndexOutOfBoundsException
673: * @throws IllegalArgumentException
674: */
675: protected abstract void add(int idx, AttributeType type)
676: throws ArrayIndexOutOfBoundsException,
677: IllegalArgumentException;
678:
679: /**
680: * DOCUMENT ME!
681: *
682: * @param idx
683: *
684: *
685: * @throws ArrayIndexOutOfBoundsException
686: */
687: protected abstract AttributeType remove(int idx)
688: throws ArrayIndexOutOfBoundsException;
689:
690: /**
691: * DOCUMENT ME!
692: *
693: * @param idx
694: *
695: *
696: * @throws ArrayIndexOutOfBoundsException
697: */
698: public abstract AttributeType get(int idx)
699: throws ArrayIndexOutOfBoundsException;
700:
701: /**
702: * DOCUMENT ME!
703: *
704: * @param idx
705: * @param type
706: *
707: *
708: * @throws ArrayIndexOutOfBoundsException
709: * @throws IllegalArgumentException
710: */
711: protected abstract AttributeType set(int idx, AttributeType type)
712: throws ArrayIndexOutOfBoundsException,
713: IllegalArgumentException;
714:
715: /**
716: * DOCUMENT ME!
717: *
718: */
719: public abstract int getAttributeCount();
720:
721: }
|