001: package org.geotools.feature.iso;
002:
003: import java.util.ArrayList;
004: import java.util.Collection;
005: import java.util.Iterator;
006: import java.util.List;
007: import java.util.logging.Logger;
008:
009: import org.opengis.feature.type.AttributeDescriptor;
010: import org.opengis.feature.type.AttributeType;
011: import org.opengis.feature.type.ComplexType;
012: import org.opengis.feature.type.Name;
013:
014: /**
015: * Helper methods for dealing with Descriptor.
016: * <p>
017: * This methods opperate directly on the interfaces provided by geoapi, no
018: * actual classes were harmed in the making of these utility methods.
019: * </p>
020: *
021: * @author Jody Garnett
022: * @author Justin Deoliveira
023: */
024: public class Descriptors {
025: private static final Logger LOGGER = org.geotools.util.logging.Logging
026: .getLogger(Descriptors.class.getPackage().getName());
027:
028: // /**
029: // * Handle subtyping in a "sensible" manner.
030: // * <p>
031: // * We explored using the XMLSchema of extention and restriction, and have
032: // * instead opted for the traditional Java lanaguage notion of an override.
033: // * <p>
034: // * The concept of an overrided allows both:
035: // * <ul>
036: // * <li>extention - completly new attribtues are tacked on the "end" of the
037: // * list
038: // * <li>restriction - attribute with the same qname are used to specify
039: // * additional (or replace) information provided by the parent.
040: // * </ol>
041: // * Note - even <b>removal</b> ( a complicated (and silly) use of
042: // * restriction in XMLSchema) is supported. To remove simply override an
043: // * attribute mentioned by the parent with multiplicity 0:0.
044: // * </p>
045: // *
046: // * @param parent
047: // * @param subtype
048: // * @return Descriptor resulting by extending the provided schema
049: // (collisions
050: // * on qname are treated as overrides)
051: // */
052: // public ComplexType subtype(ComplexType parent, ComplexType extend) {
053: // /*
054: // * if( schema instanceof AllDescriptor && subtype instanceof
055: // * AllDescriptor ){ return subtype( (AllDescriptor) schema,
056: // * (AllDescriptor) extend); } else if( schema instanceof
057: // * ChoiceDescriptor && extend instanceof ChoiceDescriptor ){ return
058: // * subtype( (ChoiceDescriptor) schema, (ChoiceDescriptor) extend); }
059: // * else if( schema instanceof OrderedDescriptor && extend instanceof
060: // * OrderedDescriptor ){ return subtype( (OrderedDescriptor) schema,
061: // * (OrderedDescriptor) extend); } else { List<Descriptor> all = new
062: // * ArrayList<Descriptor>(); all.add( schema ); all.add( extend );
063: // * return factory.ordered( all, 1, 1 ); }
064: // */
065: // try {
066: // return restriction(parent, extend);
067: // }
068: // catch(IllegalArgumentException structsDontMatch){
069: // return extension(parent, extend);
070: // }
071: // }
072: //
073: // public ComplexType subtype(
074: // ComplexType parent, Collection/*<AttributeDescriptor>*/ schema
075: // ) {
076: // try {
077: // return restriction(parent, schema);
078: // }
079: // catch(IllegalArgumentException structsDontMatch){
080: // return extension(parent,schema);
081: // }
082: // }
083:
084: /**
085: * Restriction only works on exact structure match.
086: * <p>
087: * This is the way XMLSchema handles it ...
088: * </p>
089: *
090: * @param schema
091: * @param sub
092: * @return
093: */
094: // @SuppressWarnings("unchecked")
095: // public ComplexType restriction(ComplexType parent,ComplexType restrict) {
096: //
097: // ComplexType type = null;
098: //
099: // if (
100: // parent instanceof ChoiceType && restrict instanceof ChoiceType
101: // ) {
102: //
103: // Set choices = (Set) restriction(
104: // ((ChoiceType)parent).getAttributes(),
105: // ((ChoiceType)restrict).getAttributes(),
106: // new HashSet()
107: // );
108: //
109: // type = xmlFactory.createChoiceType(choices);
110: // }
111: // else if (parent instanceof SequenceType && restrict instanceof
112: // SequenceType) {
113: // List sequence = (List) restriction(
114: // ((SequenceType)parent).getAttributes(),
115: // ((SequenceType)restrict).getAttributes(),
116: // new ArrayList()
117: // );
118: //
119: // type = xmlFactory.createSequenceType(sequence);
120: // }
121: // else if (
122: // parent instanceof ComplexType && restrict instanceof ComplexType
123: // ){
124: // List elements = (List) restriction(
125: // ((ComplexType)parent).getAttributes(),
126: // ((ComplexType)restrict).getAttributes(),
127: // new ArrayList()
128: // );
129: //
130: // ComplexType ct = (ComplexType) restrict;
131: // type = xmlFactory.createType(
132: // ct.getName(),elements,ct.isIdentified(),ct.isNillable().booleanValue(),
133: // ct.getRestrictions(),ct,ct.isAbstract());
134: //
135: // }
136: // else {
137: // throw new IllegalArgumentException("Cannot restrict provided schema");
138: // }
139: //
140: // return type;
141: //
142: // }
143: // public ComplexType restriction(ComplexType parent,
144: // Collection/*<AttributeDescriptor>*/ schema) {
145: // ComplexType type = null;
146: //
147: // if (parent instanceof ChoiceType) {
148: // Set choices = (Set) restriction(
149: // ((ChoiceType)parent).getAttributes(),schema,new HashSet()
150: // );
151: // type = xmlFactory.createChoiceType(choices);
152: // }
153: // else if (parent instanceof SequenceType) {
154: // List sequence = (List) restriction(
155: // ((SequenceType)parent).getAttributes(), schema, new ArrayList()
156: // );
157: //
158: // type = xmlFactory.createSequenceType(sequence);
159: // }
160: // else if (parent instanceof ComplexType){
161: // List elements = (List) restriction(
162: // ((ComplexType)parent).getAttributes(),schema, new ArrayList()
163: // );
164: //
165: // //duplicate parent type with new schema
166: // //JD: this is a bit of a hack, creating type manually (ie wihtout
167: // // factory, because we have constructed the schema manually
168: //
169: // ComplexType ct = (ComplexType) parent;
170: // type = new ComplexTypeImpl(
171: // ct.getName(),elements,ct.isIdentified(),
172: // ct.isNillable().booleanValue(),ct.getRestrictions(),ct,
173: // ct.isAbstract()
174: // );
175: // type = xmlFactory.createType(
176: // ct.getName(),elements,ct.isIdentified(),ct.isNillable().booleanValue(),
177: // ct.getRestrictions(),null,ct.isAbstract());
178: // }
179: // else {
180: // throw new IllegalArgumentException("Cannot restrict provided schema");
181: // }
182: //
183: // return type;
184: // }
185: //
186: // public ComplexType extension(
187: // ComplexType parent, Collection/*<AttributeDescriptor>*/ schema
188: // ) {
189: //
190: // //create a dummy type for the schema
191: // ComplexType type = null;
192: //
193: // if (parent instanceof ChoiceType) {
194: //
195: // Set choices = new HashSet();
196: // choices.addAll(((ChoiceType)parent).getAttributes());
197: // choices.addAll(schema);
198: //
199: // type = xmlFactory.createChoiceType(choices);
200: // }
201: // else if (parent instanceof SequenceType) {
202: //
203: // List sequence = new ArrayList();
204: //
205: // sequence.addAll(((SequenceType)parent).getAttributes());
206: // sequence.addAll(schema);
207: //
208: // type = xmlFactory.createSequenceType(sequence);
209: // }
210: // else if (parent instanceof ComplexType){
211: // List elements = new ArrayList();
212: // elements.addAll(((ComplexType)parent).getAttributes());
213: // elements.addAll(schema);
214: //
215: // //JD: fix this, passing in null here to avoid recalling this method
216: // // this method needs to be factored out somewhere else
217: // ComplexType ct = (ComplexType) parent;
218: // type = xmlFactory.createType(
219: // ct.getName(),elements,ct.isIdentified(),ct.isNillable().booleanValue(),
220: // ct.getRestrictions(),null,ct.isAbstract());
221: // }
222: //
223: // return type;
224: // }
225: //
226: //
227: // /**
228: // * Extending a schema.
229: // * <p>
230: // * Since we will be creating a new Descriptor we need the factory.
231: // */
232: // public ComplexType extension(ComplexType parent, ComplexType extend) {
233: //
234: // ComplexType type = null;
235: //
236: // if (parent instanceof ChoiceType && extend instanceof ChoiceType) {
237: //
238: // Set choices = new HashSet();
239: // choices.addAll(((ChoiceType)parent).getAttributes());
240: // choices.addAll(((ChoiceType)extend).getAttributes());
241: //
242: // type = xmlFactory.createChoiceType(choices);
243: // }
244: // else if (
245: // parent instanceof SequenceType && extend instanceof SequenceType
246: // ) {
247: //
248: // List sequence = new ArrayList();
249: //
250: // sequence.addAll(((SequenceType)parent).getAttributes());
251: // sequence.addAll(((SequenceType)extend).getAttributes());
252: //
253: // type = xmlFactory.createSequenceType(sequence);
254: // }
255: // else if (
256: // parent instanceof ComplexType && extend instanceof ComplexType
257: // ){
258: // List elements = new ArrayList();
259: // elements.addAll(((ComplexType)parent).getAttributes());
260: // elements.addAll(((ComplexType)extend).getAttributes());
261: //
262: // ComplexType ct = (ComplexType) extend;
263: //
264: // //JD: this is a bit of a hack, creating type manually (ie wihtout
265: // // factory, because we have constructed the schema manually
266: // type = new ComplexTypeImpl(
267: // ct.getName(),elements,ct.isIdentified(),
268: // ct.isNillable().booleanValue(),ct.getRestrictions(),ct,
269: // ct.isAbstract()
270: // );
271: //
272: // }
273: //
274: // return type;
275: //
276: // }
277: /**
278: * We can only restrict node if the restricftion is a subtype that used by
279: * node.
280: *
281: * @param node
282: * @param restrict
283: * @return restrict, iff restrict.getType() ISA node.getType()
284: */
285: AttributeDescriptor restrict(AttributeDescriptor node,
286: AttributeDescriptor restrict) {
287:
288: if (node.type() == restrict.type()) {
289: return restrict;
290: }
291: for (AttributeType/* <?> */type = (AttributeType) restrict
292: .type(); type != null; type = type.getSuper()) {
293: if (node.type().equals(type)) {
294: return restrict;
295: }
296: }
297: throw new IllegalArgumentException(
298: "Cannot restrict provided schema");
299: }
300:
301: Collection restriction(Collection schema, Collection restrict,
302: Collection restriction) {
303:
304: if (schema.size() != restrict.size()) {
305: throw new IllegalArgumentException(
306: "You must provide an exact structure match in order to implement restriction");
307: }
308:
309: Iterator i = schema.iterator();
310: Iterator j = restrict.iterator();
311: while (i.hasNext() && j.hasNext()) {
312: restriction.add(restrict((AttributeDescriptor) i.next(),
313: (AttributeDescriptor) j.next()));
314: }
315: return restriction;
316: }
317:
318: /**
319: * Locate type associated with provided name, or null if not found.
320: * <p>
321: * Namespaces are not taken in count, so if two properties share the same
322: * local name, the first one that matches will be returned.
323: * </p>
324: *
325: * @param schema
326: * @param name
327: * @return
328: */
329: static public AttributeType type(Collection schema, Name name) {
330: AttributeDescriptor node = node(schema, name);
331: if (node != null)
332: return (AttributeType) node.type();
333: return null;
334: }
335:
336: /**
337: * Locate type associated with provided name, or null if not found.
338: * <p>
339: * Namespaces are not taken in count, so if two properties share the same
340: * local name, the first one that matches will be returned.
341: * </p>
342: *
343: * @param schema
344: * @param name
345: * @return
346: */
347: static public AttributeType type(ComplexType schema, String name) {
348: return type(schema, Types.typeName(name));
349: }
350:
351: /**
352: * Locate type associated with provided name, or null if not found.
353: *
354: * @param schema
355: * @param name
356: * @return
357: */
358: static public AttributeType type(ComplexType schema, Name name) {
359: AttributeDescriptor node = node(schema, name);
360: if (node != null)
361: return (AttributeType) node.type();
362: return null;
363: }
364:
365: /**
366: * Finds the first node associated with the provided name disregarding
367: * namespaces
368: *
369: * @param schema
370: * @param name
371: * @return
372: */
373: static public AttributeDescriptor node(ComplexType schema,
374: String name) {
375: // return node(schema,new org.geotools.feature.Name(name));
376:
377: for (Iterator itr = list(schema).iterator(); itr.hasNext();) {
378: AttributeDescriptor node = (AttributeDescriptor) itr.next();
379:
380: if (node.getName() == null) {
381: // this may be due to old api usage style, where
382: // only types had names
383: LOGGER.warning("node has no name set, try to fix! "
384: + node);
385: if (node.type().getName().getLocalPart().equals(name)) {
386: return node;
387: }
388: } else {
389: // this is the correct usage
390: if (node.getName().getLocalPart().equals(name)) {
391: return node;
392: }
393: }
394:
395: }
396: AttributeType super Type = schema.getSuper();
397: if (super Type instanceof ComplexType) {
398: return node((ComplexType) super Type, name);
399: }
400: return null;
401: }
402:
403: // static public List nodes(Attribute schema) {
404: // List nodes = new ArrayList();
405: //
406: // for (Iterator itr = list(schema).iterator(); itr.hasNext();) {
407: // Descriptor child = (Descriptor)itr.next();
408: // if (child instanceof AttributeDescriptor) {
409: // AttributeDescriptor node = (AttributeDescriptor) child;
410: // nodes.add(node);
411: // }
412: // }
413: // return nodes;
414: // }
415:
416: /**
417: * Finds the node associated with the provided name.
418: *
419: * @param schema
420: * @param name
421: * @return AttributeDescriptor assoicated with provided name, or null if not
422: * found.
423: */
424: static public AttributeDescriptor node(ComplexType schema, Name name) {
425: return node(list(schema), name);
426: }
427:
428: /**
429: * Finds the node associated with the provided name.
430: *
431: * @param schema
432: * @param name
433: * @return AttributeDescriptor assoicated with provided name, or null if not
434: * found.
435: */
436: static public AttributeDescriptor node(Collection schema, Name name) {
437: for (Iterator itr = schema.iterator(); itr.hasNext();) {
438:
439: AttributeDescriptor node = (AttributeDescriptor) itr.next();
440:
441: Name nodeName = node.getName();
442: if (nodeName == null) {
443: // this may be due to old api usage style, where
444: // only types had names
445: LOGGER.warning("node has no name set, try to fix! "
446: + node);
447: Name name2 = node.type().getName();
448: if (null == name.getNamespaceURI()) {
449: if (name.getLocalPart().equals(
450: nodeName.getLocalPart())) {
451: return node;
452: }
453: } else if (nodeName.getNamespaceURI().equals(
454: name.getNamespaceURI())
455: && nodeName.getLocalPart().equals(
456: name.getLocalPart())) {
457: return node;
458: }
459: } else {
460: // this is the correct usage
461: if (name.getNamespaceURI() != null) {
462: if (name.getLocalPart().equals(
463: nodeName.getLocalPart())) {
464: return node;
465: }
466: } else if (name.equals(nodeName)) {
467: return node;
468: }
469: }
470:
471: }
472: return null;
473: }
474:
475: /**
476: * Finds the node associated with the provided type.
477: * <p>
478: * Note a type may be included in more then one node, in which case this
479: * will only find the first one.
480: * </p>
481: *
482: * @param schema
483: * @param type
484: * @return AttributeDescriptor assoicated with provided name, or null if not
485: * found.
486: */
487: static public AttributeDescriptor node(ComplexType schema,
488: AttributeType type) {
489: for (Iterator itr = list(schema).iterator(); itr.hasNext();) {
490: AttributeDescriptor node = (AttributeDescriptor) itr.next();
491: if (node.type() == type) {
492: return node;
493: }
494: }
495: return null;
496: }
497:
498: /**
499: * List of nodes matching AttributeType.
500: *
501: * @param schema
502: * @param type
503: * @return List of nodes for the provided type, or empty.
504: */
505: static public List/* <AttributeDescriptor> */nodes(
506: ComplexType schema, AttributeType type) {
507: List/* <AttributeDescriptor> */nodes = new ArrayList/* <AttributeDescriptor> */();
508: for (Iterator itr = list(schema).iterator(); itr.hasNext();) {
509: AttributeDescriptor node = (AttributeDescriptor) itr.next();
510: if (node.type().equals(type)) {
511: nodes.add(node);
512: }
513: }
514: return nodes;
515: }
516:
517: /**
518: * List of types described by this schema.
519: * <p>
520: * On the cases where order matters, the returned list preserves the order
521: * of descriptors declared in <code>schema</code>
522: * </p>
523: *
524: * @param type
525: * @return List of nodes for the provided type, or empty.
526: */
527: static public List/* <AttributeType> */types(AttributeType type) {
528: List/* <AttributeType> */types = new ArrayList/* <AttributeType> */();
529: for (Iterator itr = list(type).iterator(); itr.hasNext();) {
530: AttributeDescriptor node = (AttributeDescriptor) itr.next();
531: types.add(node.type());
532: }
533: return types;
534: }
535:
536: /**
537: * True if there may be more then one AttributeType in the schema.
538: * <p>
539: * This may happen if:
540: * <ul>
541: * <li>The AttributeType is referenced by more then one node.
542: * <li>The node referencing the type has multiplicy greater then 1
543: * </ul>
544: *
545: * @param schema
546: * @param type
547: * @return
548: */
549: public static boolean multiple(ComplexType schema,
550: AttributeType type) {
551: // return maxOccurs( schema, type ) != 1;
552: return maxOccurs(schema, type) > 1;
553: }
554:
555: public static int maxOccurs(ComplexType schema, AttributeType type) {
556: List/* <AttributeDescriptor> */nodes = nodes(schema, type);
557: if (nodes.isEmpty())
558: return 0;
559:
560: int max = 0;
561: for (Iterator itr = nodes.iterator(); itr.hasNext();) {
562: AttributeDescriptor node = (AttributeDescriptor) itr.next();
563: if (max == Integer.MAX_VALUE) {
564: return Integer.MAX_VALUE;
565: }
566: max += node.getMaxOccurs();
567: }
568: return max;
569: }
570:
571: /**
572: * Returns the list of descriptors defined in the provided schema,
573: * preserving declaration order when relevant.
574: *
575: * @param schema
576: * @return
577: */
578: // @SuppressWarnings("unchecked")
579: static public List/* <? extends Descriptor> */list(
580: AttributeType type) {
581:
582: ArrayList list = new ArrayList();
583:
584: if (type instanceof ComplexType) {
585: list = new ArrayList(((ComplexType) type).attributes());
586: }
587:
588: return list;
589:
590: // if (schema instanceof OrderedDescriptor) {
591: // return ((OrderedDescriptor) schema).sequence();
592: // } else if (schema instanceof AllDescriptor) {
593: // return new ArrayList/*<AttributeDescriptor>*/(((AllDescriptor)
594: // schema)
595: // .all());
596: // } else if (schema instanceof ChoiceDescriptor) {
597: // return new ArrayList/*<Descriptor>*/(((ChoiceDescriptor) schema)
598: // .options());
599: // }
600: //
601: // return Collections.EMPTY_LIST;
602:
603: }
604:
605: /**
606: * Determines if a collection of attribute descriptors is "simple".
607: *
608: * @param schema
609: * Collection of attribute descriptors.
610: *
611: * @return True if schema is simple, otherwise false.
612: */
613: public static boolean isSimple(
614: Collection/* <AttributeDescriptor> */schema) {
615: for (Iterator itr = schema.iterator(); itr.hasNext();) {
616: AttributeDescriptor d = (AttributeDescriptor) itr.next();
617: if (d.getMinOccurs() != 1 || d.getMaxOccurs() != 1) {
618: return false;
619: }
620: if (d.type() instanceof ComplexType) {
621: return false;
622: }
623: }
624:
625: return true;
626: }
627: }
|