All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.glassfish.jaxb.runtime.v2.runtime.reflect.Lister Maven / Gradle / Ivy

There is a newer version: 4.0.5
Show newest version
/*
 * Copyright (c) 1997, 2020 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 {@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 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; 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 org.glassfish.jaxb.runtime.v2.runtime.reflect.ListIterator iterator(T collection, XMLSerializer context) { final Iterator itr = collection.iterator(); return new org.glassfish.jaxb.runtime.v2.runtime.reflect.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 org.glassfish.jaxb.runtime.v2.runtime.reflect.ListIterator iterator(PropT prop, XMLSerializer context) { final org.glassfish.jaxb.runtime.v2.runtime.reflect.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 org.glassfish.jaxb.runtime.v2.runtime.reflect.ListIterator { private final org.glassfish.jaxb.runtime.v2.runtime.reflect.ListIterator i; private final XMLSerializer context; private Object last; private IDREFSIterator(org.glassfish.jaxb.runtime.v2.runtime.reflect.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 org.glassfish.jaxb.runtime.v2.runtime.reflect.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 org.glassfish.jaxb.runtime.v2.runtime.reflect.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 }; }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy