Skip to contentMethod: getClassFor(String)
1: /*
2: * Copyright (c) 2012, 2019 Oracle and/or its affiliates and others.
3: * All rights reserved.
4: *
5: * This program and the accompanying materials are made available under the
6: * terms of the Eclipse Public License v. 2.0, which is available at
7: * http://www.eclipse.org/legal/epl-2.0.
8: *
9: * This Source Code may also be made available under the following Secondary
10: * Licenses when the conditions for such availability set forth in the
11: * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
12: * version 2 with the GNU Classpath Exception, which is available at
13: * https://www.gnu.org/software/classpath/license.html.
14: *
15: * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
16: */
17:
18: package jakarta.el;
19:
20: import static java.lang.reflect.Modifier.isAbstract;
21: import static java.lang.reflect.Modifier.isInterface;
22: import static java.lang.reflect.Modifier.isPublic;
23:
24: import java.util.ArrayList;
25: import java.util.HashMap;
26: import java.util.HashSet;
27: import java.util.List;
28: import java.util.Map;
29:
30: /**
31: * Handles imports of class names and package names. An imported package name implicitly imports all the classes in the
32: * package. A class that has been imported can be used without its package name. The name is resolved to its full
33: * (package and class) name at evaluation time.
34: */
35: public class ImportHandler {
36:
37: private Map<String, String> classNameMap = new HashMap<>();
38: private Map<String, Class<?>> classMap = new HashMap<>();
39: private Map<String, String> staticNameMap = new HashMap<>();
40: private HashSet<String> notAClass = new HashSet<>();
41: private List<String> packages = new ArrayList<>();
42:
43: {
44: importPackage("java.lang");
45: }
46:
47: /**
48: * Import a static field or method.
49: *
50: * @param name The static member name, including the full class name, to be imported
51: * @throws ELException if the name does not include a ".".
52: */
53: public void importStatic(String name) throws ELException {
54: int i = name.lastIndexOf('.');
55: if (i <= 0) {
56: throw new ELException("The name " + name + " is not a full static member name");
57: }
58:
59: String memberName = name.substring(i + 1);
60: String className = name.substring(0, i);
61:
62: staticNameMap.put(memberName, className);
63: }
64:
65: /**
66: * Import a class.
67: *
68: * @param name The full class name of the class to be imported
69: * @throws ELException if the name does not include a ".".
70: */
71: public void importClass(String name) throws ELException {
72: int i = name.lastIndexOf('.');
73: if (i <= 0) {
74: throw new ELException("The name " + name + " is not a full class name");
75: }
76:
77: String className = name.substring(i + 1);
78:
79: classNameMap.put(className, name);
80: }
81:
82: /**
83: * Import all the classes in a package.
84: *
85: * @param packageName The package name to be imported
86: */
87: public void importPackage(String packageName) {
88: packages.add(packageName);
89: }
90:
91: /**
92: * Resolve a class name.
93: *
94: * @param name The name of the class (without package name) to be resolved.
95: * @return If the class has been imported previously, with {@link #importClass} or {@link #importPackage}, then its
96: * Class instance. Otherwise <code>null</code>.
97: * @throws ELException if the class is abstract or is an interface, or not public.
98: */
99: public Class<?> resolveClass(String name) {
100: String className = classNameMap.get(name);
101: if (className != null) {
102: return resolveClassFor(className);
103: }
104:
105: for (String packageName : packages) {
106: String fullClassName = packageName + "." + name;
107: Class<?> c = resolveClassFor(fullClassName);
108: if (c != null) {
109: classNameMap.put(name, fullClassName);
110: return c;
111: }
112: }
113:
114: return null;
115: }
116:
117: /**
118: * Resolve a static field or method name.
119: *
120: * @param name The name of the member(without package and class name) to be resolved.
121: * @return If the field or method has been imported previously, with {@link #importStatic}, then the class object
122: * representing the class that declares the static field or method. Otherwise <code>null</code>.
123: * @throws ELException if the class is not public, or is abstract or is an interface.
124: */
125: public Class<?> resolveStatic(String name) {
126: String className = staticNameMap.get(name);
127: if (className != null) {
128: Class<?> c = resolveClassFor(className);
129: if (c != null) {
130: return c;
131: }
132: }
133:
134: return null;
135: }
136:
137: private Class<?> resolveClassFor(String className) {
138: Class<?> c = classMap.get(className);
139: if (c != null) {
140: return c;
141: }
142:
143: c = getClassFor(className);
144: if (c != null) {
145: checkModifiers(c.getModifiers());
146: classMap.put(className, c);
147: }
148:
149: return c;
150: }
151:
152: private Class<?> getClassFor(String className) {
153:• if (!notAClass.contains(className)) {
154: try {
155: return Class.forName(className, false, Thread.currentThread().getContextClassLoader());
156: // Some operating systems have case-insensitive path names. An example is Windows if className is
157: // attempting to be resolved from a wildcard import a java.lang.NoClassDefFoundError may be thrown as
158: // the expected case for the type likely doesn't match. See
159: // https://bugs.java.com/bugdatabase/view_bug.do?bug_id=8024775 and
160: // https://bugs.openjdk.java.net/browse/JDK-8133522.
161: } catch (ClassNotFoundException | NoClassDefFoundError ex) {
162: notAClass.add(className);
163: }
164: }
165:
166: return null;
167: }
168:
169: private void checkModifiers(int modifiers) {
170: if (isAbstract(modifiers) || isInterface(modifiers) || !isPublic((modifiers))) {
171: throw new ELException("Imported class must be public, and cannot be abstract or an interface");
172: }
173: }
174: }