Package ariba.util.fieldtype

Source Code of ariba.util.fieldtype.JavaTypeProvider$JavaTypeInfo

/*
    Copyright 1996-2008 Ariba, Inc.

    Licensed 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.

    $Id: //ariba/platform/util/expr/ariba/util/fieldtype/JavaTypeProvider.java#18 $
*/

package ariba.util.fieldtype;

import ariba.util.core.Assert;
import ariba.util.core.ClassUtil;
import ariba.util.core.FastStringBuffer;
import ariba.util.core.Fmt;
import ariba.util.core.ListUtil;
import ariba.util.core.MapUtil;
import ariba.util.core.SetUtil;
import ariba.util.core.StringUtil;
import ariba.util.fieldvalue.FieldValueAccessorUtil;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
    @aribaapi private
*/
public class JavaTypeProvider extends TypeProvider
{
    // The mapping table explicit mapping between types beyond the Java
    // referencing widening.  The first entry is the convertTo type and
    // the second entry is the convertFrom type.
    protected static final String[][]  _conversionTable =
        {{String.class.getName(), Character.class.getName()},
         {String.class.getName(), Character.TYPE.getName()}};

    /**
     * A character used in the class name to indicate the dimension of an
     * array element.
     */
    static final char ArrayDimensionIndicator = '[';

    private static TypeProvider DefaultTypeProvider = new JavaTypeProvider();
    private static Map _classMap = MapUtil.map();
    private static Map _safeJavaClassMap = MapUtil.map();

    static {
       populateSafeClassAlias();
    }

    private JavaTypeProvider ()
    {
        super(JavaTypeProviderId);
    }

    public static TypeProvider instance ()
    {
        return DefaultTypeProvider;
    }

    public TypeInfo getTypeInfo (String name)
    {
        TypeInfo info = (TypeInfo)_classMap.get(name);
        if (info == null) {
            synchronized(this) {
                info = (TypeInfo)_classMap.get(name);
                if (info == null) {
                    Class classForName = ClassUtil.classForName(name, false);
                    if (classForName != null) {
                         info = createTypeInfo(classForName);
                         _classMap.put(name, info);
                    }
                    else {
                        info = getAliasedClass(this, name);
                    }
                }
            }
        }

        return (info != null ?
                createContainerTypeIfNecessary((JavaTypeInfo)info) :
                null);
    }

    public TypeInfo getTypeInfo (Class cls)
    {
        String name = cls.getName();
        TypeInfo info = getTypeInfo(name);
        if (info == null) {
            synchronized(this) {
                 info = createTypeInfo(cls);
                // Todo: invalidate on reload?
                // In the event that this class came from an alternate class loader
                // and may be reloaded (e.g. in Groovy) we may end up with an ambiguous or
                // stale class under this name...
                 _classMap.put(name, info);
            }
        }

        return (info != null ?
                createContainerTypeIfNecessary((JavaTypeInfo)info) :
                null);
    }

    private TypeInfo createTypeInfo (Class classObj)
    {
        return new JavaTypeInfo(classObj);
    }

    private TypeInfo createContainerTypeIfNecessary (JavaTypeInfo type)
    {
        Class collectionClass = java.util.Collection.class;
        boolean isContainerType =
                collectionClass.isAssignableFrom(type._proxiedClass);

        if (isContainerType) {
            TypeInfo objType = getTypeInfo("java.lang.Object");
            Assert.that(objType != null, "Fail to find type info for 'java.lang.Object'");
            return new ContainerTypeInfo(type, objType);
        }

        TypeInfo arrayType = createArrayTypeInfoIfNecessary(type);
        return (arrayType != null ? arrayType : type);
    }

