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

java.lang.reflect.AccessibleObject 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.
 */
/*
 * Copyright (C) 2008 The Android Open Source Project
 *
 * 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.
 */
/*
 * Copyright (C) 2012 Trillian Mobile AB
 *
 * 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.
 */

package java.lang.reflect;

import java.lang.annotation.Annotation;
import java.util.Hashtable;
import org.robovm.rt.ReflectionAccess;

/**
 * {@code AccessibleObject} is the superclass of all member reflection classes
 * (Field, Constructor, Method). AccessibleObject provides the ability to toggle
 * a flag controlling access checks for these objects. By default, accessing a
 * member (for example, setting a field or invoking a method) checks the
 * validity of the access (for example, invoking a private method from outside
 * the defining class is prohibited) and throws IllegalAccessException if the
 * operation is not permitted. If the accessible flag is set to true, these
 * checks are omitted. This allows privileged code, such as Java object
 * serialization, object inspectors, and debuggers to have complete access to
 * objects.
 *
 * @see Field
 * @see Constructor
 * @see Method
 */
public class AccessibleObject implements AnnotatedElement {
    static final ReflectionAccess REFLECTION_ACCESS = new ReflectionAccess() {
        
        public Method[] clone(Method[] m) {
            if (m.length == 0) {
                return m;
            }
            Method[] result = new Method[m.length];
            for (int i = 0; i < result.length; i++) {
                result[i] = new Method(m[i]);
            }
            return result;
        }
        
        @SuppressWarnings({ "unchecked", "rawtypes" })
        public Constructor[] clone(Constructor[] c) {
            if (c.length == 0) {
                return c;
            }
            Constructor[] result = new Constructor[c.length];
            for (int i = 0; i < result.length; i++) {
                result[i] = new Constructor(c[i]);
            }
            return result;
        }
        
        public Field[] clone(Field[] f) {
            if (f.length == 0) {
                return f;
            }
            Field[] result = new Field[f.length];
            for (int i = 0; i < result.length; i++) {
                result[i] = new Field(f[i]);
            }
            return result;
        }
        
        public Method clone(Method m) {
            return new Method(m);
        }
        
        @SuppressWarnings({ "rawtypes", "unchecked" })
        public Constructor clone(Constructor c) {
            return new Constructor(c);
        }
        
        public Field clone(Field f) {
            return new Field(f);
        }
        
        public boolean equals(Method a, Method b) {
            if (a.getReturnType() != b.getReturnType()) {
                return false;
            }
            if (!a.getName().equals(b.getName())) {
                return false;
            }
            return compareClassLists(a.getParameterTypes(false), b.getParameterTypes(false));
        }
        
        public boolean matchParameterTypes(Constructor c, Class[] parameterTypes) {
            return compareClassLists(c.getParameterTypes(false), parameterTypes);
        }

        public boolean matchParameterTypes(Method m, Class[] parameterTypes) {
            return compareClassLists(m.getParameterTypes(false), parameterTypes);
        }
        
        private boolean compareClassLists(Class[] a, Class[] b) {
            // Copied from Android's ClassCache.java
            // Start (C) Android
            if (a == null) {
                return (b == null) || (b.length == 0);
            }

            int length = a.length;

            if (b == null) {
                return (length == 0);
            }

            if (length != b.length) {
                return false;
            }

            for (int i = length - 1; i >= 0; i--) {
                if (a[i] != b[i]) {
                    return false;
                }
            }

            return true;
            // End (C) Android
        }        
    };
    
    /**
     * If true, object is accessible, bypassing normal access checks
     */
    boolean flag = false;

    // Holds a mapping from Java type names to native type codes.
    static Hashtable trans;

    static {
        trans = new Hashtable(9);
        trans.put("byte", "B");
        trans.put("char", "C");
        trans.put("short", "S");
        trans.put("int", "I");
        trans.put("long", "J");
        trans.put("float", "F");
        trans.put("double", "D");
        trans.put("void", "V");
        trans.put("boolean", "Z");
    }

