/* * Geotools2 - OpenSource mapping toolkit * http://geotools.org * (C) 2002, Geotools Project Managment Committee (PMC) * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * */ package org.geotools.feature; import org.geotools.factory.Factory; import org.geotools.factory.FactoryConfigurationError; import org.geotools.factory.FactoryFinder; import java.util.Arrays; import java.util.HashSet; import java.util.Set; /** * A factory patterned object for the creation of FeatureTypes. Because * FeatureTypes are meant to be immutable, this object is mutable. * *

* The basic idea for usage is that you configure the factory to whatever state * is desired, setting properties and adding AttributeTypes. When the desired * state is acheived, the expected FeatureType can be retrieved by calling
* getFeatureType()
* Repeated calls to getFeatureType will return the same FeatureType * given that no calls which modify the state of the factory are made. *

* *

* Here's an example of how to use this: *

 
 * FeatureTypeFactory factory = FeatureTypeFactory.newInstance();  
 * factory.addType(...);
 * factory.setName(...);  
 * factory.setNamespace(...);  
 * FeatureType type = factory.getFeatureType(); 
 * 
There are also a set of convenience * methods for creation of FeatureTypes. These are the various newFeatureType * methods. *

* *

* Remember, changes to any state will invalidate the current FeatureType, * if one exists
* This class is not thread-safe. *

* * @author Ian Schneider * @version $Id: FeatureTypeFactory.java,v 1.12 2004/02/21 10:16:50 aaime Exp $ */ public abstract class FeatureTypeFactory implements Factory { /** The types that all features have. */ private static Set builtInTypes = null; /** If the base types have been initialized */ private static boolean initialized; /** The name to give the FeatureType to be created. */ private String name; /** The namespace to give the FeatureType to be created. */ private String namespace; /** If something in the factory has changed. */ private boolean dirty = true; /** The type created. */ private FeatureType type = null; /** The current defaultGeometry of the FeatureType returned. */ private GeometryAttributeType defaultGeometry = null; /** If the type is abstract. */ private boolean abstractType = false; /** The types that this is derived from. */ private java.util.Set superTypes; /** * An empty public constructor. Subclasses should not provide a * constructor. */ public FeatureTypeFactory() { } /** * Create a FeatureTypeFactory which contains all of the AttributeTypes * from the given FeatureType. This is simply a convenience method for
*
     * FeatureTypeFactory factory = FeatureTypeFactory.newInstace();
     * factory.importType(yourTypeHere); 
     * factory.setName(original.getName());
     * factory.setNamespace(original.getNamespace());
     * factory.setNillable(original.isNillable());
     * factory.setDefaultGeometry(original.getDefaultGeometry()); 
     * 
* * @param original The FeatureType to obtain information from. * * @return A new FeatureTypeFactory which is initialized with the state of * the original FeatureType. * * @throws FactoryConfigurationError If a FeatureTypeFactory cannot be * found. */ public static FeatureTypeFactory createTemplate(FeatureType original) throws FactoryConfigurationError { FeatureTypeFactory factory = newInstance(original.getTypeName()); factory.importType(original); factory.setNamespace(original.getNamespace()); factory.setDefaultGeometry(original.getDefaultGeometry()); FeatureType[] ancestors = original.getAncestors(); if (ancestors != null) { factory.setSuperTypes(Arrays.asList(ancestors)); } return factory; } /** * The most specific way to create a new FeatureType. * * @param types The AttributeTypes to create the FeatureType with. * @param name The typeName of the FeatureType. Required, may not be null. * @param ns The namespace of the FeatureType. Optional, may be null. * @param isAbstract True if this created type should be abstract. * @param superTypes A Collection of types the FeatureType will inherit * from. Currently, all types inherit from feature in the opengis * namespace. * * @return A new FeatureType created from the given arguments. * * @throws FactoryConfigurationError If there are problems creating a * factory. * @throws SchemaException If the AttributeTypes provided are invalid in * some way. */ public static FeatureType newFeatureType(AttributeType[] types, String name, String ns, boolean isAbstract, FeatureType[] superTypes) throws FactoryConfigurationError, SchemaException { return newFeatureType(types, name, ns, isAbstract, superTypes, null); } /** * The most specific way to create a new FeatureType. * * @param types The AttributeTypes to create the FeatureType with. * @param name The typeName of the FeatureType. Required, may not be null. * @param ns The namespace of the FeatureType. Optional, may be null. * @param isAbstract True if this created type should be abstract. * @param superTypes A Collection of types the FeatureType will inherit * from. Currently, all types inherit from feature in the opengis * namespace. * * @return A new FeatureType created from the given arguments. * * @throws FactoryConfigurationError If there are problems creating a * factory. * @throws SchemaException If the AttributeTypes provided are invalid in * some way. */ public static FeatureType newFeatureType(AttributeType[] types, String name, String ns, boolean isAbstract, FeatureType[] superTypes, AttributeType defaultGeometry) throws FactoryConfigurationError, SchemaException { FeatureTypeFactory factory = newInstance(name); factory.addTypes(types); factory.setNamespace(ns); factory.setAbstract(isAbstract); if(defaultGeometry != null) factory.setDefaultGeometry((GeometryAttributeType) defaultGeometry); if (superTypes != null) { factory.setSuperTypes(Arrays.asList(superTypes)); } return factory.getFeatureType(); } /** * The most specific way to create a new FeatureType. * * @param types The AttributeTypes to create the FeatureType with. * @param name The typeName of the FeatureType. Required, may not be null. * @param ns The namespace of the FeatureType. Optional, may be null. * @param isAbstract True if this created type should be abstract. * @param superTypes A Collection of types the FeatureType will inherit * from. Currently, all types inherit from feature in the opengis * namespace. * * @return A new FeatureType created from the given arguments. * * @throws FactoryConfigurationError If there are problems creating a * factory. * @throws SchemaException If the AttributeTypes provided are invalid in * some way. */ public static FeatureType newFeatureType(AttributeType[] types, String name, String ns, boolean isAbstract, FeatureType[] superTypes, GeometryAttributeType defaultGeometry) throws FactoryConfigurationError, SchemaException { FeatureTypeFactory factory = newInstance(name); factory.addTypes(types); factory.setNamespace(ns); factory.setAbstract(isAbstract); if (superTypes != null) { factory.setSuperTypes(Arrays.asList(superTypes)); } if(defaultGeometry != null) { factory.setDefaultGeometry(defaultGeometry); } return factory.getFeatureType(); } /** * Create a new FeatureType with the given AttributeTypes. A short cut for * calling newFeatureType(types,name,ns,isAbstract,null). * * @param types The AttributeTypes to create the FeatureType with. * @param name The typeName of the FeatureType. Required, may not be null. * @param ns The namespace of the FeatureType. Optional, may be null. * @param isAbstract True if this created type should be abstract. * * @return A new FeatureType created from the given arguments. * * @throws FactoryConfigurationError If there are problems creating a * factory. * @throws SchemaException If the AttributeTypes provided are invalid in * some way. */ public static FeatureType newFeatureType(AttributeType[] types, String name, String ns, boolean isAbstract) throws FactoryConfigurationError, SchemaException { return newFeatureType(types, name, ns, isAbstract, null); } /** * Create a new FeatureType with the given AttributeTypes. A short cut for * calling newFeatureType(types,name,ns,false,null). * * @param types The AttributeTypes to create the FeatureType with. * @param name The typeName of the FeatureType. Required, may not be null. * @param ns The namespace of the FeatureType. Optional, may be null. * * @return A new FeatureType created from the given arguments. * * @throws FactoryConfigurationError If there are problems creating a * factory. * @throws SchemaException If the AttributeTypes provided are invalid in * some way. */ public static FeatureType newFeatureType(AttributeType[] types, String name, String ns) throws FactoryConfigurationError, SchemaException { return newFeatureType(types, name, ns, false); } /** * Create a new FeatureType with the given AttributeTypes. A short cut for * calling newFeatureType(types,name,null,false,null). Useful * for test cases or datasources which may not allow a namespace. * * @param types The AttributeTypes to create the FeatureType with. * @param name The typeName of the FeatureType. Required, may not be null. * * @return A new FeatureType created from the given arguments. * * @throws FactoryConfigurationError If there are problems creating a * factory. * @throws SchemaException If the AttributeTypes provided are invalid in * some way. */ public static FeatureType newFeatureType(AttributeType[] types, String name) throws FactoryConfigurationError, SchemaException { return newFeatureType(types, name, null, false); } /** * Create a new FeatureTypeFactory with the given typeName. * * @param name The typeName of the feature to create. * * @return A new FeatureTypeFactory instance. * * @throws FactoryConfigurationError If there exists a configuration error. */ public static FeatureTypeFactory newInstance(String name) throws FactoryConfigurationError { FeatureTypeFactory factory = (FeatureTypeFactory) FactoryFinder .findFactory("org.geotools.feature.FeatureTypeFactory", "org.geotools.feature.DefaultFeatureTypeFactory"); factory.setName(name); return factory; } /** * Import all of the AttributeTypes from the given FeatureType into this * factory. * *

* If strict is true, non-uniquely named AttributeTypes will throw an * exception. *

* *

* If strict is false, these will be silently ignored, but not added. *

* *

* No other information is imported. *

* * @param type The FeatureType to import from. * @param strict Enforce namespace restrictions. * * @throws IllegalArgumentException If strict is true and there are naming * problems. */ public void importType(FeatureType type, boolean strict) throws IllegalArgumentException { for (int i = 0, ii = type.getAttributeCount(); i < ii; i++) { try { addType(type.getAttributeType(i)); } catch (IllegalArgumentException iae) { if (strict) { throw iae; } } } } /** * Set the super types of this factory. The types will be copied into a * Set. * * @param types A Collection of types. */ public final void setSuperTypes(java.util.Collection types) { superTypes = new java.util.LinkedHashSet(types); } /** * Obtain the super types of this factory. Any user types will be appended * to the built in types of this factory. * * @return A Collection representing the super types of the FeatureType * this factory will create. */ public final java.util.Collection getSuperTypes() { Set supers = (superTypes == null) ? new HashSet() : superTypes; Set builtin = getBuiltinTypes(); if (builtin != null) { supers.addAll(builtin); } return supers; } /** * A convienence method for importing AttributeTypes, simply calls
* importType(type,false) * * @param type The type to import. */ public void importType(FeatureType type) { importType(type, false); } /** * Set the name of the FeatureType this factory will produce. * * @param name The new name. May be null. */ public void setName(String name) { dirty |= isDifferent(name, this.name); this.name = name; } /** * Get the current configuration of the name of this factory. * * @return The current name. May be null. */ public final String getName() { return name; } /** * Set the namespace of the FeatureType this factory will produce. * * @param namespace The new namespace. May be null. */ public void setNamespace(String namespace) { dirty |= isDifferent(namespace, this.namespace); this.namespace = namespace; } /** * Get the current configuration of the namespace of this factory. * * @return The current namespace. May be null. */ public final String getNamespace() { return namespace; } /** * Is this factory configured to be abstract? * * @return True if it is, false if it aint. */ public final boolean isAbstract() { return abstractType; } /** * Configure this factory to produce an abstract type. * * @param a True or false. */ public final void setAbstract(boolean a) { dirty = true; this.abstractType = a; } private boolean isDifferent(String s1, String s2) { if (s1 != null) { return !s1.equals(s2); } if (s2 != null) { return !s2.equals(s1); } return s1 != s2; } /** * Remove all the AttributeTypes in this factory. */ public final void removeAll() { int cnt = getAttributeCount(); for (int i = cnt; i > 0; i++) { removeType(i - 1); } } /** * Add an array of AttributeTypes to this factory. * * @param types The types or a null array. * * @throws NullPointerException If any of the types are null. * @throws IllegalArgumentException If there are naming problems. */ public final void addTypes(AttributeType[] types) throws NullPointerException, IllegalArgumentException { if (types == null) { return; } for (int i = 0; i < types.length; i++) { addType(types[i]); } } /** * A the given AttributeType to this factory. * * @param type The type to add. * * @throws NullPointerException If the type is null. * @throws IllegalArgumentException If another type exists with the same * name. */ public final void addType(AttributeType type) throws NullPointerException, IllegalArgumentException { if (type == null) { throw new NullPointerException("type"); } dirty = true; check(type); add(type); } /** * Remove the given type from this factory. * * @param type The type to remove. * * @throws NullPointerException If the type is null. */ public final void removeType(AttributeType type) throws NullPointerException { if (type == null) { throw new NullPointerException("type"); } dirty = true; AttributeType removed = remove(type); if (removed == defaultGeometry) { defaultGeometry = null; } } /** * Insert the given type at the index specified. * * @param idx The index to insert at. * @param type The AttributeType to insert. * * @throws NullPointerException If the type is null. * @throws IllegalArgumentException If the AttributeType is not allowed. * @throws ArrayIndexOutOfBoundsException If the index is out of range. */ public final void addType(int idx, AttributeType type) throws NullPointerException, IllegalArgumentException, ArrayIndexOutOfBoundsException { if (type == null) { throw new NullPointerException("type"); } dirty = true; check(type); add(idx, type); } /** * Remove the AttributeType at the given index. * * @param idx The index to remove at. * * @throws ArrayIndexOutOfBoundsException If the index is out of bounds. */ public final void removeType(int idx) throws ArrayIndexOutOfBoundsException { dirty = true; AttributeType removed = remove(idx); if (removed == defaultGeometry) { defaultGeometry = null; } } /** * Set the AttributeType at the given index. Overwrites the existing type. * * @param idx The index to use. * @param type The type to use. * * @throws IllegalArgumentException If the type is not good. * @throws NullPointerException if they type passed in is null * @throws ArrayIndexOutOfBoundsException if the index is out of bounds. */ public final void setType(int idx, AttributeType type) throws IllegalArgumentException, NullPointerException, ArrayIndexOutOfBoundsException { if (type == null) { throw new NullPointerException("type"); } dirty = true; check(type); AttributeType removed = set(idx, type); if (removed == defaultGeometry) { defaultGeometry = null; } } /** * Swap the AttributeTypes at the given locations. * * @param idx1 The index of the first. * @param idx2 The index of the second. * * @throws ArrayIndexOutOfBoundsException if either index is not in the * array bounds. */ public final void swap(int idx1, int idx2) throws ArrayIndexOutOfBoundsException { // implementation note: // we must rely on the subclass implementation, which, hopefully does // not do any checking. If we used setType, there is a name overlap. AttributeType tmp = get(idx1); set(idx1, get(idx2)); set(idx2, tmp); // must do this! dirty = true; } /** * Return the AttributeType currently used as the defaultGeometry property * for the FeatureType this factory will create. * * @return The AttributeType representing the defaultGeometry or null. */ public final GeometryAttributeType getDefaultGeometry() { return defaultGeometry; } /** * Sets the defaultGeometry of this factory. If the defaultGeometry * AttributeType does not exist as an AttributeType within this factory, * it is added. This will overwrite the existing defaultGeometry, yet not * remove it from the existing AttributeTypes. * * @param defaultGeometry The AttributeType to use as the defaultGeometry. * May be null. * * @throws IllegalArgumentException if the type is not a geometry. */ public final void setDefaultGeometry(GeometryAttributeType defaultGeometry) throws IllegalArgumentException { // check if Geometry if ((defaultGeometry != null) && !defaultGeometry.isGeometry()) { String mess = "Attempted to set a non-geometry type as " + "defaultGeometry: "; throw new IllegalArgumentException(mess + defaultGeometry); } dirty = true; // do this! this.defaultGeometry = defaultGeometry; // if the defaultGeometry hasn't been added, add it! if ((defaultGeometry != null) && !contains(defaultGeometry)) { addType(defaultGeometry); } } /** * Get a FeatureType which reflects the state of this factory. Any * modifications to the state of the factory (adding, removing, or * reordering any AttributeTypes or changing any other properties - * isNillable,name,etc.), will cause the factory to "retool" itself. If * the factory has not changed since a call to this method, the return * value will be the same FeatureType which the previous method returned. * Otherwise, a new FeatureType will be created. * * @return The featureType reflecting the current factory state. * * @throws SchemaException if name is null or blank */ public final FeatureType getFeatureType() throws SchemaException { // we're dirty, recreate the FeatureType if (dirty || (type == null)) { // no defaultGeometry assigned, search for one. if (defaultGeometry == null) { for (int i = 0, ii = getAttributeCount(); i < ii; i++) { if (get(i) instanceof GeometryAttributeType) { defaultGeometry = (GeometryAttributeType) get(i); break; } } } if ((name == null) || (name.trim().length() == 0)) { throw new SchemaException( "Cannot create FeatureType with null or blank name"); } type = createFeatureType(); // oops, the subclass messed up... if (type == null) { throw new NullPointerException(getClass().getName() + ".createFeatureType()"); } if (isAbstract() && !type.isAbstract()) { throw new RuntimeException( "FeatureTypeFactory poorly implemented, " + "expected abstract type, received " + type); } // not dirty anymore. dirty = false; } return type; } /** * Returns a string representation of this factory. * * @return The string representing this factory. */ public String toString() { String types = ""; for (int i = 0, ii = getAttributeCount(); i < ii; i++) { types += get(i); if (i < ii) { types += " , "; } } return "FeatureTypeFactory(" + getClass().getName() + ") [ " + types + " ]"; } /** * Check to see if this factory contains the given AttributeType. The * comparison is done by name. * * @param type The AttributeType to search for by name. * * @return true if a like-named AttributeType exists, * false otherwise. */ public final boolean contains(AttributeType type) { for (int i = 0, ii = getAttributeCount(); i < ii; i++) { if (get(i).getName().equals(type.getName())) { return true; } } return false; } /** * Checks to see if this factory already contains the type. * * @param type * * @throws IllegalArgumentException DOCUMENT ME! */ protected void check(AttributeType type) { if (contains(type)) { throw new IllegalArgumentException("Duplicate AttributeTypes " + type); } } protected final Set getBuiltinTypes() { if ((builtInTypes == null) && !initialized) { builtInTypes = new HashSet(); try { builtInTypes.add(newFeatureType(null, "Feature", "http://www.opengis.net/gml", true)); initialized = true; } catch (Exception e) { throw new RuntimeException(e); } addBaseTypes(builtInTypes); } return builtInTypes; } protected void addBaseTypes(Set types) { // base class hook } /** * DOCUMENT ME! * * @return */ protected abstract FeatureType createFeatureType() throws SchemaException; /** * DOCUMENT ME! * * @param type * * @throws IllegalArgumentException */ protected abstract void add(AttributeType type) throws IllegalArgumentException; /** * DOCUMENT ME! * * @param type * * @return */ protected abstract AttributeType remove(AttributeType type); /** * DOCUMENT ME! * * @param idx * @param type * * @throws ArrayIndexOutOfBoundsException * @throws IllegalArgumentException */ protected abstract void add(int idx, AttributeType type) throws ArrayIndexOutOfBoundsException, IllegalArgumentException; /** * DOCUMENT ME! * * @param idx * * @return * * @throws ArrayIndexOutOfBoundsException */ protected abstract AttributeType remove(int idx) throws ArrayIndexOutOfBoundsException; /** * DOCUMENT ME! * * @param idx * * @return * * @throws ArrayIndexOutOfBoundsException */ public abstract AttributeType get(int idx) throws ArrayIndexOutOfBoundsException; /** * DOCUMENT ME! * * @param idx * @param type * * @return * * @throws ArrayIndexOutOfBoundsException * @throws IllegalArgumentException */ protected abstract AttributeType set(int idx, AttributeType type) throws ArrayIndexOutOfBoundsException, IllegalArgumentException; /** * DOCUMENT ME! * * @return */ public abstract int getAttributeCount(); }