001: package org.geotools.feature.iso.simple;
002:
003: import java.net.URI;
004: import java.net.URISyntaxException;
005: import java.util.ArrayList;
006: import java.util.HashMap;
007: import java.util.Iterator;
008: import java.util.List;
009: import java.util.Map;
010:
011: import org.geotools.feature.SchemaException;
012: import org.opengis.feature.simple.SimpleFeatureType;
013: import org.opengis.feature.simple.SimpleTypeFactory;
014: import org.opengis.feature.type.AttributeDescriptor;
015: import org.opengis.feature.type.AttributeType;
016: import org.opengis.feature.type.Name;
017:
018: import com.vividsolutions.jts.geom.Geometry;
019: import com.vividsolutions.jts.geom.GeometryCollection;
020: import com.vividsolutions.jts.geom.LineString;
021: import com.vividsolutions.jts.geom.MultiLineString;
022: import com.vividsolutions.jts.geom.MultiPoint;
023: import com.vividsolutions.jts.geom.MultiPolygon;
024: import com.vividsolutions.jts.geom.Point;
025: import com.vividsolutions.jts.geom.Polygon;
026:
027: /**
028: * Utility class for working with simple feature types.
029: *
030: * @author Justin Deoliveira, The Open Planning Project, jdeolive@openplans.org
031: *
032: */
033: public class SimpleFeatureTypes {
034:
035: /**
036: * Injected factory
037: */
038: SimpleTypeFactory factory;
039:
040: /**
041: * Builder used to create new types.
042: */
043: SimpleTypeBuilder builder;
044:
045: /**
046: * Creates a new instance of the class.
047: *
048: * @param builder The factory dependency.
049: */
050: public SimpleFeatureTypes(SimpleTypeFactory factory) {
051: this .factory = factory;
052: builder = new SimpleTypeBuilder(factory);
053: builder.load(new SimpleSchema());
054: }
055:
056: /**
057: * Creates a new simple feature type from an old one restricting the set
058: * of attributes to maintain in the new type.
059: * <p>
060: * The order of hte attributes in the new type is specified by the order
061: * of the names in the <code>properties</code> paramter, not by the
062: * order of the properties in the original type.
063: * </p>
064: *
065: * @param featureType The original type.
066: * @param properties The set of names to include from the original type.
067: *
068: * @return The newly created type.
069: */
070: public SimpleFeatureType sub(SimpleFeatureType featureType,
071: Name[] properties) {
072:
073: if ((properties == null)) {
074: return featureType;
075: }
076:
077: boolean same = featureType.getAttributeCount() == properties.length;
078:
079: for (int i = 0; (i < featureType.getAttributeCount()) && same; i++) {
080: AttributeType type = featureType.getType(i);
081: same = type.getName().equals(properties[i]);
082: }
083:
084: if (same) {
085: return featureType;
086: }
087:
088: builder.init(featureType);
089:
090: //remove any attributes not specified and ensure order
091: List attributes = new ArrayList();
092: for (int i = 0; i < properties.length; i++) {
093: for (Iterator itr = builder.getAttributes().iterator(); itr
094: .hasNext();) {
095: AttributeDescriptor ad = (AttributeDescriptor) itr
096: .next();
097: if (ad.getName().equals(properties[i])) {
098: attributes.add(itr.next());
099: }
100: }
101: }
102: builder.getAttributes().clear();
103: builder.getAttributes().addAll(attributes);
104:
105: return (SimpleFeatureType) builder.feature();
106:
107: }
108:
109: /**
110: * Utility method for SimpleFeatureType construction.
111: *
112: * <p>
113: * Will parse a String of the form: <i>"name:Type,name2:Type2,..."</i>
114: * </p>
115: *
116: * <p>
117: * Where <i>Type</i> is defined by createAttribute.
118: * </p>
119: *
120: * <p>
121: * You may indicate the default Geometry with an astrix: "*geom:Geometry".
122: * </p>
123: *
124: * <p>
125: * Example:<code>name:"",age:0,geom:Geometry,centroid:Point,url:java.io.URL"</code>
126: * </p>
127: *
128: * @param identification identification of FeatureType:
129: * (<i>namesapce</i>).<i>typeName</i>
130: * @param typeSpec Specification for FeatureType
131: *
132: * @throws SchemaException In the event of an invalid type spec.
133: */
134: public SimpleFeatureType create(String identification,
135: String typeSpec) throws SchemaException {
136:
137: int split = identification.lastIndexOf('.');
138: String namespace = (split == -1) ? null : identification
139: .substring(0, split);
140: String typeName = (split == -1) ? identification
141: : identification.substring(split + 1);
142:
143: if (namespace != null) {
144: try {
145: new URI(namespace);
146: } catch (URISyntaxException badNamespace) {
147: throw (SchemaException) new SchemaException(
148: badNamespace).initCause(badNamespace);
149: }
150: }
151:
152: builder.init();
153: builder.setNamespaceURI(namespace);
154: builder.setName(typeName);
155:
156: String[] types = typeSpec.split(",");
157: for (int i = 0; i < types.length; i++) {
158: if (types[i].startsWith("*")) {
159: types[i] = types[i].substring(1);
160: builder.setGeometryName(types[i]);
161: }
162: String name = name(types[i]);
163: Class clazz = clazz(types[i]);
164:
165: builder.attribute(name, clazz);
166: }
167: return (SimpleFeatureType) builder.feature();
168: }
169:
170: /**
171: * Helper method used by {@link #create(String, String)} to pull a name
172: * out of a type spec.
173: *
174: */
175: protected String name(String typeSpec) {
176: int split = typeSpec.indexOf(":");
177:
178: String name;
179:
180: if (split == -1) {
181: name = typeSpec;
182:
183: } else {
184: name = typeSpec.substring(0, split);
185: }
186:
187: return name;
188: }
189:
190: /**
191: * Helper method used by {@link #create(String, String)} to determine if
192: * a type is nillable based on a type spec.
193: *
194: */
195: protected boolean isNillable(String typeSpec) {
196: int split = typeSpec.indexOf(":");
197:
198: String hint = null;
199:
200: if (split != -1) {
201: int split2 = typeSpec.indexOf(":", split + 1);
202:
203: if (split2 != -1) {
204: hint = typeSpec.substring(split2 + 1);
205: }
206: }
207:
208: return hint != null && hint.indexOf("nillable") != -1;
209: }
210:
211: /**
212: * Helper method used by {@link #create(String, String)} to determine the
213: * class of a type from the typeSpec.
214: *
215: */
216: protected Class clazz(String typeSpec) {
217: int split = typeSpec.indexOf(":");
218:
219: String type;
220:
221: if (split == -1) {
222: type = "String";
223: } else {
224:
225: int split2 = typeSpec.indexOf(":", split + 1);
226:
227: if (split2 == -1) {
228: type = typeSpec.substring(split + 1);
229: } else {
230: type = typeSpec.substring(split + 1, split2);
231: }
232: }
233:
234: try {
235: return type(type);
236: } catch (ClassNotFoundException e) {
237: return null;
238: }
239: }
240:
241: static Map typeMap = new HashMap();
242:
243: static {
244: typeMap.put("String", String.class);
245: typeMap.put("string", String.class);
246: typeMap.put("\"\"", String.class);
247: typeMap.put("Integer", Integer.class);
248: typeMap.put("int", Integer.class);
249: typeMap.put("0", Integer.class);
250: typeMap.put("Double", Double.class);
251: typeMap.put("double", Double.class);
252: typeMap.put("0.0", Double.class);
253: typeMap.put("Float", Float.class);
254: typeMap.put("float", Float.class);
255: typeMap.put("0.0f", Float.class);
256: typeMap.put("Geometry", Geometry.class);
257: typeMap.put("Point", Point.class);
258: typeMap.put("LineString", LineString.class);
259: typeMap.put("Polygon", Polygon.class);
260: typeMap.put("MultiPoint", MultiPoint.class);
261: typeMap.put("MultiLineString", MultiLineString.class);
262: typeMap.put("MultiPolygon", MultiPolygon.class);
263: typeMap.put("GeometryCollection", GeometryCollection.class);
264: }
265:
266: /**
267: * Helper method used by {@link #create(String, String)} to map strings
268: * to java classes.
269: *
270: * @throws ClassNotFoundException
271: */
272: protected Class type(String typeName) throws ClassNotFoundException {
273: if (typeMap.containsKey(typeName)) {
274: return (Class) typeMap.get(typeName);
275: }
276:
277: return Class.forName(typeName);
278: }
279: }
|