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

org.apache.river.reggie.proxy.ClassMapper Maven / Gradle / Ivy

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership. The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License. You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.apache.river.reggie.proxy;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.Arrays;
import java.util.Comparator;
import java.util.WeakHashMap;
import java.lang.ref.SoftReference;
import java.rmi.MarshalException;

/**
 * Maps Class to ServiceType/Base, Class to EntryClass/Base, and Class to
 * Field[], with caching for efficiency.
 *
 * @author Sun Microsystems, Inc.
 *
 */
class ClassMapper {

    /** Weak Map from Class to SoftReference(ServiceTypeBase) */
    private static final WeakHashMap serviceMap = new WeakHashMap(23);
    /** Weak Map from Class to SoftReference(EntryClassBase) */
    private static final WeakHashMap entryMap = new WeakHashMap(17);
    /** Weak Map from Class to SoftReference(sorted Field[]) */
    private static final WeakHashMap fieldMap = new WeakHashMap(17);
    /** Comparator for sorting fields */
    private static final FieldComparator comparator = new FieldComparator();
    private static final ServiceType[] empty = {};
    private static final Class[] noArg = new Class[0];

    private ClassMapper() {}

    /** Returns a ServiceTypeBase descriptor for a class. */
    public static ServiceTypeBase toServiceTypeBase(Class cls) 
        throws MarshalException 
    {
	synchronized (serviceMap) {
	    return toServiceTypeBase(cls, true);
	}
    }

    /**
     * Returns a ServiceTypeBase descriptor for a class.  If needCodebase
     * is false, the returned descriptor's codebase may be null.
     */
    private static ServiceTypeBase toServiceTypeBase(Class cls,
						     boolean needCodebase)
	throws MarshalException 
    {
	if (cls == null)
	    return null;
	SoftReference cref = (SoftReference)serviceMap.get(cls);
	ServiceTypeBase stype = null;
	if (cref != null)
	    stype = (ServiceTypeBase)cref.get();
	if (stype == null) {
	    stype = new ServiceTypeBase(
			   new ServiceType(cls,
					   toServiceType(cls.getSuperclass()),
					   toServiceType(cls.getInterfaces())),
			   null);
	    serviceMap.put(cls, new SoftReference(stype));
	}
	if (needCodebase && stype.codebase == null)
	    stype.setCodebase(cls);
	return stype;
    }

    /** Returns a ServiceType descriptor for a class. */
    private static ServiceType toServiceType(Class cls) 
	throws MarshalException 
    {
	if (cls != null)
	    return toServiceTypeBase(cls, false).type;
	return null;
    }

    /** Converts an array of Class to an array of ServiceType. */
    public static ServiceType[] toServiceType(Class[] classes) 
	throws MarshalException 
    {
	if (classes == null)
	    return null;
	if (classes.length == 0)
	    return empty;
	ServiceType[] stypes = new ServiceType[classes.length];
	synchronized (serviceMap) {
	    for (int i = classes.length; --i >= 0; ) {
		stypes[i] = toServiceType(classes[i]);
	    }
	}
	return stypes;
    }

    /** Returns a EntryClassBase descriptor for a class. */
    public static EntryClassBase toEntryClassBase(Class cls) 
	throws MarshalException 
    {
	synchronized (entryMap) {
	    return toEntryClassBase(cls, true);
	}
    }

    /**
     * Returns a EntryClassBase descriptor for a class.  If base is false,
     * the returned descriptor's codebase may be null, and the class need
     * not be public and need not have a no-arg constructor.
     */
    private static EntryClassBase toEntryClassBase(Class cls, boolean base) 
        throws MarshalException 
    {
	if (cls == null)
	    return null;
	SoftReference cref = (SoftReference)entryMap.get(cls);
	EntryClassBase eclass = null;
	if (cref != null)
	    eclass = (EntryClassBase)cref.get();
	if (eclass == null) {
	    if (base) {
		if (!Modifier.isPublic(cls.getModifiers()))
		    throw new IllegalArgumentException("entry class " +
						       cls.getName() +
						       " is not public");
		try {
		    cls.getConstructor(noArg);
		} catch (NoSuchMethodException e) {
		    throw new IllegalArgumentException("entry class " +
						       cls.getName() +
			        " does not have a public no-arg constructor");
		}
	    }
	    eclass = new EntryClassBase(
			     new EntryClass(cls,
					    toEntryClass(cls.getSuperclass())),
			     null);
	    entryMap.put(cls, new SoftReference(eclass));
	}
	if (base && eclass.codebase == null)
	    eclass.setCodebase(cls);
	return eclass;
    }

    /** Returns an EntryClass descriptor for a class. */
    private static EntryClass toEntryClass(Class cls) throws MarshalException {
	if (cls != null)
	    return toEntryClassBase(cls, false).eclass;
	return null;
    }

    /** Field of an Entry class, with marshalling information */
    static class EntryField {
	/** Field for the field */
	public final Field field;
	/**
	 * True if instances of the field need to be converted
	 * to MarshalledWrapper.  False if the type of the field
	 * is String, Integer, Boolean, Character, Long, Float,
	 * Double, Byte, or Short.
	 */
	public final boolean marshal;

	/**
	 * Basic constructor.
	 */
	public EntryField(Field field) {
	    this.field = field;
	    Class c = field.getType();
	    marshal = !(c == String.class ||
			c == Integer.class ||
			c == Boolean.class ||
			c == Character.class ||
			c == Long.class ||
			c == Float.class ||
			c == Double.class ||
			c == Byte.class ||
			c == Short.class);
	}
    }

    /**
     * Returns public fields, in super to subclass order, sorted
     * alphabetically within a given class.
     */
    public static EntryField[] getFields(Class cls) {
	synchronized (fieldMap) {
	    SoftReference cref = (SoftReference)fieldMap.get(cls);
	    EntryField[] efields = null;
	    if (cref != null)
		efields = (EntryField[])cref.get();
	    if (efields == null) {
		Field[] fields = cls.getFields();
		Arrays.sort(fields, comparator);
		int len = 0;
		for (int i = 0; i < fields.length; i++) {
		    if ((fields[i].getModifiers() &
			 (Modifier.STATIC|Modifier.FINAL|Modifier.TRANSIENT))
			== 0)
		    {
			if (fields[i].getType().isPrimitive())
			    throw new IllegalArgumentException("entry class " +
							       cls.getName() +
						  " has a primitive field");
			fields[len++] = fields[i];
		    }
		}
		efields = new EntryField[len];
		while (--len >= 0) {
		    efields[len] = new EntryField(fields[len]);
		}
		fieldMap.put(cls, new SoftReference(efields));
	    }
	    return efields;
	}
    }

    /** Comparator for sorting fields. */
    private static class FieldComparator implements Comparator {
	public FieldComparator() {}

	/** Super before subclass, alphabetical within a given class */
	public int compare(Object o1, Object o2) {
	    Field f1 = (Field)o1;
	    Field f2 = (Field)o2;
	    if (f1 == f2)
		return 0;
	    if (f1.getDeclaringClass() == f2.getDeclaringClass())
		return f1.getName().compareTo(f2.getName());
	    if (f1.getDeclaringClass().isAssignableFrom(
						     f2.getDeclaringClass()))
		return -1;
	    return 1;
	}
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy