org.glassfish.jaxb.runtime.v2.runtime.reflect.Lister Maven / Gradle / Ivy
/*
* Copyright (c) 1997, 2022 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 org.glassfish.jaxb.runtime.v2.runtime.reflect;
import com.sun.istack.SAXException2;
import org.glassfish.jaxb.runtime.api.AccessorException;
import org.glassfish.jaxb.core.v2.ClassFactory;
import org.glassfish.jaxb.core.v2.TODO;
import org.glassfish.jaxb.core.v2.model.core.Adapter;
import org.glassfish.jaxb.core.v2.model.core.ID;
import org.glassfish.jaxb.runtime.v2.runtime.XMLSerializer;
import org.glassfish.jaxb.core.v2.runtime.unmarshaller.LocatorEx;
import org.glassfish.jaxb.runtime.v2.runtime.unmarshaller.Patcher;
import org.glassfish.jaxb.runtime.v2.runtime.unmarshaller.UnmarshallingContext;
import jakarta.xml.bind.JAXBException;
import org.xml.sax.SAXException;
import java.lang.ref.WeakReference;
import java.lang.reflect.Array;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.*;
import java.util.concurrent.Callable;
/**
* 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 org.glassfish.jaxb.runtime.v2.runtime.reflect.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 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;
}
@Override
public org.glassfish.jaxb.runtime.v2.runtime.reflect.ListIterator iterator(final ItemT[] objects, XMLSerializer context) {
return new org.glassfish.jaxb.runtime.v2.runtime.reflect.ListIterator<>() {
int idx = 0;
@Override
public boolean hasNext() {
return idx < objects.length;
}
@Override
public ItemT next() {
return objects[idx++];
}
};
}
@Override
public Pack startPacking(BeanT current, Accessor acc) {
return new Pack<>(itemType);
}
@Override
public void addToPack(Pack objects, ItemT o) {
objects.add(o);
}
@Override
public void endPacking( Pack pack, BeanT bean, Accessor acc ) throws AccessorException {
acc.set(bean,pack.build());
}
@Override
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 static final long serialVersionUID = 8543908122652908717L;
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;
}
@Override
public org.glassfish.jaxb.runtime.v2.runtime.reflect.ListIterator