    /**
     * Attempts to set the value of the accessible flag for all the objects in
     * the array provided. Setting this
     * flag to {@code false} will enable access checks, setting to {@code true}
     * will disable them.
     *
     * @param objects
     *            the accessible objects
     * @param flag
     *            the new value for the accessible flag
     *
     * @see #setAccessible(boolean)
     */
    public static void setAccessible(AccessibleObject[] objects, boolean flag) {
        for (AccessibleObject object : objects) {
            object.flag = flag;
        }
    }

    /**
     * Constructs a new {@code AccessibleObject} instance. {@code
     * AccessibleObject} instances can only be constructed by the virtual
     * machine.
     */
    protected AccessibleObject() {
    }

    /**
     * Indicates whether this object is accessible without access checks being
     * performed. Returns the accessible flag.
     *
     * @return {@code true} if this object is accessible without access
     *         checks, {@code false} otherwise
     */
    public boolean isAccessible() {
        return flag;
    }

    /**
     * Attempts to set the value of the accessible flag. Setting this flag to
     * {@code false} will enable access checks, setting to {@code true} will
     * disable them.
     *
     * @param flag
     *            the new value for the accessible flag
     */
    public void setAccessible(boolean flag) {
        this.flag = flag;
    }

    public boolean isAnnotationPresent(Class annotationType) {
        if (annotationType == null) {
            throw new NullPointerException("annotationType == null");
        }
        return getAnnotation(annotationType) != null;
    }
    
    public Annotation[] getDeclaredAnnotations() {
        return getDeclaredAnnotations(true);
    }

    protected Annotation[] getDeclaredAnnotations(boolean copy) {
        throw new UnsupportedOperationException();
    }

    public Annotation[] getAnnotations() {
        // for all but Class, getAnnotations == getDeclaredAnnotations
        return getDeclaredAnnotations();
    }

    /* slow, but works for all sub-classes */
    public  T getAnnotation(Class annotationType) {
        if (annotationType == null) {
            throw new NullPointerException("annotationType == null");
        }
        Annotation[] annos = getDeclaredAnnotations(false);
        for (int i = annos.length-1; i >= 0; --i) {
            if (annos[i].annotationType() == annotationType) {
                return (T) annos[i];
            }
        }
        return null;
    }

    /**
     * Returns the signature for a class. This is the kind of signature used
     * internally by the JVM, with one-character codes representing the basic
     * types. It is not suitable for printing.
     *
     * @param clazz
     *            the class for which a signature is required
     *
     * @return The signature as a string
     */
    String getSignature(Class clazz) {
        String result = "";
        String nextType = clazz.getName();

        if(trans.containsKey(nextType)) {
            result = trans.get(nextType);
        } else {
            if(clazz.isArray()) {
                result = "[" + getSignature(clazz.getComponentType());
            } else {
                result = "L" + nextType + ";";
            }
        }
        return result;
    }

    /**
     * Returns a printable String consisting of the canonical names of the
     * classes contained in an array. The form is that used in parameter and
     * exception lists, that is, the class or type names are separated by
     * commas.
     *
     * @param types
     *            the array of classes
     *
     * @return The String of names
     */
    String toString(Class[] types) {
        StringBuilder result = new StringBuilder();

        if (types.length != 0) {
            appendTypeName(result, types[0]);
            for (int i = 1; i < types.length; i++) {
                result.append(',');
                appendTypeName(result, types[i]);
            }
        }

        return result.toString();
    }

    /**
     * Gets the Signature attribute for this instance. Returns {@code null}
     * if not found.
     */
    /*package*/ String getSignatureAttribute() {
        /*
         * Note: This method would have been declared abstract, but the
         * standard API lists this class as concrete.
         */
        throw new UnsupportedOperationException();
    }

    static boolean isSamePackage(Class c1, Class c2) {
        if (c1 == c2) {
            return true;
        }
        if (c1.getClassLoader() != c2.getClassLoader()) {
            return false;
        }
        String n1 = c1.getName();
        String n2 = c2.getName();
        int dot1 = n1.lastIndexOf('.');
        int dot2 = n2.lastIndexOf('.');
        if (dot1 == -1) {
            return dot2 == -1;
        }
        if (dot1 != dot2) {
            return false;
        }
        return n1.substring(0, dot1).equals(n2.substring(0, dot2));
    }

