/*
* GeoTools - The Open Source Java GIS Toolkit
* http://geotools.org
*
* (C) 2004-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.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;
import javax.xml.XMLConstants;
import javax.xml.namespace.QName;
import org.geotools.feature.IllegalAttributeException;
import org.geotools.feature.NameImpl;
import org.geotools.util.Converters;
import org.opengis.feature.Attribute;
import org.opengis.feature.ComplexAttribute;
import org.opengis.feature.simple.SimpleFeatureType;
import org.opengis.feature.type.AttributeDescriptor;
import org.opengis.feature.type.AttributeType;
import org.opengis.feature.type.ComplexType;
import org.opengis.feature.type.FeatureType;
import org.opengis.feature.type.Name;
import org.opengis.feature.type.PropertyDescriptor;
import org.opengis.feature.type.PropertyType;
import org.opengis.filter.Filter;
import org.xml.sax.helpers.NamespaceSupport;
/**
* This is a set of utility methods used when <b>implementing</b> types.
* <p>
* This set of classes captures the all important how does it work questions,
* particularly with respect to super types.
* </p>
* FIXME: These methods need a Q&A check to confirm correct use of Super TODO:
* Cannot tell the difference in intent from FeatureTypes
*
* @author Jody Garnett, LISAsoft
* @author Justin Deoliveira, The Open Planning Project
*
*
*
* @source $URL$
*/
public class Types {
/**
* Ensures an attribute value is withing the restrictions of the AttributeDescriptor and
* AttributeType.
* @param attribute
* @return true if the attribute value is valid
*/
public static boolean isValid( Attribute attribute ){
try {
validate(attribute.getType(), attribute, attribute.getValue(), false );
return true;
}
catch (IllegalAttributeException invalid ){
return false;
}
}
/**
* Validates content against an attribute.
*
* @param attribute
* The attribute.
* @param attributeContent
* Content of attribute (often attribute.getValue()
*
* @throws IllegalAttributeException
* In the event that content violates any restrictions specified
* by the attribute.
*/
public static void validate(Attribute attribute, Object attributeContent)
throws IllegalAttributeException {
validate(attribute.getType(), attribute, attributeContent, false);
}
/**
*
* @param type AttributeType (often attribute.getType() )
* @param attribute Attribute being tested
* @param attributeContent Content of the attribute (often attribute.getValue() )
* @throws IllegalAttributeException
*/
public static void validate(AttributeType type, Attribute attribute, Object attributeContent)
throws IllegalAttributeException {
validate(type, attribute, attributeContent, false);
}
/**
*
* @param type AttributeType (often attribute.getType() )
* @param attribute Attribute being tested
* @param attributeContent Content of the attribute (often attribute.getValue() )
* @param isSuper True if super type is being checked
* @throws IllegalAttributeException
*/
protected static void validate(AttributeType type, Attribute attribute,
Object attributeContent, boolean isSuper) throws IllegalAttributeException {
if (type == null) {
throw new IllegalAttributeException("null type");
}
if (attributeContent == null) {
if (!attribute.isNillable()) {
throw new IllegalAttributeException(type.getName() + " not nillable");
}
return;
}
if (type.isIdentified() && attribute.getIdentifier() == null) {
throw new NullPointerException(type.getName() + " is identified, null id not accepted");
}
if (!isSuper) {
// JD: This is an issue with how the xml simpel type hierarchy
// maps to our current Java Type hiearchy, the two are inconsitent.
// For instance, xs:integer, and xs:int, the later extend the
// former, but their associated java bindings, (BigDecimal, and
// Integer)
// dont.
Class clazz = attributeContent.getClass();
Class binding = type.getBinding();
if (binding != null && binding != clazz && !binding.isAssignableFrom(clazz)) {
throw new IllegalAttributeException(clazz.getName()
+ " is not an acceptable class for " + type.getName()
+ " as it is not assignable from " + binding);
}
}
if (type.getRestrictions() != null) {
for (Filter f : type.getRestrictions()) {
if (!f.evaluate(attribute)) {
throw new IllegalAttributeException("Attribute instance (" + attribute.getIdentifier() + ")"
+ "fails to pass filter: " + f);
}
}
}
// move up the chain,
if (type.getSuper() != null) {
validate(type.getSuper(), attribute, attributeContent, true);
}
}
/**
* Ensure that attributeContent is a good value for descriptor.
*/
public static void validate(AttributeDescriptor descriptor,
Object value) throws IllegalAttributeException {
if (descriptor == null) {
throw new NullPointerException("Attribute descriptor required for validation");
}
if (value == null) {
if (!descriptor.isNillable()) {
throw new IllegalArgumentException(descriptor.getName() + " requires a non null value");
}
} else {
validate( descriptor.getType(), value, false );
}
}
/**
* Do our best to make the provided value line up with the needs of descriptor.
* <p>
* This helper method uses the Coverters api to convert the provided
* value into the required class. If the value is null (and the attribute
* is not nillable) a default value will be returned.
* @param descriptor Attribute descriptor we need to supply a value for.
* @param value The provided value
* @return Our best attempt to make a valid value
* @throws IllegalArgumentException if we really could not do it.
*/
public static Object parse(AttributeDescriptor descriptor, Object value) throws IllegalArgumentException {
if (value == null){
if( descriptor.isNillable()){
return descriptor.getDefaultValue();
}
}
else {
Class target = descriptor.getType().getBinding();
if ( !target.isAssignableFrom( value.getClass() ) ) {
// attempt to convert
Object converted = Converters.convert(value,target);
if ( converted != null ) {
return converted;
}
// else {
// throw new IllegalArgumentException( descriptor.getLocalName()+ " could not convert "+value+" into "+target);
// }
}
}
return value;
}
protected static void validate(final AttributeType type, final Object value, boolean isSuper) throws IllegalAttributeException {
if (!isSuper) {
// JD: This is an issue with how the xml simpel type hierarchy
// maps to our current Java Type hiearchy, the two are inconsitent.
// For instance, xs:integer, and xs:int, the later extend the
// former, but their associated java bindings, (BigDecimal, and
// Integer)
// dont.
Class clazz = value.getClass();
Class binding = type.getBinding();
if (binding != null && !binding.isAssignableFrom(clazz)) {
throw new IllegalAttributeException(clazz.getName()
+ " is not an acceptable class for " + type.getName()
+ " as it is not assignable from " + binding);
}
}
if (type.getRestrictions() != null && type.getRestrictions().size() > 0) {
for (Filter filter : type.getRestrictions()) {
if (!filter.evaluate(value)) {
throw new IllegalAttributeException( type.getName() + " restriction "+ filter + " not met by: " + value);
}
}
}
// move up the chain,
if (type.getSuper() != null) {
validate(type.getSuper(), value, true );
}
}
/**
* FeatureType comparison indicating if the description provided by two FeatureTypes is
* similar to the point data can be exchanged. This comparison is really very focused on the
* name / value contract and is willing to overlook details like length restrictions.
* <p>
* When creating compatible FeatureTypes you will find some systems have different abilities
* which is reflected in how well they support a given FeatureType.
* <p>
* As an example databases traditionally support variable length strings with a
* limit of 32 k; while a shapefile is limited to 256 characters. When working with data from
* both these data sources you will need to make adjustments based on these abilities.
* </p>
* If true is returned data conforming to the expected FeatureType can be used with the
* actual FeatureType.
* <p>
* After assertOrderCovered returns without error the following code will work:<pre><code>
* for( Property property : feature.getProperties() ){
* Object value = property.getValue();
*
* Property target = newFeature.getProperty( property.getName().getLocalPart() );
* target.setValue( value );
* }
* </code></pre>
* Specifically this says that between the two feature types data is assignable on a name by name
* basis.
*
* @param expected Expected FeatureType being used to compare against
* @param actual Actual FeatureType
* @return true if actual is equal to or a subset of the expected feature type.
*/
public static void assertNameAssignable( FeatureType expected, FeatureType actual){
// check feature type name
String expectedName = expected.getName().getLocalPart();
String actualName = actual.getName().getLocalPart();
if( !expectedName.equals( actualName ) ){
throw new IllegalAttributeException("Expected '"+expectedName+"' but was supplied '"+actualName+"'.");
}
// check attributes names
Set<String> names = new TreeSet<String>();
for( PropertyDescriptor descriptor : actual.getDescriptors() ){
names.add( descriptor.getName().getLocalPart() );
}
for( PropertyDescriptor descriptor : expected.getDescriptors() ){
expectedName = descriptor.getName().getLocalPart();
if( names.contains( expectedName )){
names.remove( expectedName ); // only use once!
}
else {
throw new IllegalAttributeException("Expected to find a match for '"+expectedName+"' but was not available remaining names: " + names );
}
}
if( !names.isEmpty() ){
throw new IllegalAttributeException("Expected to find attributes '"+expectedName+"' but was not available remaining names: " + names );
}
// check attribute bindings
for( PropertyDescriptor expectedDescriptor : expected.getDescriptors() ){
expectedName = expectedDescriptor.getName().getLocalPart();
PropertyDescriptor actualDescriptor = actual.getDescriptor( expectedName );
Class<?> expectedBinding = expectedDescriptor.getType().getBinding();
Class<?> actualBinding = actualDescriptor.getType().getBinding();
if( !actualBinding.isAssignableFrom( expectedBinding )){
throw new IllegalArgumentException( "Expected "+expectedBinding.getSimpleName()+" for "+expectedName+" but was "+actualBinding.getSimpleName() );
}
}
}
/**
* SimpleFeatureType comparison indicating that data from one FeatureType can
* be exchanged with another - specifically ensuring that the order / value is a
* reasonable match with the expected number of attributes on each side and the
* values correctly assignable.
* <p>
* After assertOrderCovered returns without error the following code will work:<pre><code>
* List<Object> values = feature.getAttributes();
* newFeature.setAttributes( values );
* </code></pre>
*
* @param expected
* @param actual
* @return
*/
public static void assertOrderAssignable( SimpleFeatureType expected, SimpleFeatureType actual){
// check feature type name
String expectedName = expected.getName().getLocalPart();
String actualName = actual.getName().getLocalPart();
if( !expectedName.equals( actualName ) ){
throw new IllegalAttributeException("Expected '"+expectedName+"' but was supplied '"+actualName+"'.");
}
// check attributes names
if( expected.getAttributeCount() != actual.getAttributeCount() ){
throw new IllegalAttributeException("Expected "+expected.getAttributeCount()+" attributes, but was supplied "+actual.getAttributeCount() );
}
for( int i=0; i< expected.getAttributeCount(); i++){
Class<?> expectedBinding = expected.getDescriptor(i).getType().getBinding();
Class<?> actualBinding = actual.getDescriptor(i).getType().getBinding();
if( !actualBinding.isAssignableFrom( expectedBinding )){
String name = expected.getDescriptor(i).getLocalName();
throw new IllegalArgumentException( "Expected "+expectedBinding.getSimpleName()+" for "+name+" but was "+actualBinding.getSimpleName() );
}
}
}
/**
* Returns The name of attributes defined in the type.
*
* @param type The type.
*
*/
public static Name[] names(ComplexType type) {
ArrayList names = new ArrayList();
for (Iterator itr = type.getDescriptors().iterator(); itr.hasNext();) {
AttributeDescriptor ad = (AttributeDescriptor) itr.next();
names.add(ad.getName());
}
return (Name[]) names.toArray(new Name[names.size()]);
}
/**
* Creates a type name from a single non-qualified string.
*
* @param name The name, may be null
*
* @return The name in which getLocalPart() == name and getNamespaceURI() == null. Or null if name == null.
*/
public static Name typeName(String name) {
if (name == null) {
return null;
}
return new NameImpl(name);
}
/**
* Creates an attribute name from a single non-qualified string.
*
* @param name The name, may be null
* @param namespace The scope or namespace, may be null.
*
* @return The name in which getLocalPart() == name and getNamespaceURI() == namespace.
*/
public static Name typeName(String namespace, String name) {
return new NameImpl(namespace, name);
}
/**
* Creates a type name from another name.
*
* @param name The other name.
*/
public static Name typeName(Name name) {
return new NameImpl(name.getNamespaceURI(), name.getLocalPart());
}
/**
* Creates a set of attribute names from a set of strings.
* <p>
* This method returns null if names == null.
* </p>
* <p>
* The ith name has getLocalPart() == names[i] and getNamespaceURI() == null
* </p>
*/
public static Name[] toNames(String[] names) {
if (names == null) {
return null;
}
Name[] attributeNames = new Name[names.length];
for (int i = 0; i < names.length; i++) {
attributeNames[i] = typeName(names[i]);
}
return attributeNames;
}
/**
* Creates a set of type names from a set of strings.
* <p>
* This method returns null if names == null.
* </p>
* <p>
* The ith name has getLocalPart() == names[i] and getNamespaceURI() == null
* </p>
*/
public static Name[] toTypeNames(String[] names) {
if (names == null) {
return null;
}
Name[] typeNames = new Name[names.length];
for (int i = 0; i < names.length; i++) {
typeNames[i] = typeName(names[i]);
}
return typeNames;
}
/**
* Convenience method for turning an array of qualified names into a list of non qualified names.
*
*/
public static String[] fromNames(Name[] attributeNames) {
if (attributeNames == null) {
return null;
}
String[] names = new String[attributeNames.length];
for (int i = 0; i < attributeNames.length; i++) {
names[i] = attributeNames[i].getLocalPart();
}
return names;
}
/**
* Convenience method for turning an array of qualified names into a list of non qualified names.
*
*/
public static String[] fromTypeNames(Name[] typeNames) {
if (typeNames == null)
return null;
String[] names = new String[typeNames.length];
for (int i = 0; i < typeNames.length; i++) {
names[i] = typeNames[i].getLocalPart();
}
return names;
}
public static Name toTypeName(QName name) {
if (XMLConstants.NULL_NS_URI.equals(name.getNamespaceURI())) {
return typeName(name.getLocalPart());
}
return typeName(name.getNamespaceURI(), name.getLocalPart());
}
public static boolean equals(Name name, QName qName) {
if (name == null && qName != null) {
return false;
}
if (qName == null && name != null) {
return false;
}
if (XMLConstants.NULL_NS_URI.equals(qName.getNamespaceURI())) {
if (null != name.getNamespaceURI()) {
return false;
} else {
return name.getLocalPart().equals(qName.getLocalPart());
}
}
if (null == name.getNamespaceURI()
&& !XMLConstants.NULL_NS_URI.equals(qName.getNamespaceURI())) {
return false;
}
return name.getNamespaceURI().equals(qName.getNamespaceURI())
&& name.getLocalPart().equals(qName.getLocalPart());
}
/**
* Takes a prefixed attribute name and returns an {@link Name} by looking which namespace belongs the prefix to in
* {@link AppSchemaDataAccessDTO#getNamespaces()}.
*
* @param prefixedName , namespaces
* @return
* @throws IllegalArgumentException if <code>prefixedName</code> has no declared namespace in app-schema config file.
*/
public static Name degloseName(String prefixedName, NamespaceSupport namespaces)
throws IllegalArgumentException {
Name name = null;
if (prefixedName == null) {
return null;
}
int prefixIdx = prefixedName.lastIndexOf(':');
if (prefixIdx == -1) {
return Types.typeName(prefixedName);
// throw new IllegalArgumentException(prefixedName + " is not
// prefixed");
}
String nsPrefix = prefixedName.substring(0, prefixIdx);
String localName = prefixedName.substring(prefixIdx + 1);
String nsUri = namespaces.getURI(nsPrefix);
// handles undeclared namespaces in the app-schema mapping file
if (nsUri == null) {
throw new IllegalArgumentException("No namespace set: The namespace has not"
+ " been declared in the app-schema mapping file for name: " + nsPrefix + ":"
+ localName + " [Check the Namespaces section in the config file] ");
}
name = Types.typeName(nsUri, localName);
return name;
}
public static QName toQName(Name featurePath) {
return toQName(featurePath, null);
}
public static QName toQName(Name featurePath, NamespaceSupport ns) {
if (featurePath == null) {
return null;
}
String namespace = featurePath.getNamespaceURI();
String localName = featurePath.getLocalPart();
QName qName;
if (null == namespace) {
qName = new QName(localName);
} else {
if (ns != null) {
String prefix = ns.getPrefix(namespace);
if (prefix != null) {
qName = new QName(namespace, localName, prefix);
return qName;
}
}
qName = new QName(namespace, localName);
}
return qName;
}
/**
*
* @param name
* @return
* @deprecated use {@link #toTypeName(QName}
*/
public static Name toName(QName name) {
return toTypeName(name);
}
/**
* Converts content into a format which is used to store it internally within an attribute of a specific type.
*
* @param value the object to attempt parsing of.
*
* @throws IllegalArgumentException if parsing is attempted and is unsuccessful.
*/
public static Object parse(AttributeType type, Object content) throws IllegalArgumentException {
// JD: TODO: this is pretty lame
if (type instanceof AttributeTypeImpl) {
AttributeTypeImpl hack = (AttributeTypeImpl) type;
Object parsed = hack.parse(content);
if (parsed != null) {
return parsed;
}
}
return content;
}
/**
* Validates anattribute. <br>
* <p>
* Same result as calling:
*
* <pre>
* <code>
* validate(attribute.type(), attribute)
* </code>
* </pre>
*
* </p>
*
* @param attribute The attribute.
*
* @throws IllegalAttributeException In the event that content violates any restrictions specified by the attribute.
*/
public static void validate(Attribute attribute) throws IllegalAttributeException {
validate(attribute, attribute.getValue());
}
public static void validate(ComplexAttribute attribute) throws IllegalArgumentException {
}
public static void validate(ComplexAttribute attribute, Collection content)
throws IllegalArgumentException {
}
protected static void validate(ComplexType type, ComplexAttribute attribute, Collection content)
throws IllegalAttributeException {
// do normal validation
validate((AttributeType) type, (Attribute) attribute, (Object) content, false);
if (content == null) {
// not really much else we can do
return;
}
Collection schema = type.getDescriptors();
int index = 0;
for (Iterator itr = content.iterator(); itr.hasNext();) {
Attribute att = (Attribute) itr.next();
// att shall not be null
if (att == null) {
throw new NullPointerException("Attribute at index " + index
+ " is null. Attributes "
+ "can't be null. Do you mean Attribute.get() == null?");
}
// and has to be of one of the allowed types
AttributeType attType = att.getType();
boolean contains = false;
for (Iterator sitr = schema.iterator(); sitr.hasNext();) {
AttributeDescriptor ad = (AttributeDescriptor) sitr.next();
if (ad.getType().equals(attType)) {
contains = true;
break;
}
}
if (!contains) {
throw new IllegalArgumentException("Attribute of type " + attType.getName()
+ " found at index " + index
+ " but this type is not allowed by this descriptor");
}
index++;
}
// empty is allows, in such a case, content should be empty
if (type.getDescriptors().isEmpty()) {
if (!content.isEmpty()) {
throw new IllegalAttributeException(attribute.getDescriptor(),
"Type indicates empty attribute collection, content does not");
}
// we are done
return;
}
validateAll(type, attribute, content);
if (type.getSuper() != null) {
validate((ComplexType) type.getSuper(), attribute, content);
}
}
private static void validateAll(ComplexType type, ComplexAttribute att, Collection content)
throws IllegalAttributeException {
processAll(type.getDescriptors(), content);
}
private static void processAll(Collection/* <AttributeDescriptor> */all, Collection/*
* <Attribute>
*/content)
throws IllegalAttributeException {
// TODO: JD: this can be definitley be optimzed, as written its O(n^2)
// for each descriptor, count occurences of each matching attribute
ArrayList remaining = new ArrayList(content);
for (Iterator itr = all.iterator(); itr.hasNext();) {
AttributeDescriptor ad = (AttributeDescriptor) itr.next();
int min = ad.getMinOccurs();
int max = ad.getMaxOccurs();
int occurences = 0;
for (Iterator citr = remaining.iterator(); citr.hasNext();) {
Attribute a = (Attribute) citr.next();
if (a.getName().equals(ad.getName())) {
occurences++;
citr.remove();
}
}
if (occurences < ad.getMinOccurs() || occurences > ad.getMaxOccurs()) {
throw new IllegalAttributeException(ad, "Found " + occurences + " of "
+ ad.getName() + " when type" + "specifies between " + min + " and " + max);
}
}
if (!remaining.isEmpty()) {
throw new IllegalAttributeException((AttributeDescriptor) remaining.iterator().next(),
"Extra content found beyond the specified in the schema: " + remaining);
}
}
/**
* Determines if <code>parent</code> is a super type of <code>type</code>
*
* @param type The type in question.
* @param parent The possible parent type.
*
*/
public static boolean isSuperType(PropertyType type, PropertyType parent) {
while (type.getSuper() != null) {
type = type.getSuper();
if (type.equals(parent))
return true;
}
return false;
}
/**
* Returns the first descriptor matching the given local name within the given type.
*
* @param type The type, non null.
* @param name The name, non null.
*
* @return The first descriptor, or null if no match.
*/
public static PropertyDescriptor descriptor(ComplexType type, String name) {
List match = descriptors(type, name);
if (match.isEmpty())
return null;
return (PropertyDescriptor) match.get(0);
}
/**
* Returns the first descriptor matching the given name + namespace within the given type.
*
* @param type The type, non null.
* @param name The name, non null.
* @param namespace The namespace, non null.
*
* @return The first descriptor, or null if no match.
*/
public static PropertyDescriptor descriptor(ComplexType type, String name, String namespace) {
return descriptor(type, new NameImpl(namespace, name));
}
/**
* Returns the first descriptor matching the given name within the given type.
*
*
* @param type The type, non null.
* @param name The name, non null.
*
* @return The first descriptor, or null if no match.
*/
public static PropertyDescriptor descriptor(ComplexType type, Name name) {
List match = descriptors(type, name);
if (match.isEmpty())
return null;
return (PropertyDescriptor) match.get(0);
}
/**
* Returns the set of descriptors matching the given local name within the given type.
*
* @param type The type, non null.
* @param name The name, non null.
*
* @return The list of descriptors named 'name', or an empty list if none such match.
*/
public static List/* <PropertyDescriptor> */descriptors(ComplexType type, String name) {
if (name == null)
return Collections.EMPTY_LIST;
List match = new ArrayList();
for (Iterator itr = type.getDescriptors().iterator(); itr.hasNext();) {
PropertyDescriptor descriptor = (PropertyDescriptor) itr.next();
String localPart = descriptor.getName().getLocalPart();
if (name.equals(localPart)) {
match.add(descriptor);
}
}
// only look up in the super type if the descriptor is not found
// as a direct child definition
if (match.size() == 0) {
AttributeType superType = type.getSuper();
if (superType instanceof ComplexType) {
List superDescriptors = descriptors((ComplexType) superType, name);
match.addAll(superDescriptors);
}
}
return match;
}
/**
* Returns the set of descriptors matching the given name.
*
* @param type The type, non null.
* @param name The name, non null.
*
* @return The list of descriptors named 'name', or an empty list if none such match.
*/
public static List/* <PropertyDescriptor> */descriptors(ComplexType type, Name name) {
if (name == null)
return Collections.EMPTY_LIST;
List match = new ArrayList();
for (Iterator itr = type.getDescriptors().iterator(); itr.hasNext();) {
PropertyDescriptor descriptor = (PropertyDescriptor) itr.next();
Name descriptorName = descriptor.getName();
if (name.equals(descriptorName)) {
match.add(descriptor);
}
}
// only look up in the super type if the descriptor is not found
// as a direct child definition
if (match.size() == 0) {
AttributeType superType = type.getSuper();
if (superType instanceof ComplexType) {
List superDescriptors = descriptors((ComplexType) superType, name);
match.addAll(superDescriptors);
}
}
return match;
}
/**
* Returns the set of all descriptors of a complex type, including from supertypes.
*
* @param type The type, non null.
*
* @return The list of all descriptors.
*/
public static List<PropertyDescriptor> descriptors(ComplexType type) {
// get list of descriptors from types and all supertypes
List<PropertyDescriptor> children = new ArrayList<PropertyDescriptor>();
ComplexType loopType = type;
while (loopType != null) {
children.addAll(loopType.getDescriptors());
loopType = loopType.getSuper() instanceof ComplexType ? (ComplexType) loopType
.getSuper() : null;
}
return children;
}
/**
* Find a descriptor, taking in to account supertypes AND substitution groups
*
* @param parentType type
* @param name name of descriptor
* @return descriptor, null if not found
*/
public static PropertyDescriptor findDescriptor(ComplexType parentType, Name name) {
// get list of descriptors from types and all supertypes
List<PropertyDescriptor> descriptors = descriptors(parentType);
// find matching descriptor
for (Iterator<PropertyDescriptor> it = descriptors.iterator(); it.hasNext();) {
PropertyDescriptor d = it.next();
if (d.getName().equals(name)) {
return d;
}
}
// nothing found, perhaps polymorphism?? let's loop again
for (Iterator<PropertyDescriptor> it = descriptors.iterator(); it.hasNext();) {
List<AttributeDescriptor> substitutionGroup = (List<AttributeDescriptor>) it.next()
.getUserData().get("substitutionGroup");
if (substitutionGroup != null) {
for (Iterator<AttributeDescriptor> it2 = substitutionGroup.iterator(); it2
.hasNext();) {
AttributeDescriptor d = it2.next();
if (d.getName().equals(name)) { // BINGOOO !!
return d;
}
}
}
}
return null;
}
/**
* Find a descriptor, taking in to account supertypes AND substitution groups
*
* @param parentType type
* @param name name of descriptor
* @return descriptor, null if not found
*/
public static PropertyDescriptor findDescriptor(ComplexType parentType, String name) {
// get list of descriptors from types and all supertypes
List<PropertyDescriptor> descriptors = descriptors(parentType);
// find matching descriptor
for (Iterator<PropertyDescriptor> it = descriptors.iterator(); it.hasNext();) {
PropertyDescriptor d = it.next();
if (d.getName().getLocalPart().equals(name)) {
return d;
}
}
// nothing found, perhaps polymorphism?? let's loop again
for (Iterator<PropertyDescriptor> it = descriptors.iterator(); it.hasNext();) {
List<AttributeDescriptor> substitutionGroup = (List<AttributeDescriptor>) it.next()
.getUserData().get("substitutionGroup");
if (substitutionGroup != null) {
for (Iterator<AttributeDescriptor> it2 = substitutionGroup.iterator(); it2
.hasNext();) {
AttributeDescriptor d = it2.next();
if (d.getName().getLocalPart().equals(name)) { // BINGOOO !!
return d;
}
}
}
}
return null;
}
}