001: /* Copyright (c) 2001 - 2007 TOPP - www.openplans.org. All rights reserved.
002: * This code is licensed under the GPL 2.0 license, availible at the root
003: * application directory.
004: */
005: package org.vfny.geoserver.global.dto;
006:
007: import com.vividsolutions.jts.geom.Envelope;
008: import org.geotools.feature.AttributeType;
009: import org.geotools.feature.FeatureType;
010: import org.vfny.geoserver.global.xml.NameSpaceElement;
011: import org.vfny.geoserver.global.xml.NameSpaceTranslator;
012: import org.vfny.geoserver.global.xml.NameSpaceTranslatorFactory;
013: import java.util.ArrayList;
014: import java.util.Collections;
015: import java.util.HashMap;
016: import java.util.Iterator;
017: import java.util.LinkedList;
018: import java.util.List;
019: import java.util.Map;
020: import java.util.Set;
021:
022: /**
023: * Generate Data Transfer Objects from "real" objects in the system.
024: *
025: * <p>
026: * This class is used to isolate the DTO from the details of generating them.
027: * This allows DTO objects to be safely used as a wire protocol with out
028: * unrequired dependencies on such things as AttributeType and FeatureType.
029: * </p>
030: *
031: * <p>
032: * This class may choose to opperate as a facade on the services of global.xml?
033: * </p>
034: *
035: * @author jgarnett, Refractions Research, Inc.
036: * @author $Author: dmzwiers $ (last modification)
037: * @version $Id: DataTransferObjectFactory.java 6893 2007-05-29 09:38:46Z groldan $
038: */
039: public class DataTransferObjectFactory {
040: /**
041: * Construct DTO based on provided AttributeType.
042: *
043: * <p>
044: * GMLUtils is used to provide the mapping from
045: * attributeType.getName/attributeType.getType() to an XML type/fragement.
046: * </p>
047: *
048: * @param attributeType Real geotools2 AttributeType
049: *
050: * @return Data Transfer Object for provided attributeType
051: */
052: public static AttributeTypeInfoDTO create(String schemaBase,
053: AttributeType attributeType) {
054: AttributeTypeInfoDTO dto = new AttributeTypeInfoDTO();
055: dto.setName(attributeType.getName());
056:
057: if (isManditory(schemaBase, attributeType.getName())
058: || (attributeType.getMinOccurs() > 0)) {
059: dto.setMinOccurs(1);
060: } else {
061: dto.setMinOccurs(0);
062: }
063:
064: dto.setMaxOccurs(1);
065: dto.setNillable(attributeType.isNillable());
066:
067: NameSpaceTranslator xs = NameSpaceTranslatorFactory
068: .getInstance().getNameSpaceTranslator("xs");
069: NameSpaceTranslator gml = NameSpaceTranslatorFactory
070: .getInstance().getNameSpaceTranslator("gml");
071: NameSpaceElement element;
072:
073: element = xs.getElement(attributeType.getType(), attributeType
074: .getName());
075:
076: if (element == null) {
077: element = gml.getElement(attributeType.getType(),
078: attributeType.getName());
079: }
080:
081: if (element == null) {
082: element = xs.getElement("string");
083: }
084:
085: // element = xs.getElement( attributeType.getName() );
086: // if(element == null) element = gml.getElement( attributeType.getName() );
087: // if(element == null) element = xs.getElement( "string" );
088: dto.setComplex(false);
089: dto.setType(element.getTypeRefName());
090:
091: return dto;
092: }
093:
094: /**
095: * Construct any of the well-known GML attributeTypes.
096: * <p>
097: * SchemaBase is used to ensure that attribute required by the XMLSchema
098: * have a minOccurs of 1.
099: * </p>
100: * <p>
101: * This method uses NameSpaceTranslatorFactorys for xs and gml in order to
102: * provide accurate type information describing the provided attribute
103: * </p>
104: * @param schemaBase used to determine manditory attributes
105: * @param attributeName Name of attribute being described
106: * @return DataTransferObject encapsulating attribute information.
107: */
108: public static AttributeTypeInfoDTO create(String schemaBase,
109: String attributeName) {
110: AttributeTypeInfoDTO dto = new AttributeTypeInfoDTO();
111: dto.setName(attributeName);
112: dto
113: .setMinOccurs(isManditory(schemaBase, attributeName) ? 1
114: : 0);
115: dto.setMaxOccurs(1);
116: dto.setNillable(true); // nillable by default?
117:
118: NameSpaceTranslator xs = NameSpaceTranslatorFactory
119: .getInstance().getNameSpaceTranslator("xs");
120: NameSpaceTranslator gml = NameSpaceTranslatorFactory
121: .getInstance().getNameSpaceTranslator("gml");
122: NameSpaceElement element;
123:
124: element = xs.getElement(attributeName);
125:
126: if (element == null) {
127: element = gml.getElement(attributeName);
128: }
129:
130: if (element == null) {
131: element = xs.getElement("string");
132: }
133:
134: dto.setComplex(false);
135: dto.setType(element.getTypeRefName());
136:
137: return dto;
138: }
139:
140: /**
141: * Construct DTO based on provided schema.
142: *
143: * <p>
144: * GMLUtils is used to provide the mapping to an XML type/fragement for
145: * each attribute
146: * </p>
147: *
148: * @param dataStoreId Used as a backpointer to locate dataStore
149: * @param schema Real geotools2 FeatureType
150: *
151: * @return Data Transfer Object for provided schema
152: */
153: public static FeatureTypeInfoDTO create(String dataStoreId,
154: FeatureType schema) {
155: FeatureTypeInfoDTO dto = new FeatureTypeInfoDTO();
156: dto.setAbstract(null);
157: dto.setDataStoreId(dataStoreId);
158: dto.setDefaultStyle("styles/normal.sld");
159: dto.setDefinitionQuery(null); // no extra restrictions
160: dto.setDirName(dataStoreId + "_" + schema.getTypeName());
161: dto.setKeywords(Collections.EMPTY_LIST);
162: dto.setLatLongBBox(new Envelope());
163: dto.setNativeBBox(new Envelope());
164: dto.setName(schema.getTypeName());
165: dto.setNumDecimals(8);
166: dto.setSchemaAttributes(generateAttributes(schema));
167:
168: NameSpaceTranslator gml = NameSpaceTranslatorFactory
169: .getInstance().getNameSpaceTranslator("gml");
170: String schemaBase = gml.getElement("AbstractFeatureType")
171: .getQualifiedTypeDefName();
172: dto.setSchemaBase(schemaBase);
173:
174: dto.setSchemaName(dataStoreId.toUpperCase() + "_"
175: + schema.getTypeName().toUpperCase() + "_TYPE");
176: dto.setSRS(schema.getDefaultGeometry().getGeometryFactory()
177: .getSRID());
178: dto
179: .setTitle(schema.getNamespace() + " "
180: + schema.getTypeName());
181:
182: return dto;
183: }
184:
185: /**
186: * List of attributes DTO information gernated from schema.
187: *
188: * @param schema
189: * @return
190: */
191: public static List generateAttributes(FeatureType schema) {
192: AttributeType[] attributes = schema.getAttributeTypes();
193: List list = new ArrayList(attributes.length);
194:
195: for (int i = 0; i < attributes.length; i++) {
196: list.add(create("AbstractFeatureType", attributes[i]));
197: }
198:
199: return list;
200: }
201:
202: /**
203: * List of attribtue DTO information generated from schemaBase.
204: * <p>
205: * Please note this is currently only used for display by TypesForm,
206: * TypeInfo simply makes use of getRequiredBaseAttributes to select
207: * AttributeTypes from the FeatureType schema.
208: * </p>
209: * <p>
210: * More specifically the values of isNillable, minOccurs and maxOccurs
211: * provided by the DataStore may not agree with the results of this
212: * function. TypeInfo opperatates on the assumption minOccurs=1, maxOccurs=1
213: * and AttributeType.isNillable() is correct.
214: * </p>
215: * @param schemaBase SchemaBase
216: * @return List of AttributeTypeInfoDTO representative of schemaBase required
217: * Attributes
218: */
219: public static List generateRequiredAttributes(String schemaBase) {
220: String[] attributeNames = getRequiredBaseAttributes(schemaBase);
221:
222: List list = new ArrayList(attributeNames.length);
223:
224: for (int i = 0; i < attributeNames.length; i++) {
225: list.add(create(schemaBase, attributeNames[i]));
226: }
227:
228: return list;
229: }
230:
231: /**
232: * Test is attribute is a required attribtue of schemaBase.
233: *
234: * @return <code>True</code> if attribute is required for schemaBase
235: */
236: public static boolean isManditory(String schemaBase,
237: String attribute) {
238: String[] required = getRequiredBaseAttributes(schemaBase);
239:
240: for (int i = 0; i < required.length; i++) {
241: if (attribute.equals(required[i])) {
242: return true;
243: }
244: }
245:
246: return false;
247: }
248:
249: /**
250: * Required Attributes for schemaBase.
251: * <p>
252: * This information is a hardcoded representation of what woudl be available
253: * if we had actually parsed the GML XMLSchema.
254: * </p>
255: * @param schemaBase
256: * @return
257: */
258: public static String[] getRequiredBaseAttributes(String schemaBase) {
259: if (schemaBaseMap.containsKey(schemaBase)) {
260: return (String[]) schemaBaseMap.get(schemaBase);
261: }
262:
263: return new String[] {};
264: }
265:
266: public static Map schemaBaseMap = new HashMap();
267:
268: static {
269: schemaBaseMap.put("gml:AbstractFeatureType", new String[] {}); //"description","name","boundedBy"} );
270: /*schemaBaseMap.put("AbstractFeatureCollectionBaseType",
271: new String[] {"description","name","boundedBy"} );
272: schemaBaseMap.put("GeometryPropertyType",
273: new String[] {"geometry"} );
274: schemaBaseMap.put("FeatureAssociationType",
275: new String[] {"feature"} );
276: schemaBaseMap.put("BoundingShapeType",
277: new String[] {"box"} );
278: schemaBaseMap.put("PointPropertyType",
279: new String[] {"point"} );
280: schemaBaseMap.put("PolygonPropertyType",
281: new String[] {"polygon"} );
282: schemaBaseMap.put("LineStringPropertyType",
283: new String[] {"lineString"} );
284: schemaBaseMap.put("MultiPointPropertyType",
285: new String[] {"multiPoint"} );
286: schemaBaseMap.put("MultiLineStringPropertyType",
287: new String[] {"multiLineString"} );
288: schemaBaseMap.put("MultiPolygonPropertyType",
289: new String[] {"multiPolygonString"} );
290: schemaBaseMap.put("MultiGeometryPropertyType",
291: new String[] {"multiGeometry"} );
292: schemaBaseMap.put("NullType", new String[] {} );*/
293: }
294:
295: /**
296: * Mappings for name and type, or null if not found.
297: * <p>
298: * List construction order:
299: * <ul>
300: * <li>Use of property types if name and exact type match one of the gml
301: * properties references.<br>
302: * For <code>name="pointProperty", type=com.vividsolutions.jts.geom.Point</code> maps to:
303: * <b>gml:PointPropertyType</b>
304: * </li>
305: * <li>Search the schema for defined types are checked for an exact match
306: * based on type.<br>
307: * For <code>type=java.lang.String</code> maps to:
308: * <b>xs:string</b>
309: * </li>
310: * <li>A linear seach of the defined types is made making use of
311: * isAssignable.<br>
312: * For <code>type=com.vividsolutions.jts.geom.Geometry</code> maps to:
313: * <b>gml:PointType gml:LineStringType gml:LinearRingType gml:BoxType gml:PolygonType gml:GeometryCollectionType gml:MultiPointType gml:MultiLineStringType, gml:MultiPolygonType</b>
314: * </li>
315: * <li>As a wild assumption we assume <code>xs:string</code> can be used.<br>
316: * For <code>type=java.net.URL</code> maps to: <b>xs:string</b>
317: * </li>
318: * </ul>
319: * <p>
320: * All mappings are consulted using using a linear search.
321: * The list is returned in the order of most specific to least specific.
322: * </p>
323: * Complete Example:
324: * <code>name="pointProperty", class=type=com.vividsolutions.jts.geom.Point</code>
325: * <p>
326: * Expected Mapping:
327: * </p>
328: * <ul>
329: * <li>gml:PointPropertyType - pointProperty & Point.class match</li>
330: * <li>gml:PointType - Point.class match</li>
331: * <li>gml:AbstractGeometry - Point instance of Geometry match</li>
332: * <li>xs:string - String assumption</li>
333: * </ul>
334: * @param name attribute name
335: * @param type attribtue type
336: * @return List of NameSpaceElements is returned in the order of most specific to least specific.
337: */
338: public static List getElements(String name, Class type) {
339: NameSpaceTranslator xs = NameSpaceTranslatorFactory
340: .getInstance().getNameSpaceTranslator("xs");
341: NameSpaceTranslator gml = NameSpaceTranslatorFactory
342: .getInstance().getNameSpaceTranslator("gml");
343: List result = new LinkedList();
344:
345: if ((name == null) || (name == "")) {
346: throw new NullPointerException(
347: "Element name must be defined.");
348: }
349:
350: if (type == null) {
351: throw new NullPointerException(
352: "Element type must be defined.");
353: }
354:
355: Set s = xs.getAssociatedTypes(type);
356: s.addAll(xs.getAssociatedTypes(name));
357: s.addAll(gml.getAssociatedTypes(type));
358: s.addAll(gml.getAssociatedTypes(name));
359:
360: Iterator i = s.iterator();
361:
362: while (i.hasNext()) {
363: NameSpaceElement element = (NameSpaceElement) i.next();
364:
365: if (name.equals(element.getTypeDefName())) {
366: if (!result.contains(element)) {
367: result.add(element);
368: } else if (name.equals(element.getTypeRefName())) {
369: if (!result.contains(element)) {
370: result.add(element);
371: } else if (name.equals(element
372: .getQualifiedTypeDefName())) {
373: if (!result.contains(element)) {
374: result.add(element);
375: } else if (name.equals(element
376: .getQualifiedTypeRefName())) {
377: if (!result.contains(element)) {
378: result.add(element);
379: }
380: }
381: }
382: }
383: }
384: }
385:
386: if (!Object.class.equals(type)) {
387: Class cls = type;
388:
389: while (!Object.class.equals(cls)) {
390: i = s.iterator();
391:
392: while (i.hasNext()) {
393: NameSpaceElement element = (NameSpaceElement) i
394: .next();
395:
396: // add the rest afterwards
397: if (element.getJavaClass().equals(cls)
398: && !result.contains(element)) {
399: result.add(element);
400: }
401: }
402:
403: cls = cls.getSuperclass();
404: }
405: }
406:
407: i = s.iterator();
408:
409: while (i.hasNext()) {
410: NameSpaceElement element = (NameSpaceElement) i.next();
411:
412: // add the rest afterwards
413: if (!result.contains(element)) {
414: result.add(element);
415: }
416: }
417:
418: NameSpaceElement element = xs.getElement("string");
419:
420: if (!result.contains(element)) {
421: result.add(element);
422: }
423:
424: return result;
425: }
426:
427: /**
428: * Retrive best NameSpaceElement match for provided attribtue name and type.
429: * <p>
430: * Best match is determined by the search order defined by getElements.
431: * </p>
432: * @param name
433: * @param type
434: * @return Closest NameSapceElement
435: */
436: private static final NameSpaceElement getBestMatch(String name,
437: Class type) {
438: return (NameSpaceElement) getElements(name, type).get(0);
439: }
440: }
|