/*
* Copyright 2013 Guidewire Software, Inc.
*/
package gw.internal.xml;
import gw.lang.reflect.IType;
import gw.lang.reflect.TypeSystem;
import gw.lang.reflect.java.IJavaType;
import gw.lang.reflect.java.JavaTypes;
import gw.lang.reflect.gs.IGosuObject;
import gw.lang.reflect.java.JavaTypes;
import gw.util.Pair;
import gw.xml.IXmlMixedContent;
import gw.xml.XmlElement;
import gw.xml.XmlTypeInstance;
import javax.xml.namespace.QName;
import java.util.*;
public class XmlMixedContentList extends ArrayList<IXmlMixedContent> {
private LinkedHashMap<QName,List<Pair<XmlElement,Integer>>> _elementsByQName;
private int _checksum;
private XmlTypeInstance _typeInstance;
public XmlMixedContentList( XmlTypeInstance typeInstance ) {
super( 0 );
_typeInstance = typeInstance;
}
public List<XmlElement> getAllElements() {
return new ElementList( null );
}
public List<XmlElement> getElementsByQName( QName qname ) {
maybeIndexByQName();
return new ElementList( qname );
}
public List<XmlElement> getElementsBySubstitutionGroup( IType type ) {
return new SubstitutionGroupList( type );
}
public List<XmlElement> removeElementsBySubstitutionGroup( IType type ) {
List<XmlElement> children = new ArrayList<XmlElement>();
Iterator<IXmlMixedContent> it = XmlMixedContentList.this.iterator();
while ( it.hasNext() ) {
IXmlMixedContent content = it.next();
if ( content instanceof XmlElement ) {
XmlElement element = (XmlElement) content;
if ( type.isAssignableFrom( element.getIntrinsicType() ) ) {
children.add( element );
it.remove();
}
}
}
return children;
}
private void maybeIndexByQName() {
if ( _elementsByQName == null ) {
_elementsByQName = new LinkedHashMap<QName, List<Pair<XmlElement, Integer>>>( 1 );
for ( int i = 0; i < size(); i++ ) {
IXmlMixedContent child = get(i);
if ( child instanceof XmlElement ) {
XmlElement element = (XmlElement) child;
addIndexedElementByQName( element, element.getQName(), i );
addIndexedElementByQName( element, null, i ); // all elements are added to the "null" qname list
}
}
}
}
private void addIndexedElementByQName( XmlElement element, QName qname, int idx ) {
if ( element == null ) {
throw new IllegalArgumentException( "Null element cannot be added to content list" );
}
maybeIndexByQName();
List<Pair<XmlElement, Integer>> elementList = _elementsByQName.get( qname );
if ( elementList == null ) {
elementList = new ArrayList<Pair<XmlElement, Integer>>( 1 );
_elementsByQName.put( qname, elementList );
}
elementList.add( new Pair<XmlElement, Integer>( element, idx ) );
}
@Override
public IXmlMixedContent set( int index, IXmlMixedContent content ) {
if ( content == null ) {
throw new IllegalArgumentException( "Null content cannot be added to content list" );
}
XmlTypeInstanceInternals.instance().clearSimpleValue( _typeInstance );
clearCaches();
return super.set( index, content );
}
private void clearCaches() {
_elementsByQName = null;
_checksum++;
}
@Override
public boolean add( IXmlMixedContent content ) {
if ( content == null ) {
throw new IllegalArgumentException( "Null content cannot be added to content list" );
}
XmlTypeInstanceInternals.instance().clearSimpleValue( _typeInstance );
clearCaches();
return super.add( content );
}
@Override
public void add( int index, IXmlMixedContent content ) {
if ( content == null ) {
throw new IllegalArgumentException( "Null content cannot be added to content list" );
}
XmlTypeInstanceInternals.instance().clearSimpleValue( _typeInstance );
clearCaches();
super.add( index, content );
}
@Override
public IXmlMixedContent remove( int index ) {
clearCaches();
return super.remove(index);
}
@Override
public boolean remove( Object o ) {
clearCaches();
return super.remove(o);
}
@Override
public void clear() {
clearCaches();
super.clear();
}
@Override
public boolean addAll( Collection<? extends IXmlMixedContent> c ) {
for ( IXmlMixedContent content : c ) {
if ( content == null ) {
throw new IllegalArgumentException( "Null element cannot be added to content list" );
}
}
XmlTypeInstanceInternals.instance().clearSimpleValue( _typeInstance );
clearCaches();
return super.addAll(c);
}
@Override
public boolean addAll( int index, Collection<? extends IXmlMixedContent> c ) {
for ( IXmlMixedContent content : c ) {
if ( content == null ) {
throw new IllegalArgumentException( "Null content cannot be added to content list" );
}
}
XmlTypeInstanceInternals.instance().clearSimpleValue( _typeInstance );
clearCaches();
return super.addAll(index, c);
}
@Override
protected void removeRange(int fromIndex, int toIndex) {
clearCaches();
super.removeRange( fromIndex, toIndex );
}
@Override
public boolean removeAll(Collection<?> c) {
clearCaches();
return super.removeAll(c);
}
@Override
public boolean retainAll(Collection<?> c) {
clearCaches();
return super.retainAll( c );
}
private class ElementList extends AbstractList<XmlElement> implements IGosuObject {
private final QName _qname;
public ElementList( QName qname ) {
_qname = qname;
}
@Override
public XmlElement get( int index ) {
if ( XmlMixedContentList.this.isEmpty() ) {
throw new IndexOutOfBoundsException();
}
maybeIndexByQName();
List<Pair<XmlElement, Integer>> elementList = _elementsByQName.get( _qname );
if ( elementList == null ) {
throw new IndexOutOfBoundsException();
}
else {
return elementList.get( index ).getFirst();
}
}
@Override
public int size() {
if ( XmlMixedContentList.this.isEmpty() ) {
return 0;
}
maybeIndexByQName();
List<Pair<XmlElement, Integer>> elementList = _elementsByQName.get( _qname );
return elementList == null ? 0 : elementList.size();
}
@Override
public boolean add( XmlElement xmlElement ) {
return XmlMixedContentList.this.add( xmlElement ); // clears cache automatically
}
@Override
public XmlElement remove( int idx ) {
if ( XmlMixedContentList.this.isEmpty() ) {
throw new IndexOutOfBoundsException();
}
maybeIndexByQName();
List<Pair<XmlElement, Integer>> elementList = _elementsByQName.get( _qname );
if ( elementList == null ) {
return null;
}
Pair<XmlElement, Integer> pair = elementList.get( idx );
XmlElement removed = (XmlElement) XmlMixedContentList.this.remove( (int) pair.getSecond() );
if ( removed != pair.getFirst() ) {
throw new IllegalStateException( "Expected elements to be identical in qname-based list and master list" );
}
return removed;
}
@Override
public XmlElement set( int index, XmlElement element ) {
if ( XmlMixedContentList.this.isEmpty() ) {
throw new IndexOutOfBoundsException();
}
maybeIndexByQName();
List<Pair<XmlElement, Integer>> elementList = _elementsByQName.get( _qname );
if ( elementList == null ) {
return null;
}
Pair<XmlElement, Integer> pair = elementList.get( index );
XmlElement removed = (XmlElement) XmlMixedContentList.this.set( pair.getSecond(), element );
if ( removed != pair.getFirst() ) {
throw new IllegalStateException( "Expected elements to be identical in qname-based list and master list" );
}
return removed;
}
@Override
public void add( int index, XmlElement element ) {
maybeIndexByQName();
List<Pair<XmlElement, Integer>> elementList = _elementsByQName.get( _qname );
int realIndex;
if ( elementList == null ) {
realIndex = 0;
}
else {
Pair<XmlElement, Integer> pair = elementList.get( index );
realIndex = pair.getSecond();
}
XmlMixedContentList.this.add( realIndex, element );
}
@Override
public IType getIntrinsicType() {
return JavaTypes.LIST().getParameterizedType( TypeSystem.get( XmlElement.class ) );
}
}
private class SubstitutionGroupList extends AbstractList<XmlElement> implements IGosuObject {
private final IType _type;
private List<XmlElement> _children;
private List<Integer> _originalIndexes;
private int _checksum;
public SubstitutionGroupList( IType type ) {
_type = type;
_checksum = XmlMixedContentList.this._checksum - 1; // force indexing
}
@Override
public XmlElement get( int index ) {
maybeIndex();
return _children.get( index );
}
@Override
public int size() {
maybeIndex();
return _children.size();
}
@Override
public void add( int idx, XmlElement xmlElement ) {
XmlMixedContentList.this.add( idx, xmlElement ); // clears cache automatically
}
@Override
public boolean add( XmlElement xmlElement ) {
return XmlMixedContentList.this.add( xmlElement ); // clears cache automatically
}
@Override
public IType getIntrinsicType() {
return JavaTypes.LIST().getParameterizedType( _type );
}
private void maybeIndex() {
if ( _checksum != XmlMixedContentList.this._checksum ) {
_children = new ArrayList<XmlElement>();
_originalIndexes = new ArrayList<Integer>();
int originalIndex = 0;
for ( IXmlMixedContent content : XmlMixedContentList.this ) {
if ( ! ( content instanceof XmlElement ) ) {
continue;
}
XmlElement element = (XmlElement) content;
if ( _type.isAssignableFrom( element.getIntrinsicType() ) ) {
_children.add( element );
_originalIndexes.add( originalIndex );
}
originalIndex++;
}
_checksum = XmlMixedContentList.this._checksum;
}
}
@Override
public XmlElement remove( int index ) {
maybeIndex();
int originalIndex = _originalIndexes.get( index );
return (XmlElement) XmlMixedContentList.this.remove( originalIndex ); // clears cache automatically
}
}
}