    /**
     * Returns the access flags for the specified {@link Class} without checking the InnerClasses
     * attribute for inner classes. This is described in this bug report:
     * http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4471811
     */
    static native int getAccessFlags(Class cls);

    static boolean checkAccessibleFast(Member member) {
        int access = member.getModifiers();
        return (getAccessFlags(member.getDeclaringClass()) & access & Modifier.PUBLIC) > 0;
    }
    
    static boolean checkAccessible(Class caller, Member member) {
        Class callee = member.getDeclaringClass();
        if (caller == callee) {
            return true;
        }
        
        boolean samePackage = false;
        
        // Check if callee class is accessible
        if ((getAccessFlags(callee) & Modifier.PUBLIC) == 0) {
            samePackage = isSamePackage(caller, callee);
            if (!samePackage) {
                return false;
            }
        }
        
        // Check member accessible
        int access = member.getModifiers();
        if ((access & Modifier.PUBLIC) > 0) {
            return true;
        }
        if ((access & Modifier.PRIVATE) > 0) {
            return false;
        }
        if ((access & Modifier.PROTECTED) > 0) {
            Class sc = caller.getSuperclass();
            while (sc != null) {
                if (sc == callee) {
                    return true;
                }
                sc = sc.getSuperclass();
            }
        }
        return samePackage || isSamePackage(caller, callee);
    }
    
    /**
     * Appends the best {@link #toString} name for {@code c} to {@code out}.
     * This works around the fact that {@link Class#getName} is lousy for
     * primitive arrays (it writes "[C" instead of "char[]") and {@link
     * Class#getCanonicalName()} is lousy for nested classes (it uses a "."
     * separator rather than a "$" separator).
     */
    void appendTypeName(StringBuilder out, Class c) {
        int dimensions = 0;
        while (c.isArray()) {
            c = c.getComponentType();
            dimensions++;
        }
        out.append(c.getName());
        for (int d = 0; d < dimensions; d++) {
            out.append("[]");
        }
    }

    /**
     * Appends names of the specified array classes to the buffer. The array
     * elements may represent a simple type, a reference type or an array type.
     * Output format: java.lang.Object[], java.io.File, void
     *
     * @param types array of classes to print the names
     * @throws NullPointerException if any of the arguments is null
     */
    void appendArrayGenericType(StringBuilder sb, Type[] types) {
        if (types.length > 0) {
            appendGenericType(sb, types[0]);
            for (int i = 1; i < types.length; i++) {
                sb.append(',');
                appendGenericType(sb, types[i]);
            }
        }
    }

    /**
     * Appends the generic type representation to the buffer.
     *
     * @param sb buffer
     * @param obj the generic type which representation should be appended to the buffer
     *
     * @throws NullPointerException if any of the arguments is null
     */
    void appendGenericType(StringBuilder sb, Type obj) {
        if (obj instanceof TypeVariable) {
            sb.append(((TypeVariable)obj).getName());
        } else if (obj instanceof ParameterizedType) {
            sb.append(obj.toString());
        } else if (obj instanceof GenericArrayType) { //XXX: is it a working branch?
            Type simplified = ((GenericArrayType)obj).getGenericComponentType();
            appendGenericType(sb, simplified);
            sb.append("[]");
        } else if (obj instanceof Class) {
            Class c = ((Class)obj);
            if (c.isArray()){
                String as[] = c.getName().split("\\[");
                int len = as.length-1;
                if (as[len].length() > 1){
                    sb.append(as[len].substring(1, as[len].length()-1));
                } else {
                    char ch = as[len].charAt(0);
                    if (ch == 'I')
                        sb.append("int");
                    else if (ch == 'B')
                        sb.append("byte");
                    else if (ch == 'J')
                        sb.append("long");
                    else if (ch == 'F')
                        sb.append("float");
                    else if (ch == 'D')
                        sb.append("double");
                    else if (ch == 'S')
                        sb.append("short");
                    else if (ch == 'C')
                        sb.append("char");
                    else if (ch == 'Z')
                        sb.append("boolean");
                    else if (ch == 'V') //XXX: is it a working branch?
                        sb.append("void");
                }
                for (int i = 0; i < len; i++){
                    sb.append("[]");
                }
            } else {
                sb.append(c.getName());
            }
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy