001/*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *   https://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing,
013 * software distributed under the License is distributed on an
014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 * KIND, either express or implied.  See the License for the
016 * specific language governing permissions and limitations
017 * under the License.
018 */
019package org.apache.bcel.generic;
020
021import org.apache.bcel.Const;
022import org.apache.bcel.Repository;
023import org.apache.bcel.classfile.JavaClass;
024import org.apache.bcel.classfile.Utility;
025
026/**
027 * Denotes reference such as {@link String}.
028 */
029public class ObjectType extends ReferenceType {
030
031    /**
032     * Constructs a new instance.
033     *
034     * @param className fully qualified class name, for example {@link String}.
035     * @return A new instance.
036     * @since 6.0
037     */
038    public static ObjectType getInstance(final String className) {
039        return new ObjectType(className);
040    }
041
042    private final String className; // Class name of type
043
044    /**
045     * Constructs a new instance.
046     *
047     * @param className fully qualified class name, for example {@link String}.
048     */
049    public ObjectType(final String className) {
050        super(Const.T_REFERENCE, "L" + Utility.packageToPath(className) + ";");
051        this.className = Utility.pathToPackage(className);
052    }
053
054    /**
055     * Java Virtual Machine Specification edition 2, 5.4.4 Access Control.
056     *
057     * @param accessor The accessing type.
058     * @return true if accessible.
059     * @throws ClassNotFoundException Thrown if the class referenced by this type can't be found.
060     */
061    public boolean accessibleTo(final ObjectType accessor) throws ClassNotFoundException {
062        final JavaClass jc = Repository.lookupClass(className);
063        if (jc.isPublic()) {
064            return true;
065        }
066        final JavaClass acc = Repository.lookupClass(accessor.className);
067        return acc.getPackageName().equals(jc.getPackageName());
068    }
069
070    /**
071     * @return true if both type objects refer to the same class.
072     */
073    @Override
074    public boolean equals(final Object type) {
075        return type instanceof ObjectType && ((ObjectType) type).className.equals(className);
076    }
077
078    /**
079     * @return name of referenced class.
080     */
081    @Override
082    public String getClassName() {
083        return className;
084    }
085
086    /**
087     * Gets the hash code.
088     *
089     * @return A hash code value for the object.
090     */
091    @Override
092    public int hashCode() {
093        return className.hashCode();
094    }
095
096    /**
097     * If "this" doesn't reference a class, it references an interface or a non-existant entity.
098     *
099     * @return true if it references a class, false otherwise.
100     * @deprecated (since 6.0) this method returns an inaccurate result if the class or interface referenced cannot be
101     *             found: use referencesClassExact() instead
102     */
103    @Deprecated
104    public boolean referencesClass() {
105        try {
106            final JavaClass jc = Repository.lookupClass(className);
107            return jc.isClass();
108        } catch (final ClassNotFoundException e) {
109            return false;
110        }
111    }
112
113    /**
114     * Return true if this type references a class, false if it references an interface.
115     *
116     * @return true if the type references a class, false if it references an interface.
117     * @throws ClassNotFoundException Thrown if the class or interface referenced by this type can't be found
118     */
119    public boolean referencesClassExact() throws ClassNotFoundException {
120        final JavaClass jc = Repository.lookupClass(className);
121        return jc.isClass();
122    }
123
124    /**
125     * If "this" doesn't reference an interface, it references a class or a non-existant entity.
126     *
127     * @return true if it references an interface, false otherwise.
128     * @deprecated (since 6.0) this method returns an inaccurate result if the class or interface referenced cannot be
129     *             found: use referencesInterfaceExact() instead
130     */
131    @Deprecated
132    public boolean referencesInterface() {
133        try {
134            final JavaClass jc = Repository.lookupClass(className);
135            return !jc.isClass();
136        } catch (final ClassNotFoundException e) {
137            return false;
138        }
139    }
140
141    /**
142     * Return true if this type references an interface, false if it references a class.
143     *
144     * @return true if the type references an interface, false if it references a class.
145     * @throws ClassNotFoundException Thrown if the class or interface referenced by this type can't be found
146     */
147    public boolean referencesInterfaceExact() throws ClassNotFoundException {
148        final JavaClass jc = Repository.lookupClass(className);
149        return !jc.isClass();
150    }
151
152    /**
153     * Return true if this type is a subclass of given ObjectType.
154     *
155     * @param superclass The superclass to check against.
156     * @return true if this is a subclass.
157     * @throws ClassNotFoundException Thrown if any of this class's superclasses can't be found.
158     */
159    public boolean subclassOf(final ObjectType superclass) throws ClassNotFoundException {
160        if (referencesInterfaceExact() || superclass.referencesInterfaceExact()) {
161            return false;
162        }
163        return Repository.instanceOf(this.className, superclass.className);
164    }
165}