/* * Copyright 2013 Guidewire Software, Inc. */ package gw.internal.xml.xsd.typeprovider.simplevaluefactory; import gw.internal.xml.XmlDeserializationContext; import gw.internal.xml.XmlSerializationContext; import gw.internal.xml.XmlSimpleValueBase; import gw.internal.xml.XmlSimpleValueInternals; import gw.internal.xml.XmlSimpleValueValidationContext; import gw.internal.xml.xsd.typeprovider.validator.XmlSimpleValueValidator; import gw.lang.reflect.IType; import gw.lang.reflect.gs.IGosuObject; import gw.lang.reflect.java.JavaTypes; import gw.util.concurrent.LockingLazyVar; import gw.xml.XmlSimpleValue; import gw.xml.XmlSimpleValueException; import java.util.AbstractList; import java.util.ArrayList; import java.util.Collections; import java.util.List; import javax.xml.namespace.QName; public class XmlSchemaListSimpleValueFactory extends XmlSimpleValueFactory { private final XmlSimpleValueFactory _itemFactory; private final XmlSimpleValueValidator _itemValidator; private final LockingLazyVar<IType> _gosuValueType = new LockingLazyVar<IType>() { @Override protected IType init() { return JavaTypes.LIST().getParameterizedType( _itemFactory.getGosuValueType() ); } }; public XmlSchemaListSimpleValueFactory( XmlSimpleValueFactory itemFactory, XmlSimpleValueValidator itemValidator ) { _itemFactory = itemFactory; _itemValidator = itemValidator; } @Override public IType getGosuValueType() { return _gosuValueType.get(); } @Override protected XmlSimpleValue _gosuValueToStorageValue( Object gosuValue ) { //noinspection unchecked return new Value( new ItemList( (List<Object>) gosuValue ), false ); } @Override protected XmlSimpleValue _deserialize( XmlDeserializationContext context, String stringValue, boolean isDefault ) { final ItemList itemList = new ItemList( Collections.emptyList() ); final Value value = new Value( itemList, isDefault ); final String[] strings = stringValue.split( " " ); for ( String string : strings ) { if (string.length() != 0) { itemList._items.add( (XmlSimpleValueBase) _itemFactory.deserialize( context, string, isDefault ) ); } } return value; } private class Value extends XmlSimpleValueBase { private final ItemList _value; private final boolean _isDefault; public Value( ItemList value, boolean isDefault ) { _value = value; _isDefault = isDefault; } @Override public IType getGosuValueType() { return XmlSchemaListSimpleValueFactory.this.getGosuValueType(); } @Override public Object _getGosuValue() { // If this is returned as a default value, the backing list doesn't actually exist anywhere on the parent // element... Therefore, values set into it would never affect the parent element, so it might as well // be unmodifiable. See Jira PL-9876 return _isDefault ? Collections.unmodifiableList( _value ) : _value; } @Override public String _getStringValue( boolean isEnumCode ) { return _value.getStringValue( isEnumCode ); } @Override public String _serialize( XmlSerializationContext context ) { return _value.serialize( context ); } @Override public List<QName> _getQNames() { return _value.getQNames(); } } private class ItemList extends AbstractList<Object> implements IGosuObject { private List<XmlSimpleValueBase> _items = new ArrayList<XmlSimpleValueBase>( 0 ); public ItemList( List<Object> items ) { addAll( items ); } public String getStringValue( boolean isEnumCode ) { StringBuilder sb = new StringBuilder(); boolean first = true; for ( XmlSimpleValueBase item : _items ) { if ( ! first ) { sb.append( ' ' ); } first = false; sb.append( item.getStringValue( isEnumCode ) ); } return sb.toString(); } public String serialize( XmlSerializationContext context ) { StringBuilder sb = new StringBuilder(); boolean first = true; for ( XmlSimpleValueBase item : _items ) { if ( ! first ) { sb.append( ' ' ); } first = false; final String str = XmlSimpleValueInternals.instance().serialize( item, context ); if ( str.length() == 0 ) { throw new XmlSimpleValueException( "xsd:list member length is zero" ); } checkForSpaceInItem( str ); sb.append( str ); } return sb.toString(); } private void checkForSpaceInItem( String str ) { if ( str.contains( " " ) ) { throw new XmlSimpleValueException( "xsd:list member '" + str + "' contains space" ); } } @Override public Object get( int index ) { return _items.get( index ).getGosuValue(); } @Override public int size() { return _items.size(); } @Override public IType getIntrinsicType() { return getGosuValueType(); } @Override public void add( int index, Object element ) { _items.add( index, (XmlSimpleValueBase) convertItemGosuValueToStorageValue( element ) ); } @Override public Object set( int index, Object element ) { final XmlSimpleValue storageValue = convertItemGosuValueToStorageValue( element ); XmlSimpleValueInternals.instance().validate( storageValue, _itemValidator, new XmlSimpleValueValidationContext() ); final XmlSimpleValue simpleValue = _items.set( index, (XmlSimpleValueBase) storageValue ); return simpleValue == null ? null : simpleValue.getGosuValue(); } @Override public Object remove( int index ) { final XmlSimpleValue simpleValue = _items.remove( index ); return simpleValue == null ? null : simpleValue.getGosuValue(); } @Override public boolean add( Object o ) { return _items.add( (XmlSimpleValueBase) convertItemGosuValueToStorageValue( o ) ); } @Override public void clear() { _items.clear(); } public List<QName> getQNames() { List<QName> ret = new ArrayList<QName>( 0 ); for ( XmlSimpleValue item : _items ) { ret.addAll( item.getQNames() ); } return ret; } private XmlSimpleValue convertItemGosuValueToStorageValue( Object gosuValue ) { XmlSimpleValue value = _itemFactory.gosuValueToStorageValue( gosuValue ); String stringValue = value.getStringValue(); checkForSpaceInItem( stringValue ); return value; } } }