/* * Copyright 2013 Guidewire Software, Inc. */ package gw.internal.xml.xsd.typeprovider; import gw.internal.xml.XmlDeserializationContext; import gw.internal.xml.XmlSimpleValueInternals; import gw.internal.xml.XmlSimpleValueValidationContext; import gw.internal.xml.XmlTypeInstanceInternals; import gw.internal.xml.xsd.typeprovider.schema.XmlSchemaAny; import gw.internal.xml.xsd.typeprovider.schema.XmlSchemaAttribute; import gw.internal.xml.xsd.typeprovider.schema.XmlSchemaComplexContent; import gw.internal.xml.xsd.typeprovider.schema.XmlSchemaComplexContentExtension; import gw.internal.xml.xsd.typeprovider.schema.XmlSchemaComplexContentRestriction; import gw.internal.xml.xsd.typeprovider.schema.XmlSchemaComplexType; import gw.internal.xml.xsd.typeprovider.schema.XmlSchemaElement; import gw.internal.xml.xsd.typeprovider.schema.XmlSchemaObject; import gw.internal.xml.xsd.typeprovider.schema.XmlSchemaSimpleContent; import gw.internal.xml.xsd.typeprovider.schema.XmlSchemaSimpleContentExtension; import gw.internal.xml.xsd.typeprovider.schema.XmlSchemaSimpleContentRestriction; import gw.internal.xml.xsd.typeprovider.schema.XmlSchemaSimpleType; import gw.internal.xml.xsd.typeprovider.schema.XmlSchemaSimpleTypeRestriction; import gw.internal.xml.xsd.typeprovider.schema.XmlSchemaType; import gw.internal.xml.xsd.typeprovider.simplevaluefactory.XmlSimpleValueFactory; import gw.internal.xml.xsd.typeprovider.validator.XmlSimpleValueValidator; import gw.lang.reflect.ConstructorInfoBuilder; import gw.lang.reflect.IConstructorHandler; import gw.lang.reflect.IConstructorInfo; import gw.lang.reflect.ILocationAwareFeature; import gw.lang.reflect.IMethodInfo; import gw.lang.reflect.IPropertyAccessor; import gw.lang.reflect.IPropertyInfo; import gw.lang.reflect.IType; import gw.lang.reflect.LocationInfo; import gw.lang.reflect.ParameterInfoBuilder; import gw.lang.reflect.PropertyInfoBuilder; import gw.lang.reflect.TypeSystem; import gw.lang.reflect.gs.IGosuObject; import gw.lang.reflect.java.IAsmJavaClassInfo; import gw.lang.reflect.java.IJavaClassConstructor; import gw.lang.reflect.java.IJavaClassInfo; import gw.lang.reflect.java.IJavaType; import gw.lang.reflect.java.JavaTypes; import gw.util.GosuExceptionUtil; import gw.util.concurrent.LockingLazyVar; import gw.xml.IXmlSchemaEnumValue; import gw.xml.XmlBase; import gw.xml.XmlElement; import gw.xml.XmlSimpleValue; import gw.xml.XmlTypeInstance; import javax.xml.namespace.QName; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.util.AbstractList; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Set; /** * IType for statically typed XmlTypeInstance types. See XmlTypeInstance for a discussion of what an XmlTypeInstance is. */ public class XmlSchemaTypeInstanceTypeData<T> extends XmlSchemaTypeData<T> implements IXmlSchemaTypeInstanceTypeData<T>, ILocationAwareFeature { private boolean _initialized; private XmlSchemaTypeSchemaInfo _schemaInfo; private static final String VALUE_PROPERTY_NAME = "$Value"; private static final String QNAME_PROPERTY_NAME = "$QNAME"; private final XmlSchemaResourceTypeLoaderBase _typeLoader; private final String _typeName; private final boolean _anonymousType; private final XmlSchemaType _xsdType; private final T _context; private static final String ELEMENT_QNAME_PREFIX = "$ELEMENT_QNAME_"; private static final String ATTRIBUTE_QNAME_PREFIX = "$ATTRIBUTE_QNAME_"; private LockingLazyVar<IType> _superType = new LockingLazyVar<IType>() { @Override protected IType init() { XmlSchemaTypeInstanceTypeData superTypeData = _superTypeData.get(); return superTypeData == null ? TypeSystem.get( XmlTypeInstance.class ) : superTypeData.getType(); } }; private LockingLazyVar<XmlSchemaTypeInstanceTypeData> _superTypeData = new LockingLazyVar<XmlSchemaTypeInstanceTypeData>() { @Override protected XmlSchemaTypeInstanceTypeData init() { XmlSchemaTypeInstanceTypeData superType = null; if ( getXsdType() instanceof XmlSchemaComplexType ) { XmlSchemaComplexType complexType = (XmlSchemaComplexType) getXsdType(); if ( complexType.getContentModel() instanceof XmlSchemaComplexContent ) { XmlSchemaComplexContent contentModel = (XmlSchemaComplexContent) complexType.getContentModel(); if ( contentModel.getContent() instanceof XmlSchemaComplexContentExtension ) { XmlSchemaComplexContentExtension extension = (XmlSchemaComplexContentExtension) contentModel.getContent(); superType = (XmlSchemaTypeInstanceTypeData) XmlSchemaIndex.getGosuTypeDataBySchemaObject( getXsdType().getSchemaIndex().getXmlSchemaComplexTypeByQName( extension.getBaseTypeName() ) ); } else if ( contentModel.getContent() instanceof XmlSchemaComplexContentRestriction ) { XmlSchemaComplexContentRestriction restriction = (XmlSchemaComplexContentRestriction) contentModel.getContent(); QName baseTypeName = restriction.getBaseTypeName(); if ( baseTypeName != null ) { superType = (XmlSchemaTypeInstanceTypeData) XmlSchemaIndex.getGosuTypeDataBySchemaObject( getXsdType().getSchemaIndex().getXmlSchemaComplexTypeByQName( baseTypeName ) ); } } } else if ( complexType.getContentModel() instanceof XmlSchemaSimpleContent ) { XmlSchemaSimpleContent contentModel = (XmlSchemaSimpleContent) complexType.getContentModel(); if ( contentModel.getContent() instanceof XmlSchemaSimpleContentExtension ) { XmlSchemaSimpleContentExtension extension = (XmlSchemaSimpleContentExtension) contentModel.getContent(); superType = (XmlSchemaTypeInstanceTypeData) XmlSchemaIndex.getGosuTypeDataBySchemaObject( getXsdType().getSchemaIndex().getXmlSchemaTypeByQName( extension.getBaseTypeName() ) ); } else if ( contentModel.getContent() instanceof XmlSchemaSimpleContentRestriction ) { XmlSchemaSimpleContentRestriction restriction = (XmlSchemaSimpleContentRestriction) contentModel.getContent(); superType = (XmlSchemaTypeInstanceTypeData) XmlSchemaIndex.getGosuTypeDataBySchemaObject( getXsdType().getSchemaIndex().getXmlSchemaTypeByQName( restriction.getBaseTypeName() ) ); } } } else { XmlSchemaSimpleType simpleType = (XmlSchemaSimpleType) getXsdType(); if ( simpleType.getContent() instanceof XmlSchemaSimpleTypeRestriction ) { XmlSchemaSimpleTypeRestriction restriction = (XmlSchemaSimpleTypeRestriction) simpleType.getContent(); superType = (XmlSchemaTypeInstanceTypeData) XmlSchemaIndex.getGosuTypeDataBySchemaObject( getXsdType().getSchemaIndex().getXmlSchemaSimpleTypeByQName( restriction.getBaseTypeName() ) ); } if ( superType == null ) { if ( XmlSchemaIndex.ANY_SIMPLE_TYPE_QNAME.equals( simpleType.getQName() ) ) { superType = (XmlSchemaTypeInstanceTypeData) XmlSchemaIndex.getGosuTypeDataBySchemaObject( getXsdType().getSchemaIndex().getXmlSchemaComplexTypeByQName( XmlSchemaIndex.ANY_TYPE_QNAME ) ); } else { superType = (XmlSchemaTypeInstanceTypeData) XmlSchemaIndex.getGosuTypeDataBySchemaObject( getXsdType().getSchemaIndex().getXmlSchemaSimpleTypeByQName( XmlSchemaIndex.ANY_SIMPLE_TYPE_QNAME ) ); } } } return superType; } }; private final LockingLazyVar<Constructor<?>> _constructorInternal = new LockingLazyVar<Constructor<?>>() { @Override protected Constructor<?> init() { // Find the most specific java-backed type in the hierarchy. There will always be at least one (AnyType) IType type = getType(); while ( true ) { try { Constructor<?> ctor = Class.forName( "gw.internal.schema." + type.getName() ).getDeclaredConstructor( IType.class, Object.class ); ctor.setAccessible( true ); return ctor; } catch ( ClassNotFoundException ex ) { type = type.getSupertype(); // continue } catch ( Exception ex ) { throw GosuExceptionUtil.forceThrow( ex ); } } } }; public XmlSchemaTypeInstanceTypeData( XmlSchemaIndex<T> schemaIndex, XmlSchemaResourceTypeLoaderBase typeLoader, String typeName, XmlSchemaType xsdType, boolean anonymousType, T context ) { super( schemaIndex ); _typeLoader = typeLoader; _typeName = typeName; _xsdType = xsdType; _anonymousType = anonymousType; _context = context; } public XmlSchemaResourceTypeLoaderBase getTypeLoader() { return _typeLoader; } public XmlSchemaType getXsdType() { return _xsdType; } @Override public boolean prefixSuperProperties() { return getSuperType().equals( TypeSystem.get( XmlTypeInstance.class ) ); } @Override public IType getSupertypeToCopyPropertiesFrom() { return TypeSystem.get( XmlTypeInstance.class ); } @Override public long getFingerprint() { return getSchemaIndex().getFingerprint(); } @Override public Class getBackingClass() { IJavaClassInfo clazz = getSchemaIndex().getGeneratedClass(getType().getName()); return (!(clazz instanceof IAsmJavaClassInfo) && clazz != null) ? clazz.getBackingClass() : XmlTypeInstance.class; } @Override public IJavaClassInfo getBackingClassInfo() { IJavaClassInfo clazz = getSchemaIndex().getGeneratedClass(getType().getName()); return clazz != null ? clazz : (JavaTypes.getSystemType(XmlTypeInstance.class)).getBackingClassInfo(); } public List<IPropertyInfo> getDeclaredProperties() { maybeInit(); List<IPropertyInfo> props = new ArrayList<IPropertyInfo>(); // create "QNAME" constant if this is a top-level type if ( getXsdType().getQName() != null ) { makeQNameConstant( props ); } // create "Value" property if this is a simple type or has a simple content if ( _schemaInfo.hasSimpleContent() ) { makeSimpleValueProperty( props ); } Map<XmlSchemaPropertyType,Map<QName,XmlSchemaPropertySpec>> todo = new HashMap<XmlSchemaPropertyType, Map<QName, XmlSchemaPropertySpec>>(); boolean gotRestriction = false; XmlSchemaTypeSchemaInfo schemaInfo = _schemaInfo; while ( schemaInfo != null ) { for ( XmlSchemaPropertySpec prop : schemaInfo.getProperties() ) { if ( gotRestriction && prop.getPropertyType() == XmlSchemaPropertyType.ELEMENT ) { continue; // stop grabbing super element properties once we hit a complex restriction } Map<QName, XmlSchemaPropertySpec> map = todo.get( prop.getPropertyType() ); if ( map == null ) { map = new HashMap<QName, XmlSchemaPropertySpec>(); todo.put( prop.getPropertyType(), map ); } if ( ! map.containsKey( prop.getQName() ) ) { map.put( prop.getQName(), prop ); } } if ( schemaInfo.isComplexRestriction() ) { gotRestriction = true; } schemaInfo = schemaInfo.getSuperElementInfo(); } for ( Map<QName, XmlSchemaPropertySpec> map : todo.values() ) { for ( XmlSchemaPropertySpec prop : map.values() ) { if ( ! prop.isProhibited() ) { makeChildProperties( props, prop ); } } } return props; } private void makeQNameConstant( List<IPropertyInfo> props ) { props.add( new PropertyInfoBuilder() .withLocation( getLocationInfo() ) .withName( QNAME_PROPERTY_NAME ) .withType( JavaTypes.QNAME() ) .withDescription( "The QName of this type" ) .withWritable( false ) .withStatic() .withAccessor( new IPropertyAccessor() { @Override public Object getValue( Object ctx ) { return getXsdType().getQName(); } @Override public void setValue( Object ctx, Object value ) { throw new UnsupportedOperationException(); } } ) .build( this ) ); } private void makeChildProperties( List<IPropertyInfo> props, final XmlSchemaPropertySpec prop ) { if ( prop.getXmlSchemaObject() instanceof XmlSchemaAny ) { return; } if ( prop.getSimpleTypePropertyName() != null ) { makeSimpleValueChildProperty( props, prop ); } if ( prop.getElementPropertyName() != null ) { makeChildElementProperty( props, prop ); } if ( prop.getQNamePropertyName() != null ) { makeQNameStaticProperty( props, prop ); } } private void makeQNameStaticProperty( List<IPropertyInfo> props, final XmlSchemaPropertySpec prop ) { props.add( new PropertyInfoBuilder() .withLocation( prop.getXmlSchemaObject().getLocationInfo() ) .withName( prop.getQNamePropertyName() ) .withStatic() .withWritable( false ) .withType( JavaTypes.QNAME() ) .withDescription( "Returns " + ( prop.getQName().getNamespaceURI().length() == 0 ? ( "{}" + prop.getQName().getLocalPart() ) : prop.getQName().toString() ) ) .withAccessor( new IPropertyAccessor() { @Override public Object getValue(Object ctx) { return prop.getQName(); } @Override public void setValue(Object ctx, Object value) { throw new UnsupportedOperationException(); } }) .build( this ) ); } private void makeChildElementProperty( List<IPropertyInfo> props, final XmlSchemaPropertySpec prop ) { final IType propType = prop.getElementPropertyGosuType( prop.isPlural() ); LocationInfo locationInfo = prop.getXmlSchemaObject().getLocationInfo(); props.add( new PropertyInfoBuilder() .withLocation( locationInfo ) .withName( prop.getElementPropertyName() ) .withType( propType ) .withAnnotations( new XmlSchemaAutoinsertAnnotationData( this ), new XmlSchemaAutocreateAnnotationData( getType(), this ) ) .withWritable( true ) .withAccessor( new IPropertyAccessor() { @Override public Object getValue( Object ctx ) { XmlBase node = (XmlBase) ctx; if ( prop.isPlural() ) { return XmlTypeInstanceInternals.instance()._getChildrenBySubstitutionGroup( node.getTypeInstance(), prop.getElementPropertyGosuType() ); } else { return XmlTypeInstanceInternals.instance()._getChildBySubstitutionGroup( node.getTypeInstance(), prop.getElementPropertyGosuType() ); } } @Override public void setValue( Object ctx, Object value ) { XmlBase node = (XmlBase) ctx; if ( prop.isPlural() ) { XmlTypeInstanceInternals.instance()._removeChildrenBySubstitutionGroup( node.getTypeInstance(), prop.getElementPropertyGosuType() ); //noinspection unchecked node.getChildren().addAll( (List<XmlElement>) value ); } else { // first remove any existing children by that name XmlTypeInstanceInternals.instance()._removeChildBySubstitutionGroup( node.getTypeInstance(), prop.getElementPropertyGosuType() ); if ( value != null ) { XmlElement child = (XmlElement) value; node.addChild( child ); } } } } ) .build( this ) ); } private void makeSimpleValueChildProperty( List<IPropertyInfo> props, final XmlSchemaPropertySpec prop) { final IType simpleTypePropertyGosuType = prop.getSimpleTypePropertyGosuType( prop.isPlural() ); LocationInfo locationInfo = prop.getXmlSchemaObject().getLocationInfo(); props.add( new PropertyInfoBuilder() .withLocation( locationInfo ) .withName( prop.getSimpleTypePropertyName() ) .withType( simpleTypePropertyGosuType ) .withAnnotations( new XmlSchemaAutoinsertAnnotationData( this ), new XmlSchemaAutocreateAnnotationData( simpleTypePropertyGosuType, this ) ) .withWritable( true ) .withAccessor( new IPropertyAccessor() { @Override public Object getValue( Object ctx ) { XmlBase node = (XmlBase) ctx; if ( prop.isPlural() ) { return new SimpleValueList( node, prop ); } else { switch ( prop.getPropertyType() ) { case ATTRIBUTE: { XmlSimpleValue simpleValue = node.getAttributeSimpleValue( prop.getQName() ); if ( simpleValue == null ) { String defaultValue = prop.getDefaultValue(); if ( defaultValue != null ) { simpleValue = prop.getSimpleValueFactory().deserialize( new XmlDeserializationContext( null ), defaultValue, true ); } } if ( simpleValue == null ) { return null; } else { simpleValue = reserializeSimpleValueIfNecessary( simpleValue, prop ); return simpleValue.getGosuValue(); } } case ELEMENT: { XmlElement child; child = XmlTypeInstanceInternals.instance()._getChildBySubstitutionGroup( node.getTypeInstance(), prop.getElementPropertyGosuType() ); XmlSimpleValue simpleValue = null; if ( child != null ) { simpleValue = child.getSimpleValue(); if ( simpleValue != null ) { simpleValue = reserializeSimpleValueIfNecessary( simpleValue, prop ); } } return simpleValue == null ? null : simpleValue.getGosuValue(); } default: throw new RuntimeException( "Unknown schema object type: " + prop.getPropertyType() ); } } } @Override public void setValue( Object ctx, Object value ) { if ( prop.isPlural() ) { if ( value == null ) { throw new IllegalArgumentException( "Simple value list cannot be null" ); } @SuppressWarnings( { "MismatchedQueryAndUpdateOfCollection" } ) List list = (List) getValue( ctx ); list.clear(); List toSet = (List) value; //noinspection unchecked list.addAll( toSet ); } else { XmlBase node = (XmlBase) ctx; switch ( prop.getPropertyType() ) { case ATTRIBUTE: { if ( value instanceof String ) { // XML requires attributes to go through the whitespace "replace" function // This isn't strictly necessary here, but it provides a more uniform view when // compared with how whitespace handling would be done by the validator anyway // this really belongs somewhere else value = new XmlSimpleValueValidationContext().doReplace( (String) value ); } XmlSimpleValue storageValue = value == null ? null : gosuValueToStorageValue( prop, prop, value ); node.setAttributeSimpleValue( prop.getQName(), storageValue ); break; } case ELEMENT: { if ( value == null ) { XmlTypeInstanceInternals.instance()._removeChildBySubstitutionGroup( node.getTypeInstance(), prop.getElementPropertyGosuType() ); } else { XmlElement child = XmlTypeInstanceInternals.instance()._getChildBySubstitutionGroup( node.getTypeInstance(), prop.getElementPropertyGosuType() ); addSimpleContent( value, node, child, prop ); } break; } default: throw new RuntimeException( "Unknown schema object type: " + prop.getPropertyType() ); } } } } ) .build( this ) ); } private XmlSimpleValue reserializeSimpleValueIfNecessary( XmlSimpleValue simpleValue, XmlSchemaSimpleValueProvider prop ) { IType propGosuValueType = prop.getSimpleValueFactory().getGosuValueType(); if ( ! propGosuValueType.isAssignableFrom( simpleValue.getGosuValueType() ) ) { // xsd -> gosu type mapping mismatch ( byte does not extend int in Gosu as it does in XSD ) // this isn't quite right of course. you really want to check the simple type hierarchy or something, to see // if the two types are coercible if ( propGosuValueType instanceof IXmlSchemaEnumerationTypeData ) { IXmlSchemaEnumerationTypeData typeData = (IXmlSchemaEnumerationTypeData) propGosuValueType; simpleValue = XmlSimpleValue.makeEnumInstance( (IXmlSchemaEnumValue) typeData.deserialize( simpleValue ) ); } else { simpleValue = prop.getSimpleValueFactory().deserialize( new XmlDeserializationContext( null ), simpleValue.getStringValue(), false ); } } return simpleValue; } private void addSimpleContent( Object newValue, XmlBase parentElement, XmlElement child, XmlSchemaPropertySpec prop ) { if ( child == null ) { IType xmlElementType = XmlSchemaIndex.getGosuTypeBySchemaObject( prop.getXmlSchemaObject() ); child = (XmlElement) xmlElementType.getTypeInfo().getConstructor().getConstructor().newInstance(); parentElement.addChild( child ); } final XmlSchemaTypeSchemaInfo schemaInfo = XmlSchemaIndex.getSchemaInfoByType( child.getTypeInstance().getIntrinsicType() ); XmlSimpleValue storageValue = gosuValueToStorageValue( prop, schemaInfo, newValue ); child.setSimpleValue( storageValue ); } private XmlSimpleValue gosuValueToStorageValue( XmlSchemaSimpleValueProvider staticSchemaInfo, XmlSchemaSimpleValueProvider runtimeSchemaInfo, Object value ) { XmlSimpleValue storageValue = null; if ( value != null ) { XmlSimpleValueValidationContext context = new XmlSimpleValueValidationContext(); if ( runtimeSchemaInfo.getSimpleValueFactory().getGosuValueType().isAssignableFrom( staticSchemaInfo.getSimpleValueFactory().getGosuValueType() ) ) { // use schema factory on the value verbatim, since it handles gosu values of this type XmlSimpleValueFactory factory = runtimeSchemaInfo.getSimpleValueFactory(); if ( value instanceof String ) { // this really belongs somewhere else value = runtimeSchemaInfo.getValidator().collapseWhitespace( (String) value, context ); } storageValue = factory.gosuValueToStorageValue( value ); } else { // convert through property value factory first, then convert to string, then reparse using schema value factory String newValue = staticSchemaInfo.getSimpleValueFactory().gosuValueToStorageValue( value ).getStringValue(); newValue = runtimeSchemaInfo.getValidator().collapseWhitespace( newValue, context ); storageValue = runtimeSchemaInfo.getSimpleValueFactory().deserialize( new XmlDeserializationContext( null ), newValue, false ); } XmlSimpleValueInternals.instance().validate( storageValue, runtimeSchemaInfo.getValidator(), context ); } return storageValue; } private void makeSimpleValueProperty( List<IPropertyInfo> props) { props.add( new PropertyInfoBuilder() .withLocation( _xsdType.getLocationInfo() ) .withName( VALUE_PROPERTY_NAME ) .withType( _schemaInfo.getSimpleTypePropertyGosuType() ) .withAnnotations( new XmlSchemaAutoinsertAnnotationData( this ), new XmlSchemaAutocreateAnnotationData( _schemaInfo.getSimpleTypePropertyGosuType(), this ) ) .withWritable( true ) .withAccessor( new IPropertyAccessor() { @Override public Object getValue( Object ctx ) { XmlBase xsdTypeOrElement = (XmlBase) ctx; XmlSimpleValue simpleValue = xsdTypeOrElement.getSimpleValue(); if ( simpleValue != null ) { simpleValue = reserializeSimpleValueIfNecessary( simpleValue, _schemaInfo ); } return simpleValue == null ? null : simpleValue.getGosuValue(); } @Override public void setValue( Object ctx, Object value ) { XmlTypeInstance typeInstance = ( (XmlBase) ctx ).getTypeInstance(); final XmlSchemaTypeSchemaInfo runtimeSchemaInfo = XmlSchemaIndex.getSchemaInfoByType( typeInstance.getIntrinsicType() ); XmlSimpleValue storageValue = value == null ? null : gosuValueToStorageValue( _schemaInfo, runtimeSchemaInfo, value ); typeInstance.setSimpleValue( storageValue ); } } ) .build( this ) ); } public void maybeInit() { if ( ! _initialized ) { TypeSystem.lock(); try { if ( ! _initialized ) { XmlSimpleValueFactory xmlSimpleValueFactory = XmlSchemaIndex.getSimpleValueFactoryForSchemaType( getXsdType() ); XmlSimpleValueValidator validator = XmlSchemaIndex.getSimpleValueValidatorForSchemaType( getXsdType() ); _schemaInfo = new XmlSchemaTypeSchemaInfo( this, xmlSimpleValueFactory, validator ); LinkedHashMap<String, LinkedHashMap<XmlSchemaPropertyType, LinkedHashMap<QName,PropertySpec>>> properties = collateProperties(); Set<String> usedPropertyNames = new HashSet<String>(); for ( Map.Entry<String,LinkedHashMap<XmlSchemaPropertyType,LinkedHashMap<QName,PropertySpec>>> entry : properties.entrySet() ) { final String originalPropertyName = entry.getKey(); for ( Map.Entry<XmlSchemaPropertyType,LinkedHashMap<QName,PropertySpec>> entry2 : entry.getValue().entrySet() ) { final XmlSchemaPropertyType propertyType = entry2.getKey(); final String originalPropertyName2; if ( entry.getValue().size() > 1 ) { // multiple property types - qualify originalPropertyName2 = originalPropertyName + propertyType.getSuffix(); // differentiate _Element and _Attribute } else { originalPropertyName2 = originalPropertyName; } Map<QName, PropertySpec> map = entry2.getValue(); for ( Map.Entry<QName, PropertySpec> entry3 : map.entrySet() ) { QName qname = entry3.getKey(); PropertySpec propertySpec = entry3.getValue(); XmlSchemaObject xmlSchemaObject = propertySpec._xmlSchemaObject; boolean isPlural = propertySpec._isPlural; String elementPropertyName = null; String qnamePropertyName; String simpleTypePropertyName; if ( propertyType == XmlSchemaPropertyType.ELEMENT ) { elementPropertyName = XmlSchemaIndex.makeUniquePropertyName( usedPropertyNames, originalPropertyName2, _typeLoader.getPropertyNameNormalizationMode() ); qnamePropertyName = XmlSchemaIndex.makeUniquePropertyName( usedPropertyNames, ELEMENT_QNAME_PREFIX + elementPropertyName, _typeLoader.getPropertyNameNormalizationMode() ); simpleTypePropertyName = null; // simple type, or complex type with simple content - both cause two properties to be created.... Whatever and Whatever_elem if ( XmlSchemaIndex.hasSimpleContent( xmlSchemaObject ) ) { simpleTypePropertyName = elementPropertyName; elementPropertyName = XmlSchemaIndex.makeUniquePropertyName( usedPropertyNames, simpleTypePropertyName + "_elem", _typeLoader.getPropertyNameNormalizationMode() ); } } else { simpleTypePropertyName = XmlSchemaIndex.makeUniquePropertyName( usedPropertyNames, originalPropertyName2, _typeLoader.getPropertyNameNormalizationMode() ); qnamePropertyName = XmlSchemaIndex.makeUniquePropertyName( usedPropertyNames, ATTRIBUTE_QNAME_PREFIX + simpleTypePropertyName, _typeLoader.getPropertyNameNormalizationMode() ); } _schemaInfo.addProperty( new XmlSchemaPropertySpec( elementPropertyName, simpleTypePropertyName, qnamePropertyName, qname, xmlSchemaObject, propertySpec._xmlSchemaType, propertyType, isPlural, propertySpec._defaultValue, propertySpec._isProhibited ) ); } } } _initialized = true; } } catch ( Exception ex ) { throw new RuntimeException( "Error initializing typeinfo for " + _typeName, ex ); } finally { TypeSystem.unlock(); } } } private LinkedHashMap<String, LinkedHashMap<XmlSchemaPropertyType, LinkedHashMap<QName,PropertySpec>>> collateProperties() { LinkedHashMap<String, LinkedHashMap<XmlSchemaPropertyType, LinkedHashMap<QName,PropertySpec>>> properties = new LinkedHashMap<String, LinkedHashMap<XmlSchemaPropertyType, LinkedHashMap<QName,PropertySpec>>>(); //noinspection unchecked for ( XmlSchemaFlattenedChild child : (List<XmlSchemaFlattenedChild>)getXsdType().getSchemaIndex().getFlattenedChildrenBySchemaType( getXsdType() ) ) { XmlSchemaPropertyType propertyType; QName propertyName; XmlSchemaObject xmlSchemaObject = child.getXmlSchemaObject(); String defaultValue = null; boolean isProhibited; XmlSchemaType xmlSchemaType; if ( xmlSchemaObject instanceof XmlSchemaElement) { XmlSchemaElement xsdElement = (XmlSchemaElement) xmlSchemaObject; xsdElement = XmlSchemaIndex.getActualElement( xsdElement ); propertyName = xsdElement.getQName(); xmlSchemaType = XmlSchemaIndex.getSchemaTypeForElement( xsdElement ); propertyType = XmlSchemaPropertyType.ELEMENT; isProhibited = false; xmlSchemaObject = xsdElement; // in case it changed } else if ( xmlSchemaObject instanceof XmlSchemaAttribute ) { XmlSchemaAttribute xsdAttribute = (XmlSchemaAttribute) xmlSchemaObject; xsdAttribute = XmlSchemaIndex.getActualAttribute( xsdAttribute ); // default and fixed are mutually exclusive if ( xsdAttribute.getFixedValue() != null ) { defaultValue = xsdAttribute.getFixedValue(); } else { defaultValue = xsdAttribute.getDefaultValue(); } propertyName = xsdAttribute.getQName(); xmlSchemaType = XmlSchemaIndex.getSchemaTypeForAttribute( xsdAttribute ); propertyType = XmlSchemaPropertyType.ATTRIBUTE; isProhibited = xsdAttribute.isProhibited(); xmlSchemaObject = xsdAttribute; // in case it changed } else { throw new RuntimeException( "Unhandled schema object type: " + xmlSchemaObject.getClass().getName() ); } String normPropName = XmlSchemaIndex.makeCamelCase( XmlSchemaIndex.normalizeName( propertyName.getLocalPart(), _typeLoader.getPropertyNameNormalizationMode() ), _typeLoader.getPropertyNameNormalizationMode() ); LinkedHashMap<XmlSchemaPropertyType, LinkedHashMap<QName,PropertySpec>> propertyTypes = properties.get( normPropName ); if ( propertyTypes == null ) { propertyTypes = new LinkedHashMap<XmlSchemaPropertyType, LinkedHashMap<QName,PropertySpec>>(); properties.put( normPropName, propertyTypes ); } LinkedHashMap<QName, PropertySpec> map = propertyTypes.get( propertyType ); if ( map == null ) { map = new LinkedHashMap<QName, PropertySpec>(); propertyTypes.put( propertyType, map ); } boolean isPlural = _xsdType.getSchemaIndex().getChildPluralityBySchemaType( _xsdType, propertyType, propertyName ); PropertySpec schemaType = new PropertySpec( xmlSchemaObject, xmlSchemaType, isPlural, defaultValue, isProhibited ); map.put( propertyName, schemaType ); } return properties; } public List<IMethodInfo> getDeclaredMethods() { return Collections.emptyList(); } public List<IConstructorInfo> getDeclaredConstructors() { maybeInit(); if ( _xsdType.getName() != null && _xsdType.getName().startsWith( XmlSchemaIndex.REDEFINE_PREFIX ) ) { //constructor.withAccessibility( IRelativeTypeInfo.Accessibility.PRIVATE ); return Collections.emptyList(); } IConstructorHandler constructorHandler; final IJavaClassInfo _clazz = getSchemaIndex().getGeneratedClass( getType().getName() ); IType simpleType = null; if ( _schemaInfo.hasSimpleContent() ) { simpleType = _schemaInfo.getSimpleValueFactory().getGosuValueType(); } if ( _clazz == null ) { constructorHandler = new IConstructorHandler() { @Override public Object newInstance(Object... args) { return XmlTypeInstanceInternals.instance().create( getType(), _schemaInfo, args ); } }; } else { final IJavaClassConstructor noParamCtor; final IJavaClassConstructor oneParamCtor; IJavaType clazz = (IJavaType) TypeSystem.get(_clazz); noParamCtor = getConstructor(clazz.getBackingClassInfo()); if ( simpleType != null ) { IJavaClassInfo simpleTypeBackingClass; if ( simpleType instanceof IXmlType ) { IType type = TypeSystem.getByFullName("gw.internal.schema." + simpleType.getName()); simpleTypeBackingClass = ((IJavaType)type).getBackingClassInfo(); } else { simpleTypeBackingClass = ( (IJavaType) simpleType ).getBackingClassInfo(); } oneParamCtor = getConstructor( clazz.getBackingClassInfo(), simpleTypeBackingClass ); } else { oneParamCtor = null; } constructorHandler = new IConstructorHandler() { @Override public Object newInstance( Object... args ) { try { if ( args.length > 0 ) { //noinspection ConstantConditions return oneParamCtor.newInstance( args[0] ); } else { return noParamCtor.newInstance(); } } catch ( Throwable t ) { while ( t instanceof InvocationTargetException && t.getCause() != null ) { t = t.getCause(); } throw GosuExceptionUtil.forceThrow( t ); } } }; } List<IConstructorInfo> ctors = new ArrayList<IConstructorInfo>(); ctors.add( new ConstructorInfoBuilder().withConstructorHandler( constructorHandler ).build( this ) ); if ( simpleType != null ) { ctors.add( new ConstructorInfoBuilder().withConstructorHandler( constructorHandler ).withParameters( new ParameterInfoBuilder().withName( "value" ).withType( simpleType ) ).build( this ) ); } return ctors; } private IJavaClassConstructor getConstructor(IJavaClassInfo clazz, IJavaClassInfo param) { IJavaClassConstructor[] declaredConstructors = clazz.getDeclaredConstructors(); for (IJavaClassConstructor constructor : declaredConstructors) { IJavaClassInfo[] parameterTypes = constructor.getParameterTypes(); if (parameterTypes.length == 1 && parameterTypes[0].equals(param)) { return constructor; } } return null; } private IJavaClassConstructor getConstructor(IJavaClassInfo clazz) { IJavaClassConstructor[] declaredConstructors = clazz.getDeclaredConstructors(); for (IJavaClassConstructor constructor : declaredConstructors) { if (constructor.getParameterTypes().length == 0) { return constructor; } } return null; } @Override public boolean isFinal() { return true; } @Override public boolean isEnum() { return false; } @Override public IType getSuperType() { return _superType.get(); } public XmlSchemaTypeSchemaInfo getSchemaInfo() { maybeInit(); return _schemaInfo; } @Override public Constructor<?> getConstructorInternal() { return _constructorInternal.get(); } @Override public String getName() { return _typeName; } @Override public XmlSchemaObject getSchemaObject() { return _xsdType; } @Override public boolean isAnonymous() { return _anonymousType; } @Override public List<Class<?>> getAdditionalInterfaces() { //noinspection unchecked return Arrays.asList( IXmlSchemaTypeInstanceTypeData.class, ILocationAwareFeature.class ); } public XmlSchemaTypeInstanceTypeData getSuperTypeData() { return _superTypeData.get(); } @Override public LocationInfo getLocationInfo() { return _xsdType.getLocationInfo(); } private static class PropertySpec { private final XmlSchemaObject _xmlSchemaObject; private final XmlSchemaType _xmlSchemaType; private final boolean _isPlural; private final String _defaultValue; private final boolean _isProhibited; public PropertySpec( XmlSchemaObject xmlSchemaObject, XmlSchemaType xmlSchemaType, boolean isPlural, String defaultValue, boolean isProhibited ) { if ( ! ( xmlSchemaObject instanceof XmlSchemaElement || xmlSchemaObject instanceof XmlSchemaAttribute || xmlSchemaObject instanceof XmlSchemaAny ) ) { throw new IllegalArgumentException( "INTERNAL ERROR: Unsupported xml schema object: " + xmlSchemaObject ); } _xmlSchemaObject = xmlSchemaObject; _xmlSchemaType = xmlSchemaType; _isPlural = isPlural; _defaultValue = defaultValue; _isProhibited = isProhibited; } @Override public String toString() { return "PropertySpec{" + "xmlSchemaObject=" + _xmlSchemaObject + "xmlSchemaType=" + _xmlSchemaType + ", isPlural=" + _isPlural + ", defaultValue='" + _defaultValue + '\'' + '}'; } } public T getContext() { return _context; } private class SimpleValueList extends AbstractList<Object> implements IGosuObject { private final List<XmlElement> _items; private final XmlBase _node; private final XmlSchemaPropertySpec _prop; public SimpleValueList( XmlBase node, XmlSchemaPropertySpec prop ) { _node = node; _prop = prop; _items = XmlTypeInstanceInternals.instance()._getChildrenBySubstitutionGroup( node.getTypeInstance(), prop.getElementPropertyGosuType() ); } @Override public Object get( int index ) { XmlElement element = _items.get( index ); return getElementSimpleValue( element ); } private Object getElementSimpleValue( XmlElement element ) { if ( element == null ) { return null; } XmlSimpleValue simpleValue = element.getSimpleValue(); if ( simpleValue == null ) { return null; } simpleValue = reserializeSimpleValueIfNecessary( simpleValue, _prop ); return simpleValue.getGosuValue(); } @Override public int size() { return _items.size(); } @Override public IType getIntrinsicType() { return _prop.getSimpleTypePropertyGosuType( true ); } @Override public boolean add( Object value ) { addSimpleContent( value, _node, null, _prop ); return true; } @Override public void clear() { _items.clear(); } @Override public Object remove( int index ) { return getElementSimpleValue( _items.remove( index ) ); } } }