    /**
     * Create a <code>ContainerTypeInfo</code> if the type is an array type.
     * @param type - the name of the array class
     * @return the <code>TypeInfo</code> for the array class.  If the
     * name does not corresponding to an array class, return null.
     */
    protected TypeInfo createArrayTypeInfoIfNecessary (JavaTypeInfo type)
    {
        Class proxiedClass = type.getProxiedClass();
        if (proxiedClass == null || !proxiedClass.isArray()) {
            return null;
        }

        String name = type.getName();
        int  index = 0;
        char current = name.charAt(index);
        while (index < name.length() && current == ArrayDimensionIndicator) {
            current = name.charAt(++index);
        }

        if (index > 0 && index < name.length()) {
            if ((name.length() - (index+1) < 2) || current != 'L') {
                return null;
            }

            String elementTypeName = name.substring(index+1,name.length()-1);
            TypeInfo elementInfo = getTypeInfo(elementTypeName);

            if (elementInfo != null) {
                TypeInfo arrayInfo = new JavaTypeProvider.JavaArrayTypeInfo(
                                                     proxiedClass, index+1);

                return new ContainerTypeInfo(arrayInfo, elementInfo);
            }
        }

        return null;
    }

    /**
     * Populate the cache with safe java alias
     */
    private static void populateSafeClassAlias ()
    {
        Set safeClassNames = SafeJavaRepository.getInstance().getAllSafeClassNames();
        Iterator iter = safeClassNames.iterator();
        while (iter.hasNext()) {
            String name = (String)iter.next();
            String[] elements = StringUtil.delimitedStringToArray(name, '.');
            if (elements.length > 0) {
                String shortName = elements[elements.length-1];
                if (!shortName.equals(name)) {
                    // Is this classname has a known short name defined in
                    // ClassAliasRepository? If so, then skip this classname.
                    String knownShortName =
                            ClassAliasRepository.getInstance().getAliasForClassName(name);
                    if (StringUtil.nullOrEmptyOrBlankString(knownShortName)) {
                        // If there is no other class using this short name,
                        // put it in the class map.
                        if (_safeJavaClassMap.get(shortName) == null) {
                            _safeJavaClassMap.put(shortName, name);
                        }
                        else {
                            // If there is another class using this shortname,
                            // then assert.
                            Assert.that(true, Fmt.S("%s%s%s",
                                "The shortname '" + shortName + "' is ambiguous.",
                                " It is being defined in classes '" + name + "'",
                                " and '" + _safeJavaClassMap.get(shortName) + "'.",
                                " Please register the proper short name in platform/util/expr/fieldtype/ClassAlias.csv."));
                        }
                    }
                }
            }
        }
    }

    protected static TypeInfo getAliasedClass (TypeRetriever retriever, String name)
    {
        String className =
                ClassAliasRepository.getInstance().getClassNameForAlias(name);
        if (!StringUtil.nullOrEmptyOrBlankString(className) &&
            !className.equals(name)) {
            return retriever.getTypeInfo(className);
        }
        className = (String)_safeJavaClassMap.get(name);
        if (!StringUtil.nullOrEmptyOrBlankString(className) &&
            !className.equals(name)) {
            return retriever.getTypeInfo(className);
        }
        return null;
    }

    //---------------------------------------------------------------------
    // JavaTypeInfo

    /**
     * Subclass of <code>TypeInfo</code> for Java class.
     */
    public static class JavaTypeInfo implements TypeInfo, PropertyResolver
    {

        protected Class   _proxiedClass;
        protected Map     _fieldInfos;
        protected Map     _methodInfos;
        protected boolean _hasMethodInfoLoaded = false;
        protected boolean _isJavaObjectType;

        JavaTypeInfo ()
        {
            _fieldInfos = MapUtil.map();
            _methodInfos = MapUtil.map();
        }

        JavaTypeInfo (Class proxiedClass)
        {
            this();
            setProxiedClass(proxiedClass);
        }

        public String      getName ()
        {
            return _proxiedClass.getName();
        }

        public String      getImplementationName ()
        {
            return getName();
        }

        public Class getProxiedClass ()
        {
            return _proxiedClass;
        }

        public void setProxiedClass (Class proxiedClass)
        {
            _proxiedClass = proxiedClass;
            _isJavaObjectType = (proxiedClass == Object.class);
        }

        /**
         * @see TypeInfo#getElementType()
         */
        public TypeInfo   getElementType ()
        {
            return null;
        }

