blob: 45ecc91971e16909c8b688c0d8b6f506135707e9 [file] [log] [blame]
/*
* 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 Public License v. 2.0, which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* This Source Code may also be made available under the following Secondary
* Licenses when the conditions for such availability set forth in the
* Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
* version 2 with the GNU Classpath Exception, which is available at
* https://www.gnu.org/software/classpath/license.html.
*
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
*/
package org.jvnet.tiger_types;
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.HashSet;
import java.util.List;
import java.util.EnumSet;
/**
* Abstracts away the process of creating a collection (array, {@link List}, etc)
* of items.
*
* @author Kohsuke Kawaguchi
*/
public abstract class Lister<T> {
/**
* Type of the individual item
*/
public final Class itemType;
public final Type itemGenericType;
protected final Collection r;
protected Lister(Class itemType, Type itemGenericType) {
this(itemType,itemGenericType,new ArrayList());
}
protected Lister(Class itemType, Type itemGenericType, Collection r) {
this.itemType = itemType;
this.itemGenericType = itemGenericType;
this.r = r;
}
public void add(Object o) {
r.add(o);
}
public abstract T toCollection();
/**
* Creates a {@link Lister} instance that produces the given type.
*/
public static Lister create(Type t) {
return create(Types.erasure(t),t);
}
/**
* Creates a {@link Lister} instance that produces the given type.
*
* @param c
* The erasure version of 't'. This is taken
* as a parameter as a performance optimizaiton.
*
* @return
* null if the given type doesn't look like a collection.
* @throws IllegalArgumentException
* if the given type does look like a collection yet this implementation
* is not capable of how to handle it.
*/
public static <T> Lister<T> create(Class<T> c, Type t) {
if(c.isArray()) {
// array
Class<?> ct = c.getComponentType();
return new Lister(ct,ct) {
public Object toCollection() {
return r.toArray((Object[])Array.newInstance(itemType,r.size()));
}
};
}
if(Collection.class.isAssignableFrom(c)) {
final Type col = Types.getBaseClass(t, Collection.class);
final Type itemType;
if (col instanceof ParameterizedType)
itemType = Types.getTypeArgument(col, 0);
else
itemType = Object.class;
Collection items=null;
try {
items = (Collection)c.newInstance();
} catch (InstantiationException e) {
// this is not instanciable. Try known instanciable versions.
for (Class ct : CONCRETE_TYPES) {
if(c.isAssignableFrom(ct)) {
try {
items = (Collection)ct.newInstance();
break;
} catch (InstantiationException x) {
throw toError(x);
} catch (IllegalAccessException x) {
throw toError(x);
}
}
}
// EnumSet
if(items==null && c==EnumSet.class) {
items = EnumSet.noneOf(Types.erasure(itemType).asSubclass(Enum.class));
}
if(items==null)
throw new IllegalArgumentException("Don't know how to instanciate "+c);
} catch (IllegalAccessException e) {
throw toError(e);
}
return new Lister(Types.erasure(itemType),itemType,items) {
public Object toCollection() {
return r;
}
};
}
return null;
}
private static IllegalAccessError toError(IllegalAccessException e) {
IllegalAccessError x = new IllegalAccessError();
x.initCause(e);
return x;
}
private static InstantiationError toError(InstantiationException e) {
InstantiationError x = new InstantiationError();
x.initCause(e);
return x;
}
private static final Class[] CONCRETE_TYPES = new Class[] {
ArrayList.class,
HashSet.class
};
public Type getItemGenericType() {
return this.itemGenericType;
}
}