/* * GeoTools - The Open Source Java GIS Toolkit * http://geotools.org * * (C) 2002-2008, Open Source Geospatial Foundation (OSGeo) * * 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.type; import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; import java.util.List; import java.util.logging.Logger; import org.geotools.feature.NameImpl; import org.opengis.feature.type.AttributeDescriptor; import org.opengis.feature.type.AttributeType; import org.opengis.feature.type.ComplexType; import org.opengis.feature.type.Name; /** * Helper methods for dealing with Descriptor. * <p> * This methods opperate directly on the interfaces provided by geoapi, no * actual classes were harmed in the making of these utility methods. * </p> * * @author Jody Garnett * @author Justin Deoliveira * * @since 2.5 * * * @source $URL$ */ public class Descriptors { private static final Logger LOGGER = org.geotools.util.logging.Logging.getLogger(Descriptors.class.getPackage().getName()); /** * Wraps a list of {@link AttributeType} in {@link AttributeDescriptor}. * * @param typeList The list of attribute types. * @return The list of attribute descriptors. * * @see #wrapAttributeType(AttributeType) */ public final static List wrapAttributeTypes( List/*<AttributeType>*/ typeList ){ List descriptors = new ArrayList( typeList.size() ); for( Iterator i = typeList.iterator(); i.hasNext(); ){ AttributeType attributeType = (AttributeType) i.next(); descriptors.add( wrapAttributeType(attributeType)); } return descriptors; } /** * Wraps a {@link AttributeType} in {@link AttributeDescriptor}. * * @param type The attribute type. * @return The attribute descriptor. */ public final static AttributeDescriptor wrapAttributeType( AttributeType type ) { if ( type == null ) { return null; } return new AttributeDescriptorImpl(type, type.getName(), 1,1,true,null ); } /** * Returns the attribute descriptor from a list which matches the specified * name, or <code>null</code> if no such descriptor is found. * * @param descriptors The list of {@link AttributeDescriptor}. * @param name The name to match. * * @return The matching attribute descriptor, or <code>null</code>. */ public final static AttributeDescriptor find( List descriptors, Name name ){ if( name == null ) return null; for( Iterator i = descriptors.iterator(); i.hasNext(); ){ AttributeDescriptor attributeType = (AttributeDescriptor) i.next(); if( name.equals( attributeType.getType().getName() ) ){ return attributeType; } } return null; // no default geometry here? } // /** // * Handle subtyping in a "sensible" manner. // * <p> // * We explored using the XMLSchema of extention and restriction, and have // * instead opted for the traditional Java lanaguage notion of an override. // * <p> // * The concept of an overrided allows both: // * <ul> // * <li>extention - completly new attribtues are tacked on the "end" of the // * list // * <li>restriction - attribute with the same qname are used to specify // * additional (or replace) information provided by the parent. // * </ol> // * Note - even <b>removal</b> ( a complicated (and silly) use of // * restriction in XMLSchema) is supported. To remove simply override an // * attribute mentioned by the parent with multiplicity 0:0. // * </p> // * // * @param parent // * @param subtype // * @return Descriptor resulting by extending the provided schema // (collisions // * on qname are treated as overrides) // */ // public ComplexType subtype(ComplexType parent, ComplexType extend) { // /* // * if( schema instanceof AllDescriptor && subtype instanceof // * AllDescriptor ){ return subtype( (AllDescriptor) schema, // * (AllDescriptor) extend); } else if( schema instanceof // * ChoiceDescriptor && extend instanceof ChoiceDescriptor ){ return // * subtype( (ChoiceDescriptor) schema, (ChoiceDescriptor) extend); } // * else if( schema instanceof OrderedDescriptor && extend instanceof // * OrderedDescriptor ){ return subtype( (OrderedDescriptor) schema, // * (OrderedDescriptor) extend); } else { List<Descriptor> all = new // * ArrayList<Descriptor>(); all.add( schema ); all.add( extend ); // * return factory.ordered( all, 1, 1 ); } // */ // try { // return restriction(parent, extend); // } // catch(IllegalArgumentException structsDontMatch){ // return extension(parent, extend); // } // } // // public ComplexType subtype( // ComplexType parent, Collection/*<AttributeDescriptor>*/ schema // ) { // try { // return restriction(parent, schema); // } // catch(IllegalArgumentException structsDontMatch){ // return extension(parent,schema); // } // } /** * Restriction only works on exact structure match. * <p> * This is the way XMLSchema handles it ... * </p> * * @param schema * @param sub * @return */ // @SuppressWarnings("unchecked") // public ComplexType restriction(ComplexType parent,ComplexType restrict) { // // ComplexType type = null; // // if ( // parent instanceof ChoiceType && restrict instanceof ChoiceType // ) { // // Set choices = (Set) restriction( // ((ChoiceType)parent).getAttributes(), // ((ChoiceType)restrict).getAttributes(), // new HashSet() // ); // // type = xmlFactory.createChoiceType(choices); // } // else if (parent instanceof SequenceType && restrict instanceof // SequenceType) { // List sequence = (List) restriction( // ((SequenceType)parent).getAttributes(), // ((SequenceType)restrict).getAttributes(), // new ArrayList() // ); // // type = xmlFactory.createSequenceType(sequence); // } // else if ( // parent instanceof ComplexType && restrict instanceof ComplexType // ){ // List elements = (List) restriction( // ((ComplexType)parent).getAttributes(), // ((ComplexType)restrict).getAttributes(), // new ArrayList() // ); // // ComplexType ct = (ComplexType) restrict; // type = xmlFactory.createType( // ct.getName(),elements,ct.isIdentified(),ct.isNillable().booleanValue(), // ct.getRestrictions(),ct,ct.isAbstract()); // // } // else { // throw new IllegalArgumentException("Cannot restrict provided schema"); // } // // return type; // // } // public ComplexType restriction(ComplexType parent, // Collection/*<AttributeDescriptor>*/ schema) { // ComplexType type = null; // // if (parent instanceof ChoiceType) { // Set choices = (Set) restriction( // ((ChoiceType)parent).getAttributes(),schema,new HashSet() // ); // type = xmlFactory.createChoiceType(choices); // } // else if (parent instanceof SequenceType) { // List sequence = (List) restriction( // ((SequenceType)parent).getAttributes(), schema, new ArrayList() // ); // // type = xmlFactory.createSequenceType(sequence); // } // else if (parent instanceof ComplexType){ // List elements = (List) restriction( // ((ComplexType)parent).getAttributes(),schema, new ArrayList() // ); // // //duplicate parent type with new schema // //JD: this is a bit of a hack, creating type manually (ie wihtout // // factory, because we have constructed the schema manually // // ComplexType ct = (ComplexType) parent; // type = new ComplexTypeImpl( // ct.getName(),elements,ct.isIdentified(), // ct.isNillable().booleanValue(),ct.getRestrictions(),ct, // ct.isAbstract() // ); // type = xmlFactory.createType( // ct.getName(),elements,ct.isIdentified(),ct.isNillable().booleanValue(), // ct.getRestrictions(),null,ct.isAbstract()); // } // else { // throw new IllegalArgumentException("Cannot restrict provided schema"); // } // // return type; // } // // public ComplexType extension( // ComplexType parent, Collection/*<AttributeDescriptor>*/ schema // ) { // // //create a dummy type for the schema // ComplexType type = null; // // if (parent instanceof ChoiceType) { // // Set choices = new HashSet(); // choices.addAll(((ChoiceType)parent).getAttributes()); // choices.addAll(schema); // // type = xmlFactory.createChoiceType(choices); // } // else if (parent instanceof SequenceType) { // // List sequence = new ArrayList(); // // sequence.addAll(((SequenceType)parent).getAttributes()); // sequence.addAll(schema); // // type = xmlFactory.createSequenceType(sequence); // } // else if (parent instanceof ComplexType){ // List elements = new ArrayList(); // elements.addAll(((ComplexType)parent).getAttributes()); // elements.addAll(schema); // // //JD: fix this, passing in null here to avoid recalling this method // // this method needs to be factored out somewhere else // ComplexType ct = (ComplexType) parent; // type = xmlFactory.createType( // ct.getName(),elements,ct.isIdentified(),ct.isNillable().booleanValue(), // ct.getRestrictions(),null,ct.isAbstract()); // } // // return type; // } // // // /** // * Extending a schema. // * <p> // * Since we will be creating a new Descriptor we need the factory. // */ // public ComplexType extension(ComplexType parent, ComplexType extend) { // // ComplexType type = null; // // if (parent instanceof ChoiceType && extend instanceof ChoiceType) { // // Set choices = new HashSet(); // choices.addAll(((ChoiceType)parent).getAttributes()); // choices.addAll(((ChoiceType)extend).getAttributes()); // // type = xmlFactory.createChoiceType(choices); // } // else if ( // parent instanceof SequenceType && extend instanceof SequenceType // ) { // // List sequence = new ArrayList(); // // sequence.addAll(((SequenceType)parent).getAttributes()); // sequence.addAll(((SequenceType)extend).getAttributes()); // // type = xmlFactory.createSequenceType(sequence); // } // else if ( // parent instanceof ComplexType && extend instanceof ComplexType // ){ // List elements = new ArrayList(); // elements.addAll(((ComplexType)parent).getAttributes()); // elements.addAll(((ComplexType)extend).getAttributes()); // // ComplexType ct = (ComplexType) extend; // // //JD: this is a bit of a hack, creating type manually (ie wihtout // // factory, because we have constructed the schema manually // type = new ComplexTypeImpl( // ct.getName(),elements,ct.isIdentified(), // ct.isNillable().booleanValue(),ct.getRestrictions(),ct, // ct.isAbstract() // ); // // } // // return type; // // } /** * We can only restrict node if the restricftion is a subtype that used by * node. * * @param node * @param restrict * @return restrict, iff restrict.getType() ISA node.getType() */ AttributeDescriptor restrict(AttributeDescriptor node, AttributeDescriptor restrict) { if (node.getType() == restrict.getType()) { return restrict; } for (AttributeType/* <?> */type = restrict.getType(); type != null; type = type.getSuper()) { if (node.getType().equals(type)) { return restrict; } } throw new IllegalArgumentException("Cannot restrict provided schema"); } Collection restriction(Collection schema, Collection restrict, Collection restriction) { if (schema.size() != restrict.size()) { throw new IllegalArgumentException( "You must provide an exact structure match in order to implement restriction"); } Iterator i = schema.iterator(); Iterator j = restrict.iterator(); while (i.hasNext() && j.hasNext()) { restriction .add(restrict((AttributeDescriptor) i.next(), (AttributeDescriptor) j.next())); } return restriction; } /** * Locate type associated with provided name, or null if not found. * <p> * Namespaces are not taken in count, so if two properties share the same * local name, the first one that matches will be returned. * </p> * * @param schema * @param name * @return */ static public AttributeType type(Collection schema, Name name) { AttributeDescriptor node = node(schema, name); if (node != null) return node.getType(); return null; } /** * Locate type associated with provided name, or null if not found. * <p> * Namespaces are not taken in count, so if two properties share the same * local name, the first one that matches will be returned. * </p> * * @param schema * @param name * @return */ static public AttributeType type(ComplexType schema, String name) { return type(schema, new NameImpl(name)); } /** * Locate type associated with provided name, or null if not found. * * @param schema * @param name * @return */ static public AttributeType type(ComplexType schema, Name name) { AttributeDescriptor node = node(schema, name); if (node != null) return node.getType(); return null; } /** * Finds the first node associated with the provided name disregarding * namespaces * * @param schema * @param name * @return */ static public AttributeDescriptor node(ComplexType schema, String name) { // return node(schema,new org.geotools.feature.Name(name)); for (Iterator itr = list(schema).iterator(); itr.hasNext();) { AttributeDescriptor node = (AttributeDescriptor) itr.next(); if (node.getName() == null) { // this may be due to old api usage style, where // only types had names LOGGER.warning("node has no name set, try to fix! " + node); if (node.getType().getName().getLocalPart().equals(name)) { return node; } } else { // this is the correct usage if (node.getName().getLocalPart().equals(name)) { return node; } } } AttributeType superType = schema.getSuper(); if (superType instanceof ComplexType) { return node((ComplexType) superType, name); } return null; } // static public List nodes(Attribute schema) { // List nodes = new ArrayList(); // // for (Iterator itr = list(schema).iterator(); itr.hasNext();) { // Descriptor child = (Descriptor)itr.next(); // if (child instanceof AttributeDescriptor) { // AttributeDescriptor node = (AttributeDescriptor) child; // nodes.add(node); // } // } // return nodes; // } /** * Finds the node associated with the provided name. * * @param schema * @param name * @return AttributeDescriptor assoicated with provided name, or null if not * found. */ static public AttributeDescriptor node(ComplexType schema, Name name) { return node(list(schema), name); } /** * Finds the node associated with the provided name. * * @param schema * @param name * @return AttributeDescriptor assoicated with provided name, or null if not * found. */ static public AttributeDescriptor node(Collection schema, Name name) { for (Iterator itr = schema.iterator(); itr.hasNext();) { AttributeDescriptor node = (AttributeDescriptor) itr.next(); Name nodeName = node.getName(); if (nodeName == null) { // this may be due to old api usage style, where // only types had names LOGGER.warning("node has no name set, try to fix! " + node); Name name2 = node.getType().getName(); if (null == name.getNamespaceURI()) { if (name.getLocalPart().equals(name2.getLocalPart())) { return node; } } else if (name2.getNamespaceURI().equals(name.getNamespaceURI()) && name2.getLocalPart().equals(name.getLocalPart())) { return node; } } else { // this is the correct usage if (name.getNamespaceURI() != null) { if (name.getLocalPart().equals(nodeName.getLocalPart())) { return node; } } else if (name.equals(nodeName)) { return node; } } } return null; } /** * Finds the node associated with the provided type. * <p> * Note a type may be included in more then one node, in which case this * will only find the first one. * </p> * * @param schema * @param type * @return AttributeDescriptor assoicated with provided name, or null if not * found. */ static public AttributeDescriptor node(ComplexType schema, AttributeType type) { for (Iterator itr = list(schema).iterator(); itr.hasNext();) { AttributeDescriptor node = (AttributeDescriptor) itr.next(); if (node.getType() == type) { return node; } } return null; } /** * List of nodes matching AttributeType. * * @param schema * @param type * @return List of nodes for the provided type, or empty. */ static public List/* <AttributeDescriptor> */nodes(ComplexType schema, AttributeType type) { List/* <AttributeDescriptor> */nodes = new ArrayList/* <AttributeDescriptor> */(); for (Iterator itr = list(schema).iterator(); itr.hasNext();) { AttributeDescriptor node = (AttributeDescriptor) itr.next(); if (node.getType().equals(type)) { nodes.add(node); } } return nodes; } /** * List of types described by this schema. * <p> * On the cases where order matters, the returned list preserves the order * of descriptors declared in <code>schema</code> * </p> * * @param type * @return List of nodes for the provided type, or empty. */ static public List/* <AttributeType> */types(AttributeType type) { List/* <AttributeType> */types = new ArrayList/* <AttributeType> */(); for (Iterator itr = list(type).iterator(); itr.hasNext();) { AttributeDescriptor node = (AttributeDescriptor) itr.next(); types.add(node.getType()); } return types; } /** * True if there may be more then one AttributeType in the schema. * <p> * This may happen if: * <ul> * <li>The AttributeType is referenced by more then one node. * <li>The node referencing the type has multiplicy greater then 1 * </ul> * * @param schema * @param type * @return */ public static boolean multiple(ComplexType schema, AttributeType type) { // return maxOccurs( schema, type ) != 1; return maxOccurs(schema, type) > 1; } public static int maxOccurs(ComplexType schema, AttributeType type) { List/* <AttributeDescriptor> */nodes = nodes(schema, type); if (nodes.isEmpty()) return 0; int max = 0; for (Iterator itr = nodes.iterator(); itr.hasNext();) { AttributeDescriptor node = (AttributeDescriptor) itr.next(); if (max == Integer.MAX_VALUE) { return Integer.MAX_VALUE; } max += node.getMaxOccurs(); } return max; } /** * Returns the list of descriptors defined in the provided schema, * preserving declaration order when relevant. * * @param schema * @return */ // @SuppressWarnings("unchecked") static public List/* <? extends Descriptor> */list(AttributeType type) { ArrayList list = new ArrayList(); if (type instanceof ComplexType) { list = new ArrayList(((ComplexType) type).getDescriptors()); } return list; // if (schema instanceof OrderedDescriptor) { // return ((OrderedDescriptor) schema).sequence(); // } else if (schema instanceof AllDescriptor) { // return new ArrayList/*<AttributeDescriptor>*/(((AllDescriptor) // schema) // .all()); // } else if (schema instanceof ChoiceDescriptor) { // return new ArrayList/*<Descriptor>*/(((ChoiceDescriptor) schema) // .options()); // } // // return Collections.EMPTY_LIST; } /** * Determines if a collection of attribute descriptors is "simple". * * @param schema * Collection of attribute descriptors. * * @return True if schema is simple, otherwise false. */ public static boolean isSimple(Collection/* <AttributeDescriptor> */schema) { for (Iterator itr = schema.iterator(); itr.hasNext();) { AttributeDescriptor d = (AttributeDescriptor) itr.next(); if (d.getMinOccurs() != 1 || d.getMaxOccurs() != 1) { return false; } if (d.getType() instanceof ComplexType) { return false; } } return true; } }