        public boolean isAssignableFrom (TypeInfo other)
        {
            JavaTypeInfo otherType = getTypeInfoInProvider(other);
            if (otherType == null) {
                return false;
            }

            return _proxiedClass.isAssignableFrom(otherType._proxiedClass);
        }

        public boolean isCompatible (TypeInfo other)
        {
            TypeInfo otherType = getTypeInfoInProvider(other);
            if (otherType == null) {
                return false;
            }

            return equals(otherType) ||
                   isWideningTypeOf(otherType) ||
                   otherType.isWideningTypeOf(this);
        }

        public boolean isWideningTypeOf (TypeInfo other)
        {
            TypeInfo type = getTypeInfoInProvider(other);
            if (type == null) {
                return false;
            }

            // A type is a widening type from the other, if this type
            // is a assignable from the other type.
            if (isAssignableFrom(other)) {
                return true;
            }

            // if the this is not a superclass of "other", then check
            // the mapping table.
            for (int i=0; i < JavaTypeProvider._conversionTable.length; i++) {
                String[] entry = JavaTypeProvider._conversionTable[i];
                if (entry[0].equals(this.getName()) &&
                    entry[1].equals(other.getName())) {
                    return true;
                }
            }

            return false;
        }

        private JavaTypeInfo getTypeInfoInProvider (TypeInfo other)
        {
            if (other == null) {
                return null;
            }
           
            if (other instanceof NullTypeInfo) {
                return null;
            }
           
            JavaTypeInfo otherInfo = null;
            otherInfo = (JavaTypeInfo)(
                other instanceof JavaTypeInfo ?
                other :
                getTypeProvider().getTypeInfo(other.getImplementationName()));
            return otherInfo;
        }

        public FieldInfo getField (TypeRetriever retriever, String name)
        {
            try {
                FieldInfo fieldInfo = (FieldInfo)_fieldInfos.get(name);
                if (fieldInfo == null) {
                    synchronized (this) {
                        fieldInfo = (FieldInfo)_fieldInfos.get(name);
                        if (fieldInfo == null) {
                            Field field = _proxiedClass.getField(name);
                            fieldInfo = createFieldInfo(field);
                            _fieldInfos.put(name, fieldInfo);
                        }
                    }
                }
                return fieldInfo;
            }
            catch (Throwable e) {
                // ToDo
            }

            return null;
        }

        public MethodInfo getMethod (TypeRetriever retriever,
                                     String name,
                                     List parameters)
        {
            return getMethod(retriever, name, parameters, false);
        }

        public MethodInfo getMethod (TypeRetriever retriever,
                                     String name,
                                     List parameters,
                                     boolean staticOnly)
        {
            try {
                if (!_hasMethodInfoLoaded) {
                    synchronized (this) {
                        if (!_hasMethodInfoLoaded) {
                            loadMethods(retriever);
                        }
                    }
                }

                String mungedName = JavaMethodInfo.getMungedMethodName(name, parameters);
                MethodInfo methodInfo = (MethodInfo)_methodInfos.get(mungedName);
                if (methodInfo == null || (staticOnly && !methodInfo.isStatic())) {
                    methodInfo = findAppropriateMethodInfo(retriever, name, parameters, staticOnly);
                }
                return methodInfo;
            }
            catch (Throwable e) {
                // ToDo
                //e.printStackTrace();
            }
            return null;
        }
       
        public Set/*<MethodInfo>*/ getAllMethods (TypeRetriever retriever)
        {
            try {
                if (!_hasMethodInfoLoaded) {
                    synchronized (this) {
                        if (!_hasMethodInfoLoaded) {
                            loadMethods(retriever);
                        }
                    }
                }
                Set/*<MethodInfo>*/ result = SetUtil.set();
                result.addAll(_methodInfos.values());
                return result;
            }
            catch (Throwable e) {
                return null;
                // ToDo
                //e.printStackTrace();
            }
        }

        public int getAccessibility ()
        {
            int modifiers = _proxiedClass.getModifiers();
            return (Modifier.isPublic(modifiers) ?
                         TypeInfo.AccessibilitySafe :
                         TypeInfo.AccessibilityPrivate);
        }

