com.sun.xml.bind.v2.runtime.reflect.Lister Maven / Gradle / Ivy
Show all versions of ehcache Show documentation
/*
* Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Distribution License v. 1.0, which is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
package com.sun.xml.bind.v2.runtime.reflect;
import java.lang.ref.WeakReference;
import java.lang.reflect.Array;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.WeakHashMap;
import java.util.LinkedList;
import java.util.HashSet;
import java.util.TreeSet;
import java.util.Stack;
import java.util.concurrent.Callable;
import javax.xml.bind.JAXBException;
import com.sun.istack.SAXException2;
import com.sun.xml.bind.api.AccessorException;
import com.sun.xml.bind.v2.ClassFactory;
import com.sun.xml.bind.v2.TODO;
import com.sun.xml.bind.v2.model.core.Adapter;
import com.sun.xml.bind.v2.model.core.ID;
import com.sun.xml.bind.v2.runtime.XMLSerializer;
import com.sun.xml.bind.v2.runtime.unmarshaller.Patcher;
import com.sun.xml.bind.v2.runtime.unmarshaller.UnmarshallingContext;
import com.sun.xml.bind.v2.runtime.unmarshaller.LocatorEx;
import org.xml.sax.SAXException;
/**
* Used to list individual values of a multi-value property, and
* to pack individual values into a multi-value property.
*
* @author Kohsuke Kawaguchi ([email protected])
*/
public abstract class Lister {
protected Lister() {}
/**
* Iterates values of a multi-value property.
*
* @param context
* This parameter is used to support ID/IDREF handling.
*/
public abstract ListIterator iterator(PropT multiValueProp, XMLSerializer context);
/**
* Setting values to a multi-value property starts by creating
* a transient object called "pack" from the current field.
*/
public abstract PackT startPacking(BeanT bean, Accessor acc) throws AccessorException;
/**
* Once the {@link #startPacking} is called, you can
* add values to the pack by using this method.
*/
public abstract void addToPack( PackT pack, ItemT newValue ) throws AccessorException;
/**
* Finally, call this method to
* wraps up the {@code pack}. This method may update the field of
* the given bean.
*/
public abstract void endPacking( PackT pack, BeanT bean, Accessor acc ) throws AccessorException;
/**
* Clears the values of the property.
*/
public abstract void reset(BeanT o,Accessor acc) throws AccessorException;
/**
* Gets a reference to the appropriate {@link Lister} object
* if the field is a multi-value field. Otherwise null.
*
* @param fieldType
* the type of the field that stores the collection
* @param idness
* ID-ness of the property.
* @param adapter
* adapter to be used for individual items. can be null.
*/
public static
Lister create(Type fieldType,ID idness, Adapter adapter) {
Class rawType = (Class) Utils.REFLECTION_NAVIGATOR.erasure(fieldType);
Class itemType;
Lister l;
if( rawType.isArray() ) {
itemType = rawType.getComponentType();
l = getArrayLister(itemType);
} else
if( Collection.class.isAssignableFrom(rawType) ) {
Type bt = Utils.REFLECTION_NAVIGATOR.getBaseClass(fieldType,Collection.class);
if(bt instanceof ParameterizedType)
itemType = (Class) Utils.REFLECTION_NAVIGATOR.erasure(((ParameterizedType)bt).getActualTypeArguments()[0]);
else
itemType = Object.class;
l = new CollectionLister(getImplClass(rawType));
} else
return null;
if(idness==ID.IDREF)
l = new IDREFS(l,itemType);
if(adapter!=null)
l = new AdaptedLister(l,adapter.adapterType);
return l;
}
private static Class getImplClass(Class fieldType) {
return ClassFactory.inferImplClass(fieldType,COLLECTION_IMPL_CLASSES);
}
/**
* Cache instances of {@link ArrayLister}s.
*/
private static final Map> arrayListerCache =
Collections.synchronizedMap(new WeakHashMap>());
/**
* Creates a lister for array type.
*/
private static Lister getArrayLister( Class componentType ) {
Lister l=null;
if(componentType.isPrimitive())
l = primitiveArrayListers.get(componentType);
else {
WeakReference wr = arrayListerCache.get(componentType);
if(wr!=null)
l = wr.get();
if(l==null) {
l = new ArrayLister(componentType);
arrayListerCache.put(componentType,new WeakReference(l));
}
}
assert l!=null;
return l;
}
/**
* {@link Lister} for an array.
*
*
* Array packing is slower, but we expect this to be used less frequently than
* the {@link CollectionLister}.
*/
private static final class ArrayLister extends Lister> {
private final Class itemType;
public ArrayLister(Class itemType) {
this.itemType = itemType;
}
public ListIterator iterator(final ItemT[] objects, XMLSerializer context) {
return new ListIterator() {
int idx=0;
public boolean hasNext() {
return idx acc) {
return new Pack(itemType);
}
public void addToPack(Pack objects, ItemT o) {
objects.add(o);
}
public void endPacking( Pack pack, BeanT bean, Accessor acc ) throws AccessorException {
acc.set(bean,pack.build());
}
public void reset(BeanT o,Accessor acc) throws AccessorException {
acc.set(o,(ItemT[])Array.newInstance(itemType,0));
}
}
public static final class Pack extends ArrayList {
private final Class itemType;
public Pack(Class itemType) {
this.itemType = itemType;
}
public ItemT[] build() {
return super.toArray( (ItemT[])Array.newInstance(itemType,size()) );
}
}
/**
* Listers for the primitive type arrays, keyed by their primitive Class object.
*/
/*package*/ static final Map primitiveArrayListers = new HashMap();
static {
// register primitive array listers
PrimitiveArrayListerBoolean.register();
PrimitiveArrayListerByte.register();
PrimitiveArrayListerCharacter.register();
PrimitiveArrayListerDouble.register();
PrimitiveArrayListerFloat.register();
PrimitiveArrayListerInteger.register();
PrimitiveArrayListerLong.register();
PrimitiveArrayListerShort.register();
}
/**
* {@link Lister} for a collection
*/
public static final class CollectionLister extends Lister {
/**
* Sometimes we need to create a new instance of a collection.
* This is such an implementation class.
*/
private final Class implClass;
public CollectionLister(Class implClass) {
this.implClass = implClass;
}
public ListIterator iterator(T collection, XMLSerializer context) {
final Iterator itr = collection.iterator();
return new ListIterator() {
public boolean hasNext() {
return itr.hasNext();
}
public Object next() {
return itr.next();
}
};
}
public T startPacking(BeanT bean, Accessor acc) throws AccessorException {
T collection = acc.get(bean);
if(collection==null) {
collection = ClassFactory.create(implClass);
if(!acc.isAdapted())
acc.set(bean,collection);
}
collection.clear();
return collection;
}
public void addToPack(T collection, Object o) {
collection.add(o);
}
public void endPacking( T collection, BeanT bean, Accessor acc ) throws AccessorException {
// this needs to be done in the endPacking, because
// sometimes the accessor uses an adapter, and the adapter needs to see
// the whole thing.
// but always doing so causes a problem when this collection property
// is getter-only
// invoke set when possible (see Issue 488)
try {
if (acc.isAdapted()) {
acc.set(bean,collection);
}
} catch (AccessorException ae) {
if(acc.isAdapted()) throw ae;
}
}
public void reset(BeanT bean, Accessor acc) throws AccessorException {
T collection = acc.get(bean);
if(collection == null) {
return;
}
collection.clear();
}
}
/**
* {@link Lister} for IDREFS.
*/
private static final class IDREFS extends Lister.Pack> {
private final Lister core;
/**
* Expected type to which IDREF resolves to.
*/
private final Class itemType;
public IDREFS(Lister core, Class itemType) {
this.core = core;
this.itemType = itemType;
}
public ListIterator iterator(PropT prop, XMLSerializer context) {
final ListIterator i = core.iterator(prop,context);
return new IDREFSIterator(i, context);
}
public Pack startPacking(BeanT bean, Accessor acc) {
return new Pack(bean,acc);
}
public void addToPack(Pack pack, String item) {
pack.add(item);
}
public void endPacking(Pack pack, BeanT bean, Accessor acc) {
}
public void reset(BeanT bean, Accessor acc) throws AccessorException {
core.reset(bean,acc);
}
/**
* PackT for this lister.
*/
private class Pack implements Patcher {
private final BeanT bean;
private final List idrefs = new ArrayList();
private final UnmarshallingContext context;
private final Accessor acc;
private final LocatorEx location;
public Pack(BeanT bean, Accessor acc) {
this.bean = bean;
this.acc = acc;
this.context = UnmarshallingContext.getInstance();
this.location = new LocatorEx.Snapshot(context.getLocator());
context.addPatcher(this);
}
public void add(String item) {
idrefs.add(item);
}
/**
* Resolves IDREFS and fill in the actual array.
*/
public void run() throws SAXException {
try {
Object pack = core.startPacking(bean,acc);
for( String id : idrefs ) {
Callable callable = context.getObjectFromId(id,itemType);
Object t;
try {
t = (callable!=null) ? callable.call() : null;
} catch (SAXException e) {
throw e;
} catch (Exception e) {
throw new SAXException2(e);
}
if(t==null) {
context.errorUnresolvedIDREF(bean,id,location);
} else {
TODO.prototype(); // TODO: check if the type of t is proper.
core.addToPack(pack,t);
}
}
core.endPacking(pack,bean,acc);
} catch (AccessorException e) {
context.handleError(e);
}
}
}
}
/**
* {@link Iterator} for IDREFS lister.
*
*
* Only in ArrayElementProperty we need to get the actual
* referenced object. This is a kind of ugly way to make that work.
*/
public static final class IDREFSIterator implements ListIterator {
private final ListIterator i;
private final XMLSerializer context;
private Object last;
private IDREFSIterator(ListIterator i, XMLSerializer context) {
this.i = i;
this.context = context;
}
public boolean hasNext() {
return i.hasNext();
}
/**
* Returns the last referenced object (not just its ID)
*/
public Object last() {
return last;
}
public String next() throws SAXException, JAXBException {
last = i.next();
String id = context.grammar.getBeanInfo(last,true).getId(last,context);
if(id==null) {
context.errorMissingId(last);
}
return id;
}
}
/**
* Gets the special {@link Lister} used to recover from an error.
*/
@SuppressWarnings("unchecked")
public static Lister getErrorInstance() {
return ERROR;
}
public static final Lister ERROR = new Lister() {
public ListIterator iterator(Object o, XMLSerializer context) {
return EMPTY_ITERATOR;
}
public Object startPacking(Object o, Accessor accessor) {
return null;
}
public void addToPack(Object o, Object o1) {
}
public void endPacking(Object o, Object o1, Accessor accessor) {
}
public void reset(Object o, Accessor accessor) {
}
};
private static final ListIterator EMPTY_ITERATOR = new ListIterator() {
public boolean hasNext() {
return false;
}
public Object next() {
throw new IllegalStateException();
}
};
private static final Class[] COLLECTION_IMPL_CLASSES = new Class[] {
ArrayList.class,
LinkedList.class,
HashSet.class,
TreeSet.class,
Stack.class
};
}