/*
* Copyright 2013 Guidewire Software, Inc.
*/
package gw.internal.xml.xsd.typeprovider;
import gw.internal.xml.XmlDeserializationContext;
import gw.internal.xml.XmlSimpleValueBase;
import gw.internal.xml.xsd.typeprovider.schema.XmlSchemaEnumerationFacet;
import gw.internal.xml.xsd.typeprovider.schema.XmlSchemaObject;
import gw.internal.xml.xsd.typeprovider.schema.XmlSchemaType;
import gw.internal.xml.xsd.typeprovider.simplevaluefactory.XmlSimpleValueFactory;
import gw.lang.reflect.*;
import gw.lang.reflect.gs.IGosuObject;
import gw.lang.reflect.java.*;
import gw.util.concurrent.LockingLazyVar;
import gw.xml.IXmlSchemaEnumValue;
import gw.xml.XmlSimpleValue;
import gw.xml.XmlSimpleValueException;
import javax.xml.namespace.QName;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* IType for enumerations defined in an XSD.
*/
public class XmlSchemaEnumerationTypeData<T> extends XmlSchemaTypeData<T> implements IXmlSchemaEnumerationTypeData<T> {
private final String _typeName;
// by normalized gosu name ( Any -> ##any )
private final LinkedHashMap<String,IEnumValue> _enumMapByGosuName = new LinkedHashMap<String, IEnumValue>();
// by schema-defined name ( ##any -> ##any )
private final LinkedHashMap<Object,IEnumValue> _enumMapByXmlSchemaName = new LinkedHashMap<Object, IEnumValue>();
private final List<XmlSchemaEnumerationFacet> _enumerations;
private LinkedHashMap<IEnumValue,XmlSimpleValue> _enumerationSimpleValues;
private final LockingLazyVar<XmlSimpleValueFactory> _simpleValueFactory = new LockingLazyVar<XmlSimpleValueFactory>() {
@Override
protected XmlSimpleValueFactory init() {
XmlSchemaType baseType;
if ( _baseTypeName == null ) {
baseType = _baseType;
}
else {
baseType = _parentType.getSchemaIndex().getXmlSchemaTypeByQName( _baseTypeName );
}
return XmlSchemaIndex.getSimpleValueFactoryForSchemaType( baseType );
}
};
private boolean _initialized;
private final T _context;
private final XmlSchemaType _parentType;
private final XmlSchemaType _baseType;
private final QName _baseTypeName;
private static final IJavaType IXMLSCHEMAENUMVALUE_TYPE = (IJavaType) TypeSystem.get( IXmlSchemaEnumValue.class );
public XmlSchemaEnumerationTypeData( String typeName, List<XmlSchemaEnumerationFacet> enumerations, T context, XmlSchemaType baseType, QName baseTypeName, XmlSchemaType parentType ) {
//noinspection unchecked
super( parentType.getSchemaIndex() );
_parentType = parentType;
_typeName = typeName;
_enumerations = enumerations;
_context = context;
_baseType = baseType;
_baseTypeName = baseTypeName;
}
private String makeUniqueCode( String enumeration, Set<String> usedEnumValues ) {
enumeration = XmlSchemaIndex.normalizeName( enumeration, XmlSchemaIndex.NormalizationMode.PRESERVECASE );
enumeration = Character.toUpperCase( enumeration.charAt( 0 ) ) + enumeration.substring( 1 );
String code = enumeration;
int suffix = 2;
while ( ! usedEnumValues.add( code ) ) {
code = enumeration + suffix++;
}
return code;
}
public List<IEnumValue> getEnumValues() {
maybeInit();
return new ArrayList<IEnumValue>( _enumMapByGosuName.values() ); // Have to clone this list - EnumerationPopupListModel tries to sort this
}
public List<String> getEnumConstants() {
List<String> enumConstants = new ArrayList<String>();
for( IEnumValue v : getEnumValues() ) {
enumConstants.add( v.getCode() );
}
return enumConstants;
}
public void maybeInit() {
if ( ! _initialized ) {
TypeSystem.lock();
try {
if ( ! _initialized ) {
LinkedHashMap<IEnumValue, XmlSimpleValue> enumerationSimpleValues = new LinkedHashMap<IEnumValue, XmlSimpleValue>();
IJavaClassInfo clazz = getSchemaIndex().getGeneratedClass(_typeName);
IEnumType javaEnumType = clazz == null ? null : (IEnumType) TypeSystem.get( clazz );
int ordinal = 0;
Set<String> usedEnumValues = new HashSet<String>();
for ( final XmlSchemaEnumerationFacet enumeration : _enumerations ) {
XmlSimpleValueFactory valueFactory = getSimpleValueFactory();
XmlDeserializationContext context = new XmlDeserializationContext( null );
for ( Map.Entry<String, String> entry : enumeration.getNamespaceContext().entrySet() ) {
context.addNamespace( entry.getKey(), entry.getValue() );
}
XmlSimpleValueBase simpleValue = (XmlSimpleValueBase) valueFactory.deserialize( context, enumeration.getValue(), false );
if ( _enumMapByXmlSchemaName.containsKey( simpleValue.getGosuValue() ) ) {
continue;
}
final String code = makeUniqueCode( simpleValue.getStringValue( true ), usedEnumValues );
IEnumValue value;
if ( javaEnumType != null && clazz.getBackingClass() != null ) {
value = javaEnumType.getEnumValue( code );
if ( value instanceof EnumValuePlaceholder ) {
try {
Class enumClass = Class.forName( javaEnumType.getName() );
value = new EnumAdapter( Enum.valueOf( enumClass, code ), javaEnumType );
} catch ( Exception e ) {
value = new XmlSchemaEnumValue( this, simpleValue, code, ordinal++ );
e.printStackTrace();
}
}
}
else {
value = new XmlSchemaEnumValue( this, simpleValue, code, ordinal++ );
}
_enumMapByGosuName.put(code, value);
_enumMapByXmlSchemaName.put( simpleValue.getGosuValue(), value );
enumerationSimpleValues.put( (IEnumValue) value.getValue(), simpleValue );
}
_enumerationSimpleValues = enumerationSimpleValues;
_initialized = true;
}
}
finally {
TypeSystem.unlock();
}
}
}
// public void maybeInit() {
// if (!_initialized) {
// TypeSystem.lock();
// try {
// _enumerationSimpleValues = new LinkedHashMap<IEnumValue, XmlSimpleValue>();
// IJavaClassInfo clazz = getSchemaIndex().getGeneratedClass(_typeName);
// IEnumType javaEnumType = clazz == null ? null : (IEnumType) TypeSystem.get(clazz);
// int ordinal = 0;
// Set<String> usedEnumValues = new HashSet<String>();
// for (final XmlSchemaEnumerationFacet enumeration : _enumerations) {
// XmlSimpleValueFactory valueFactory = getSimpleValueFactory();
// XmlDeserializationContext context = new XmlDeserializationContext(null);
// for (Map.Entry<String, String> entry : enumeration.getNamespaceContext().entrySet()) {
// context.addNamespace(entry.getKey(), entry.getValue());
// }
// XmlSimpleValueBase simpleValue = (XmlSimpleValueBase) valueFactory.deserialize(context, enumeration.getValue(), false);
//
// if (_enumMapByXmlSchemaName.containsKey(simpleValue.getGosuValue())) {
// continue;
// }
//
// final String code = makeUniqueCode(simpleValue.getStringValue(true), usedEnumValues);
//
// IEnumValue value;
// if (javaEnumType != null) {
// value = javaEnumType.getEnumValue(code);
// } else {
// value = new XmlSchemaEnumValue(this, simpleValue, code, ordinal++);
// }
//
// }
// _initialized = true;
// } finally {
// TypeSystem.unlock();
// }
// }
// }
public XmlSimpleValue getEnumSimpleValue( IEnumValue value ) {
maybeInit();
//noinspection RedundantCast
return _enumerationSimpleValues.get( (IEnumValue) value.getValue() );
}
public IEnumValue getEnumValue( String strName ) {
maybeInit();
return _enumMapByGosuName.get( strName );
}
public Map<IEnumValue, XmlSimpleValue> getEnumSimpleValues() {
return _enumerationSimpleValues; // added to assist in debugging PL-17888
}
public IEnumValue deserialize( XmlSimpleValue value ) {
maybeInit();
if (value == null) {
return null;
}
if ( ! value.getGosuValueType().equals( getSimpleValueFactory().getGosuValueType() ) ) {
// coerce
value = getSimpleValueFactory().deserialize( value.getStringValue() );
}
IEnumValue enumValue = _enumMapByXmlSchemaName.get( value.getGosuValue() );
if ( enumValue == null ) {
throw new XmlSimpleValueException( "Enum value not found for type " + this + ": " + value );
}
return enumValue;
}
@Override
public List<Class<?>> getAdditionalInterfaces() {
//noinspection unchecked
return Arrays.asList( IEnumType.class, IXmlSchemaEnumerationTypeData.class );
}
@Override
public boolean prefixSuperProperties() {
return false;
}
public List<IPropertyInfo> getDeclaredProperties() {
List<IEnumValue> enumValues = getEnumValues();
List<IPropertyInfo> props = new ArrayList<IPropertyInfo>( enumValues.size() );
for ( final IEnumValue value : enumValues ) {
props.add( new PropertyInfoBuilder()
.withName( value.getCode() )
.withType( getType() )
.withStatic()
.withAccessor( new IPropertyAccessor() {
@Override
public Object getValue( Object ctx ) {
return value.getValue();
}
@Override
public void setValue( Object ctx, Object value ) {
throw new UnsupportedOperationException();
}
} ).build( this ) );
}
props.add( new PropertyInfoBuilder()
.withName( "SerializedValue" )
.withType( JavaTypes.STRING() )
.withWritable( false )
.withDescription( "Returns the serialized value of this " + getType().getRelativeName() )
.withAccessor( new IPropertyAccessor() {
@Override
public Object getValue( Object ctx ) {
return ( (IEnumValue) ctx ).getValue().toString();
}
@Override
public void setValue( Object ctx, Object value ) {
throw new UnsupportedOperationException();
}
} )
.build( this )
);
props.add( new PropertyInfoBuilder()
.withName( "GosuValue" )
.withType( getSimpleValueFactory().getGosuValueType() )
.withWritable( false )
.withDescription( "Returns the value of this " + getType().getRelativeName() )
.withAccessor( new IPropertyAccessor() {
@Override
public Object getValue( Object ctx ) {
//noinspection RedundantCast
return _enumerationSimpleValues.get( (IEnumValue) ctx ).getGosuValue();
}
@Override
public void setValue( Object ctx, Object value ) {
throw new UnsupportedOperationException();
}
} )
.build( this )
);
return props;
}
public List<IMethodInfo> getDeclaredMethods() {
List<IMethodInfo> methods = new ArrayList<IMethodInfo>();
methods.add( new MethodInfoBuilder()
.withStatic()
.withName( "forGosuValue" )
.withParameters( new ParameterInfoBuilder().withName( "gosuValue" ).withDescription( "The value of the enumeration" ).withType( getSimpleValueFactory().getGosuValueType() ) )
.withReturnType( getType() )
.withCallHandler( new IMethodCallHandler() {
@Override
public Object handleCall( Object ctx, Object... args ) {
IEnumValue value = _enumMapByXmlSchemaName.get( args[0] );
return value == null ? null : value.getValue();
}
} )
.build( this ) );
methods.add( new MethodInfoBuilder()
.withStatic()
.withName( "valueOf" )
.withParameters( new ParameterInfoBuilder().withName( "name" ).withDescription( "The name of the enum constant to look up" ).withType( JavaTypes.STRING() ) )
.withReturnType( getType() )
.withCallHandler( new IMethodCallHandler() {
@Override
public Object handleCall( Object ctx, Object... args ) {
String name = (String) args[0];
return _enumMapByGosuName.get( name );
}
} )
.build( this )
);
methods.add( new MethodInfoBuilder()
.withStatic()
.withName( "values" )
.withReturnType( getType().getArrayType() )
.withCallHandler( new IMethodCallHandler() {
@Override
public Object handleCall( Object ctx, Object... args ) {
Collection<IEnumValue> enumValues = _enumMapByXmlSchemaName.values();
@SuppressWarnings( { "SuspiciousToArrayCall" } )
IGosuObject[] values = enumValues.toArray( new IGosuObject[enumValues.size()] );
Object result = getType().makeArrayInstance(values.length);
for (int i = 0; i < values.length; i++) {
IGosuObject value = values[i];
getType().setArrayComponent(result, i, value);
}
return result;
}
} )
.build( this )
);
methods.add( new MethodInfoBuilder()
.withName( "name" )
.withReturnType( JavaTypes.STRING() )
.withCallHandler( new IMethodCallHandler() {
@Override
public Object handleCall( Object ctx, Object... args ) {
IEnumValue enumValue = (IEnumValue) ctx;
IXmlSchemaEnumValue value = (IXmlSchemaEnumValue) enumValue.getValue();
return value.getCode();
}
} )
.build( this )
);
methods.add( new MethodInfoBuilder()
.withName( "ordinal" )
.withReturnType( JavaTypes.pINT() )
.withCallHandler( new IMethodCallHandler() {
@Override
public Object handleCall( Object ctx, Object... args ) {
IEnumValue enumValue = (IEnumValue) ctx;
IXmlSchemaEnumValue value = (IXmlSchemaEnumValue) enumValue.getValue();
return value.getOrdinal();
}
} )
.build( this )
);
return methods;
}
public List<IConstructorInfo> getDeclaredConstructors() {
return Collections.emptyList();
}
@Override
public boolean isFinal() {
return true;
}
@Override
public boolean isEnum() {
return true;
}
@Override
public IType getSuperType() {
return JavaTypes.OBJECT();
}
@Override
public String getName() {
return _typeName;
}
@Override
public XmlSchemaObject getSchemaObject() {
return _parentType;
}
@Override
public boolean isAnonymous() {
return false;
}
@Override
public T getContext() {
return _context;
}
@Override
public List<? extends IType> getInterfaces() {
return Collections.singletonList( IXMLSCHEMAENUMVALUE_TYPE );
}
@Override
public long getFingerprint() {
return getSchemaIndex().getFingerprint();
}
@Override
public Class getBackingClass() {
IJavaClassInfo clazz = getSchemaIndex().getGeneratedClass(_typeName);
if (clazz != null && !(clazz instanceof IAsmJavaClassInfo) && clazz.getBackingClass() != null) {
return clazz.getBackingClass();
} else {
return XmlSchemaEnumValue.class;
}
}
@Override
public IJavaClassInfo getBackingClassInfo() {
IJavaClassInfo clazz = getSchemaIndex().getGeneratedClass(_typeName);
if (clazz != null) {
return clazz;
} else {
return (JavaTypes.getSystemType(XmlSchemaEnumValue.class)).getBackingClassInfo();
}
}
public XmlSimpleValueFactory getSimpleValueFactory() {
return _simpleValueFactory.get();
}
private class EnumAdapter implements IEnumValue, IGosuObject
{
private final Enum _enum;
private final IEnumType javaEnumType;
public EnumAdapter(Enum e, IEnumType javaEnumType)
{
_enum = e;
this.javaEnumType = javaEnumType;
}
public String getCode()
{
return _enum.name();
}
public Object getValue()
{
return _enum;
}
public int getOrdinal()
{
return _enum.ordinal();
}
public String getDisplayName()
{
return getCode();
}
public IType getIntrinsicType()
{
return TypeSystem.getOrCreateTypeReference( javaEnumType );
}
}
}