        public String toString ()
        {
            return getName();
        }

        public PropertyResolver getPropertyResolver ()
        {
            return this;
        }

        public TypeInfo resolveTypeForName (TypeRetriever retriever, String name)
        {
            PropertyInfo property = resolvePropertyForName(retriever, name);
            if (property != null) {
                return property.getType(retriever);
            }

            return null;
        }

        public PropertyInfo resolvePropertyForName (TypeRetriever retriever,
                                                    String name)
        {
            FieldInfo field = getField(retriever, name);
            if (field != null) {
                return field;
            }

            loadMethods(retriever);
            Iterator methods = _methodInfos.keySet().iterator();
            while (methods.hasNext()) {
                String key = (String)methods.next();
                JavaMethodInfo method = (JavaMethodInfo)_methodInfos.get(key);
                String methodName = method.getMethod().getName();
                if (FieldValueAccessorUtil.matchForGetter(name, methodName)) {
                    return method;
                }
            }

            return null;
        }

        protected FieldInfo createFieldInfo (Field field)
        {
            return new JavaFieldInfo(this, field);
        }

        protected MethodInfo createMethodInfo (TypeRetriever retriever,
                                               Method method)
        {
            return new JavaMethodInfo(retriever, this, method, false);
        }

        protected MethodInfo createMethodInfo (TypeRetriever retriever,
                                               Method method,
                                               boolean isSafe)
        {
            return new JavaMethodInfo(retriever, this, method, isSafe);
        }

        private void loadMethods (TypeRetriever retriever)
        {
            if (!_hasMethodInfoLoaded) {
                Method[] methods = _proxiedClass.getMethods();
                if (methods != null) {
                    MethodSpecification specification =
                        SafeJavaRepository.getInstance().getAllSafeMethodsForClass(
                            _proxiedClass);
                    for (int i=0; i < methods.length; i++) {
                        Method method = methods[i];
                        boolean isSafe = (specification != null ?
                                          specification.isSatisfiedBy(method) :
                                          false);
                        JavaMethodInfo methodInfo = (JavaMethodInfo)
                            createMethodInfo(retriever, method, isSafe);
                        _methodInfos.put(
                            methodInfo.getMungedName(),
                            methodInfo);
                    }
                }
                _hasMethodInfoLoaded = true;
            }
        }

        public MethodInfo findAppropriateMethodInfo (TypeRetriever retriever,
                                                      String name,
                                                      List parameters,
                                                      boolean staticOnly)
        {
            return findMethodInfo(retriever,
                    (Collection <MethodInfo>)_methodInfos.values(), name,
                    parameters, staticOnly);
        }

        private MethodInfo findMethodInfo (TypeRetriever retriever,
                                           Collection <MethodInfo> methods,
                                           String name,
                                           List parameters,
                                           boolean staticOnly)
        {
            MethodInfo mostSpecific = null;

            // Find matching method without narrowing or widening
            Iterator iter = methods.iterator();
            while (iter.hasNext()) {
                MethodInfo method = (MethodInfo)iter.next();
                if (isMatchingMethod(retriever, method, name, parameters,
                        false, false, staticOnly)) {
                    return method;
                }
            }

            // Find matching method with type widening
            iter = methods.iterator();
            while (iter.hasNext()) {
                MethodInfo method = (MethodInfo)iter.next();
                if (isMatchingMethod(retriever, method, name, parameters,
                        true, false, staticOnly)) {
                    if (mostSpecific == null ||
                        isMoreSpecific(retriever, mostSpecific, method)) {
                        mostSpecific = method;
                    }
                }
            }

            if (mostSpecific != null) {
                return mostSpecific;
            }

            // Find metching method with type narrowing
            iter = methods.iterator();
            while (iter.hasNext()) {
                MethodInfo method = (MethodInfo)iter.next();
                if (isMatchingMethod(retriever, method, name, parameters,
                        false, true, staticOnly)) {
                     if (mostSpecific == null ||
                        isMoreSpecific(retriever, mostSpecific, method)) {
                        mostSpecific = method;
                    }
                }
            }

            return mostSpecific;
        }

