001: package org.geotools.feature.iso.simple;
002:
003: import java.util.ArrayList;
004: import java.util.Collection;
005: import java.util.Collections;
006: import java.util.HashMap;
007: import java.util.HashSet;
008: import java.util.Iterator;
009: import java.util.List;
010: import java.util.Map;
011: import java.util.Set;
012:
013: import org.geotools.feature.iso.Types;
014: import org.geotools.feature.iso.type.AttributeDescriptorImpl;
015: import org.geotools.feature.iso.type.TypeFactoryImpl;
016: import org.geotools.referencing.CRS;
017: import org.opengis.feature.simple.SimpleFeatureCollectionType;
018: import org.opengis.feature.simple.SimpleFeatureType;
019: import org.opengis.feature.simple.SimpleTypeFactory;
020: import org.opengis.feature.type.AttributeDescriptor;
021: import org.opengis.feature.type.AttributeType;
022: import org.opengis.feature.type.GeometryType;
023: import org.opengis.feature.type.Name;
024: import org.opengis.feature.type.PropertyType;
025: import org.opengis.feature.type.Schema;
026: import org.opengis.feature.type.TypeFactory;
027: import org.opengis.referencing.crs.CoordinateReferenceSystem;
028: import org.opengis.util.InternationalString;
029:
030: /**
031: * Builder to ease creation of simple types.
032: * <p>
033: * For reference these are the limitations of a "Simple Feature" model:
034: * <ol>
035: * <li>Properties limited to attributes only (no associations)
036: * <li>Properties is a List - order of attributes matters
037: * <li>Attribute "index" is as good as a Name
038: * <li>Attribute "name" (ie String) is as good as Name
039: * <ul>
040: * <li>No name conflict, so lookup with simple string is okay
041: * </ul>
042: * <li>getSuper() is <code>null</code>, required for safe use of index and
043: * name
044: * </ol>
045: * </p>
046: * <p>
047: * There are four methods to manage builder state:
048: * <ul>
049: * <li>{@link init()} - completly replace settings with builder defaul
050: * <li>{@link init( PropertyType )} - completly replace settings from type
051: * <li>{@link reset()} - called after type creation to reset common type
052: * settings
053: * </ul>
054: * For examples of use please review the two type creation methods:
055: * <ul>
056: * <li>{@link feature()}
057: * <li>{@link collection()}
058: * </ul>
059: * Several methods for adding attribute make use of a Class directly, this class
060: * is used to lookup an AttributeType "binding" to be used as a prototype. In
061: * addition to providing binding one at a time, you can load a {@link Schema} in
062: * one fell swoop. You may find SimpleSchema a useful starting place.
063: * </p>
064: *
065: * @author Justin Deolivera
066: * @author Jody Garnett
067: */
068: public class SimpleTypeBuilder {
069: /**
070: * Used for content creation (contains a crs and filter factory)
071: */
072: private SimpleTypeFactory factory;
073:
074: private TypeFactory typeFactory;
075:
076: /**
077: * Naming: local name
078: */
079: private String local;
080:
081: /**
082: * Naming: uri indicating scope
083: */
084: private String uri;
085:
086: /**
087: * Description of type.
088: */
089: private InternationalString description;
090:
091: /**
092: * MemberType for collection.
093: * <p>
094: * A simple feature collection can only represent one association.
095: */
096: private SimpleFeatureType memberType;
097:
098: /**
099: * List of attributes.
100: */
101: private List attributes;
102:
103: /**
104: * Additional restrictions on the type.
105: */
106: private Set restrictions;
107:
108: /** Name of the default geometry to use */
109: private String defaultGeom;
110:
111: protected CoordinateReferenceSystem crs;
112:
113: /**
114: * Map of java class bound to properties types.
115: */
116: protected Map/* <Class,AttributeType> */bindings;
117:
118: public SimpleTypeBuilder(SimpleTypeFactory factory) {
119: this .factory = factory;
120: this .typeFactory = new TypeFactoryImpl();
121: }
122:
123: // Dependency Injection
124: //
125: public void setSimpleTypeFactory(SimpleTypeFactory factory) {
126: this .factory = factory;
127: }
128:
129: public SimpleTypeFactory getSimpleTypeFactory() {
130: return factory;
131: }
132:
133: // Creation Methods
134: //
135: /**
136: * Creation of simple feature.
137: *
138: * @return SimpleFeatureType created
139: */
140: public SimpleFeatureType feature() {
141: AttributeType geom = lookUp(getGeometryName());
142: Name name = typeName();
143: List descriptors = getDescriptors();
144: AttributeDescriptor defaultGeometry = null;
145: CoordinateReferenceSystem crs2 = getCRS();
146: Set restrictions = restrictions();
147: InternationalString description = getDescription();
148: SimpleFeatureType type = factory.createSimpleFeatureType(name,
149: descriptors, defaultGeometry, crs2, restrictions,
150: description);
151: reset();
152: return type;
153: }
154:
155: /**
156: * Creation of simple feature collection.
157: *
158: * @return SimpleFeatureCollectionType created
159: */
160: public SimpleFeatureCollectionType collection() {
161: SimpleFeatureCollectionType type = getSimpleTypeFactory()
162: .createSimpleFeatureCollectionType(typeName(),
163: memberType, getDescription());
164: reset();
165: return type;
166: }
167:
168: // Builder State
169: //
170: /** Reset the builder for new conent */
171: public void init() {
172: this .description = null;
173: this .defaultGeom = null;
174: this .local = null;
175: this .memberType = null;
176: this .uri = null;
177: this .crs = null;
178: this .attributes = null;
179:
180: }
181:
182: public void init(PropertyType type) {
183: init();
184: if (type == null)
185: return;
186:
187: uri = type.getName().getNamespaceURI();
188: local = type.getName().getLocalPart();
189: description = type.getDescription();
190: restrictions = null;
191: restrictions().addAll(type.getRestrictions());
192:
193: if (type instanceof SimpleFeatureType) {
194: SimpleFeatureType feature = (SimpleFeatureType) type;
195: attributes = newList((List) feature.attributes());
196: }
197: if (type instanceof SimpleFeatureCollectionType) {
198: SimpleFeatureCollectionType collection = (SimpleFeatureCollectionType) type;
199: attributes = Collections.EMPTY_LIST; // will prevent any addition
200: // of attributes
201: this .memberType = collection.getMemberType();
202: }
203: }
204:
205: /**
206: * Reset is called after creation a "new" type.
207: * <p>
208: * The following informatoin is reset:
209: * <ul>
210: * <li>local = local part of name
211: * <li>attributes (aka structural properties)
212: * <li>default geometry
213: * </ul>
214: */
215: public void reset() {
216: this .local = null;
217: this .attributes = newList(attributes);
218: this .defaultGeom = null;
219: }
220:
221: // Naming
222: //
223: public void setNamespaceURI(String namespace) {
224: this .uri = namespace;
225: }
226:
227: public String getNamespaceURI() {
228: return uri;
229: }
230:
231: public void setName(String name) {
232: this .local = name;
233: }
234:
235: public SimpleTypeBuilder name(String name) {
236: setName(name);
237: return this ;
238: }
239:
240: public String getName() {
241: return local;
242: }
243:
244: public void setDescription(InternationalString description) {
245: this .description = description;
246: }
247:
248: public InternationalString getDescription() {
249: return description;
250: }
251:
252: /**
253: * Used to lookup AttributeType for provided binding.
254: *
255: * @param binding
256: * @return AttributeType
257: */
258: public AttributeType getBinding(Class binding) {
259: return (AttributeType) bindings().get(binding);
260: }
261:
262: /**
263: * Used to provide a specific type for provided binding.
264: * <p>
265: * You can use this method to map the AttributeType used when addAttribute(
266: * String name, Class binding ) is called.
267: *
268: * @param binding
269: * @param type
270: */
271: public void addBinding(Class binding, AttributeType type) {
272: bindings().put(binding, type);
273: }
274:
275: /**
276: * Load the indicated schema to map Java class to your Type System. (please
277: * us a profile to prevent binding conflicts).
278: *
279: * @param schema
280: */
281: public void load(Schema schema) {
282: for (Iterator itr = schema.values().iterator(); itr.hasNext();) {
283: AttributeType type = (AttributeType) itr.next();
284: addBinding(type.getBinding(), type);
285: }
286: }
287:
288: // Attributes
289: //
290: /**
291: * Access to attributes used by builder.
292: * <p>
293: * You can use this method to perform collection opperations before
294: * construction. This is most useful when initializing the builder with a
295: * known type, performing modifications, and then creating a derrived type.
296: * </p>
297: *
298: * @return List of attributes used for creation
299: */
300: public List getAttributes() {
301: if (attributes == null) {
302: attributes = newList();
303: }
304: return attributes;
305: }
306:
307: private List getDescriptors() {
308: List types = getAttributes();
309: List descriptors = new ArrayList(types.size());
310: AttributeType type;
311: AttributeDescriptor descriptor;
312: for (Iterator it = types.iterator(); it.hasNext();) {
313: type = (AttributeType) it.next();
314: descriptor = new AttributeDescriptorImpl(type, Types
315: .typeName(type.getName()), 0, 1, true, null);
316: descriptors.add(descriptor);
317: }
318: return descriptors;
319: }
320:
321: /**
322: * Allow for user supplied list implementaion used for attributes.
323: * <p>
324: * Examples of useful attribute lists:
325: * <ul>
326: * <li>ArrayList - fixed length, use new ArrayList( size ) when known
327: * <li>LinkedList etc...
328: * </ul>
329: * The list class used here should also be used for feature contents.
330: * </p>
331: *
332: * @param attributes
333: * List implementation used to organize attributes
334: */
335: public void setAttributes(List attributes) {
336: this .attributes = attributes;
337: }
338:
339: public SimpleTypeBuilder attribute(AttributeType type) {
340: addAttribute(type);
341: return this ;
342: }
343:
344: /**
345: * Adds a new AttributeType w/ provided name and binding.
346: * <p>
347: * The binding will be used to locate an AttributeType to use as a
348: * prototype, or will be used directly if a binding cannot be found.
349: *
350: * @param name
351: * @param bind
352: * @return SimpleTypeBuilder for use with chaining
353: */
354: public SimpleTypeBuilder attribute(String name, Class bind) {
355: addAttribute(name, bind);
356: return this ;
357: }
358:
359: public SimpleTypeBuilder geometry(String name, Class bind) {
360: addGeometry(name, bind);
361: return this ;
362: }
363:
364: public void addAttribute(AttributeType type) {
365: // simple feature type => attribute name == type name, so create a new
366: // type with the same name as the attribute, which extends the old
367: // type
368: attributes().add(type);
369: }
370:
371: /**
372: * Adds a new AttributeType w/ provided name and binding.
373: * <p>
374: * The binding will be used to locate an AttributeType to use as a
375: * prototype, or will be used directly if a binding cannot be found.
376: *
377: * @param name
378: * @param bind
379: * @return SimpleTypeBuilder for use with chaining
380: */
381: public void addAttribute(String name, Class bind) {
382: AttributeType prototype = getBinding(bind);
383: AttributeType type;
384: Name typeName = Types.typeName(name);
385: if (prototype != null) {
386: if (prototype instanceof GeometryType) {
387: type = createPrototype(typeName, prototype, crs);
388: } else {
389: type = createPrototype(typeName, prototype);
390: }
391: } else {
392: type = createType(typeName, bind);
393: }
394: addAttribute(type);
395: }
396:
397: /**
398: * Add a new GeometryAttributeType w/ provided name and binding.
399: * <p>
400: * A GeometryAttribute will be created in the same manner as for
401: * addAttribute with the addition of the CRS.
402: *
403: * @param name
404: * @param binding
405: */
406: public void addGeometry(String name, Class bind) {
407: AttributeType prototype = getBinding(bind);
408: AttributeType type;
409: Name typeName = Types.typeName(name);
410: if (prototype != null) {
411: type = createPrototype(typeName, prototype, crs);
412: } else {
413: type = createType(typeName, bind, crs);
414: }
415: addAttribute(type);
416: }
417:
418: /** Indicate "default" geometry by attributeName */
419: public SimpleTypeBuilder geometry(String attributeName) {
420: setGeometryName(attributeName);
421: return this ;
422: }
423:
424: public void setGeometryName(String type) {
425: defaultGeom = type;
426: }
427:
428: /**
429: * Return the current defaultGeometry.
430: * <p>
431: * This is the name of the AttributeType that will be used for the
432: * FeatureType to be created. You make explicitly set a string, or let the
433: * first GeometryType found be used as the default.
434: * </p>
435: *
436: * @return name of the default GeometryAttributeType
437: */
438: public String getGeometryName() {
439: if (defaultGeom == null) {
440: for (Iterator i = attributes.iterator(); i.hasNext();) {
441: AttributeType attribute = (AttributeType) i.next();
442: if (attribute instanceof GeometryType) {
443: return attribute.getName().getLocalPart();
444: }
445: }
446: return null;
447: }
448: return defaultGeom;
449: }
450:
451: public void setCRS(CoordinateReferenceSystem crs) {
452: this .crs = crs;
453: }
454:
455: public SimpleTypeBuilder crs(CoordinateReferenceSystem crs) {
456: setCRS(crs);
457: return this ;
458: }
459:
460: /**
461: * Uses CRS utility class with buildres TypeFactory.getCRSFactory to look up
462: * a CoordinateReferenceSystem based on the provied srs.
463: * <p>
464: * A SpatialReferenceSystem can be one of the following:
465: * <ul>
466: * <li>"AUTHORITY:CODE"
467: * <li>Well Known Text
468: * </ul>
469: *
470: * @param srs
471: * @return TypeBuilder ready for chaining
472: * @throws IllegalArgumentException
473: * When SRS not understood
474: */
475: public SimpleTypeBuilder crs(String SRS) {
476: try {
477: setCRS(CRS.decode(SRS));
478: } catch (Exception e) {
479: throw new IllegalArgumentException("SRS '" + SRS
480: + "' unknown:" + e);
481: }
482: return this ;
483: }
484:
485: public CoordinateReferenceSystem getCRS() {
486: return crs;
487: }
488:
489: public SimpleTypeBuilder member(SimpleFeatureType memberType) {
490: setMember(memberType);
491: return this ;
492: }
493:
494: public void setMember(SimpleFeatureType memberType) {
495: this .memberType = memberType;
496: }
497:
498: // Bindings
499: //
500:
501: public void load(SimpleSchema schema) {
502: for (Iterator itr = schema.values().iterator(); itr.hasNext();) {
503: AttributeType type = (AttributeType) itr.next();
504: addBinding(type.getBinding(), type);
505: }
506: }
507:
508: // Factory method argument preparation
509: //
510: /**
511: * Naming: Accessor which returns type name as follows:
512: * <ol>
513: * <li>If <code>typeName</code> has been set, its value is returned.
514: * <li>If <code>name</code> has been set, it + <code>namespaceURI</code>
515: * are returned.
516: * </ol>
517: *
518: */
519: protected Name typeName() {
520: if (local == null)
521: return null;
522: return Types.typeName(uri, local);
523: }
524:
525: /**
526: * Grab property collection as an argument to factory method.
527: * <p>
528: * This may return a copy as needed, since most calls to a factory method
529: * end up with a reset this seems not be needed at present.
530: * </p>
531: */
532: protected Collection attributes() {
533: if (attributes == null) {
534: attributes = newList();
535: }
536: return attributes;
537: }
538:
539: protected Set restrictions() {
540: if (restrictions == null) {
541: restrictions = newSet();
542: }
543: return restrictions;
544: }
545:
546: /**
547: * Accessor for bindings.
548: */
549: protected Map bindings() {
550: if (bindings == null) {
551: bindings = new HashMap();
552: }
553: return bindings;
554: }
555:
556: // protected AssociationDescriptor contentsDescriptor() {
557: // AssociationType assocType = factory.createAssociationType(memberType
558: // .getName(), memberType, false, false, Collections.EMPTY_SET,
559: // null, null);
560: // // Q: not sure if we should be creating null names here?
561: // // A: just use "memberOf" unless they say different...
562: // return factory.createAssociationDescriptor(assocType, Types
563: // .typeName("memberType"), 0, Integer.MAX_VALUE);
564: // }
565:
566: // Utility Methods
567: // (Subclass may customize)
568: //
569: protected Set newSet() {
570: return new HashSet();
571: }
572:
573: /**
574: * Template method to enable subclasses to customize the list implementation
575: * used by "default".
576: *
577: * @return List (subclass may override)
578: */
579: protected List newList() {
580: return new ArrayList();
581: }
582:
583: /**
584: * Provides an empty copy of the provided origional list.
585: * <p>
586: * This method is used by reset for the following goals:
587: * <ul>
588: * <li>use the user supplied collection directly by the TypeFactory,
589: * <li>remember the user supplied collection type for subsequent builder
590: * use
591: * </ul>
592: * This allows a user to indicate that attributes are stored in a
593: * "LinkedList" once.
594: *
595: * @param origional
596: * Origional collection
597: * @return New instance of the originoal Collection
598: */
599: protected List newList(List origional) {
600: if (origional == null) {
601: return newList();
602: }
603: if (origional == Collections.EMPTY_LIST) {
604: return newList();
605: }
606: try {
607: return (List) origional.getClass().newInstance();
608: } catch (InstantiationException e) {
609: return newList();
610: } catch (IllegalAccessException e) {
611: return newList();
612: }
613: }
614:
615: private AttributeType lookUp(String name) {
616: if (name == null)
617: return null;
618: for (Iterator i = attributes.iterator(); i.hasNext();) {
619: AttributeType attributeType = (AttributeType) i.next();
620: if (name.equals(attributeType.getName().getLocalPart())) {
621: return attributeType;
622: }
623: }
624: return null;
625: }
626:
627: protected AttributeType createPrototype(Name typeName,
628: AttributeType proto) {
629: return typeFactory.createAttributeType(typeName, proto
630: .getBinding(), false, false, Collections.EMPTY_SET,
631: null, null);
632: }
633:
634: protected GeometryType createPrototype(Name typeName,
635: AttributeType proto, CoordinateReferenceSystem crs) {
636: return typeFactory.createGeometryType(typeName, proto
637: .getBinding(), crs, false, false,
638: Collections.EMPTY_SET, null, null);
639: }
640:
641: /**
642: * Create an AttributeType bound to this Java class. Attribute Type created
643: * with:
644: * <ul>
645: * <li>name: typeName
646: * <li>binding: bind
647: * <li>
648: * </ul>
649: * Subclass may override.
650: *
651: * @param typeName
652: * Name of attribute type to create
653: * @param bind
654: * @return
655: */
656: protected AttributeType createType(Name typeName, Class bind) {
657: return typeFactory.createAttributeType(typeName, bind, false,
658: false, Collections.EMPTY_SET, null, null);
659: }
660:
661: protected GeometryType createType(Name typeName, Class bind,
662: CoordinateReferenceSystem crs) {
663: return typeFactory.createGeometryType(typeName, bind, crs,
664: false, false, Collections.EMPTY_SET, null, null);
665: }
666: }
|