        private boolean isMatchingMethod (TypeRetriever retriever,
                                          MethodInfo method,
                                          String name,
                                          List parameters,
                                          boolean allowWidening,
                                          boolean allowNarrowing,
                                          boolean staticOnly)
        {
            // Name match test
            if (!method.getName().equals(name)) {
                return false;
            }

            if (staticOnly && !method.isStatic()) {
                return false;
            }

            // p1 - formal parameters
            // p2 - acutal parameters
            List p1 = method.getParameters(retriever);
            List p2 = parameters;

            // Number of Params match test
            int  ps1 = (p1 != null ? p1.size() : 0);
            int  ps2 = (p2 != null ? p2.size() : 0);
            if (ps1 != ps2) {
                return false;
            }

            // parameter type test.  Actual parameter (p2) must be assignable
            // to method signature.
            if (ps1 == 0 && ps2 == 0) {
                return true;
            }

            for (int i=0; i < p2.size(); i++) {
                TypeInfo p2Info = (TypeInfo)p2.get(i);
                TypeInfo p1Info = (TypeInfo)p1.get(i);
                if (p2Info instanceof NullTypeInfo &&
                    !PrimitiveTypeProvider.isSimplePrimitiveType(p1Info)) {
                    continue;
                }

                // Check for type equality
                if (p1Info.isWideningTypeOf(p2Info) &&
                    p2Info.isWideningTypeOf(p1Info)) {
                    continue;
                }

                // Check for widening
                if (allowWidening && p1Info.isWideningTypeOf(p2Info)) {
                    continue;
                }

                // If both P1 and P2 are primitives, check if it is possible
                // to do a implicit cast for narrowing conversion.
                if (allowNarrowing &&
                    PrimitiveTypeProvider.isSupportedType(p1Info.getName()) &&
                    PrimitiveTypeProvider.isSupportedType(p2Info.getName()) &&
                    PrimitiveTypeProvider.isWideningTypeOf(p2Info, p1Info)) {
                    continue;
                }

                return false;
            }

            return true;
        }

        private boolean isMoreSpecific (TypeRetriever retriever,
                                        MethodInfo method1,
                                        MethodInfo method2 )
        {
            List <TypeInfo> p1Types = method1.getParameters(retriever);
            List <TypeInfo> p2Types = method2.getParameters(retriever);

            for ( int index=0, count=p1Types.size(); index < count; ++index )
            {
                TypeInfo p1Info = p1Types.get(index);
                TypeInfo p2Info = p2Types.get(index);

                // P2 is more specific
                if (p1Info.isWideningTypeOf(p2Info)) {
                    return false;
                }

                // p1 is more specific
                if (p2Info.isWideningTypeOf(p1Info)) {
                    return true;
                }
            }

            // They are the same!  So the first is not more specific than the second.
            return false;
        }

        protected TypeProvider getTypeProvider ()
        {
            return JavaTypeProvider.instance();
        }
    }

    //---------------------------------------------------------------------
    // ArrayTypeInfo

    /**
     * Subclass of <code>TypeInfo</code> for array type.
     */
    static class JavaArrayTypeInfo extends JavaTypeInfo
    {
        protected int      _dimension;

        JavaArrayTypeInfo (Class proxiedClass, int dimension)
        {
            super(proxiedClass);
            _dimension = dimension;
        }

        public int getDimension ()
        {
            return _dimension;
        }

        public void setDimension (int dimension)
        {
            _dimension = dimension;
        }
    }

    //---------------------------------------------------------------------
    // JavaFieldInfo

    static class JavaFieldInfo implements FieldInfo
    {
        protected Field _proxiedField;
        protected TypeInfo _parentType;

        JavaFieldInfo (TypeInfo type, Field field)
        {
            _proxiedField = field;
            _parentType = type;
        }

        public String  getName ()
        {
            return _proxiedField.getName();
        }

        public TypeInfo    getType (TypeRetriever retriever)
        {
            Class fieldType = _proxiedField.getType();
            return retriever.getTypeInfo(fieldType.getName());
        }

        public TypeInfo getParentType ()
        {
            return _parentType;
        }

        public int getAccessibility ()
        {
            int modifiers = _proxiedField.getModifiers();
            return (Modifier.isPublic(modifiers) ?
                         TypeInfo.AccessibilitySafe :
                         TypeInfo.AccessibilityPrivate);
        }

        public String toString ()
        {
            return getName();
        }
    }

    public static class JavaMethodInfo implements MethodInfo
    {
        protected Method     _proxiedMethod;
        protected TypeInfo   _parentType;
        protected String     _mungedName;
        protected boolean    _isSafe;

        protected List /* <TypeInfo> */ _parametersType = null;
        protected boolean               _hasParametersLoaded = false;

        protected TypeInfo   _returnType = null;

        JavaMethodInfo (TypeRetriever retriever,
                        TypeInfo type,
                        Method method,
                        boolean isSafe)
        {
            _proxiedMethod = method;
            _parentType = type;
            _parametersType = getParameters(retriever);
            _returnType = getReturnType(retriever);
            _mungedName = getMungedMethodName(getName(), _parametersType);
            _isSafe = isSafe;
        }

        public String      getName ()
        {
            return _proxiedMethod.getName();
        }

        public String  getMungedName ()
        {
            return _mungedName;
        }

        public boolean isStatic ()
        {
            return Modifier.isStatic(_proxiedMethod.getModifiers());
        }

        public Method getMethod ()
        {
            return _proxiedMethod;
        }


        /**
         *
         * @param retriever
         * @return Return an empty list if there is no parameter.  If any
         * parameter type cannot be retrieved, return null.
         */
        public List /* <TypeInfo> */ getParameters (TypeRetriever retriever)
        {
            if (_parametersType == null && !_hasParametersLoaded) {
                Class[] classes = _proxiedMethod.getParameterTypes();
                List types = ListUtil.list();
                if (classes != null) {
                    for (int i=0; i < classes.length; i++) {
                        TypeInfo type = retriever.getTypeInfo(classes[i].getName());
                        if (type != null) {
                            types.add(type);
                        }
                        else {
                            types = null;
                            break;
                        }
                    }
                }

                _hasParametersLoaded = true;
                _parametersType = types;
            }
            return _parametersType;
        }

        /**
         *
         * @param retriever
         * @return Return a void class if there is no return type.  If the
         * return type exists but cannot be found, then return null.
         */
        public TypeInfo    getReturnType (TypeRetriever retriever)
        {
            if (_returnType == null) {
                Class type = _proxiedMethod.getReturnType();
                if (type != null) {
                    _returnType = retriever.getTypeInfo(type.getName());
                }
            }
            return _returnType;
        }

        public TypeInfo    getType (TypeRetriever retriever)
        {
            return getReturnType(retriever);
        }

        public int getAccessibility ()
        {
            return (_isSafe ?
                         TypeInfo.AccessibilitySafe :
                         TypeInfo.AccessibilityPrivate);
        }

        public String toString ()
        {
            return getName();
        }

        public TypeInfo    getParentType ()
        {
            return _parentType;
        }

        static String getMungedMethodName (String methodName,
                                           List parameters)
        {
            FastStringBuffer buffer = new FastStringBuffer();
            buffer.append(methodName);
            if (!ListUtil.nullOrEmptyList(parameters)) {
                for (int i=0; i < parameters.size(); i++) {
                    TypeInfo paramType = (TypeInfo)parameters.get(i);
                    buffer.append('#');
                    buffer.append(paramType.getName());
                }
            }
            return buffer.toString();
        }

        static boolean hasNullParameter (List parameters)
        {
            if (!ListUtil.nullOrEmptyList(parameters)) {
                for (int i=0; i < parameters.size(); i++) {
                    TypeInfo paramType = (TypeInfo)parameters.get(i);
                    if (paramType instanceof NullTypeInfo) {
                        return true;
                    }
                }
            }
            return false;
        }
    }
}
TOP

Related Classes of ariba.util.fieldtype.JavaTypeProvider$JavaTypeInfo

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.