1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * https://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19 package org.apache.bcel.util;
20
21 import java.io.IOException;
22 import java.io.InputStream;
23
24 import org.apache.bcel.classfile.ClassParser;
25 import org.apache.bcel.classfile.JavaClass;
26 import org.apache.bcel.classfile.Utility;
27
28 /**
29 * This abstract class provides a logic of a loading {@link JavaClass} objects class names via {@link ClassPath}.
30 *
31 * <p>
32 * Subclasses can choose caching strategy of the objects by implementing the abstract methods (for example,
33 * {@link #storeClass(JavaClass)} and {@link #findClass(String)}).
34 * </p>
35 *
36 * @since 6.4.0
37 */
38 abstract class AbstractClassPathRepository implements Repository {
39
40 private final ClassPath classPath;
41
42 AbstractClassPathRepository(final ClassPath classPath) {
43 this.classPath = classPath;
44 }
45
46 @Override
47 public abstract void clear();
48
49 @Override
50 public abstract JavaClass findClass(String className);
51
52 @Override
53 public ClassPath getClassPath() {
54 return classPath;
55 }
56
57 /**
58 * Finds the JavaClass object for a runtime Class object. If a class with the same name is already in this Repository,
59 * the Repository version is returned. Otherwise, getResourceAsStream() is called on the Class object to find the
60 * class's representation. If the representation is found, it is added to the Repository.
61 *
62 * @see Class
63 * @param clazz the runtime Class object
64 * @return JavaClass object for given runtime class
65 * @throws ClassNotFoundException if the class is not in the Repository, and its representation could not be found
66 */
67 @Override
68 public JavaClass loadClass(final Class<?> clazz) throws ClassNotFoundException {
69 final String className = clazz.getName();
70 final JavaClass repositoryClass = findClass(className);
71 if (repositoryClass != null) {
72 return repositoryClass;
73 }
74 String name = className;
75 final int i = name.lastIndexOf('.');
76 if (i > 0) {
77 name = name.substring(i + 1);
78 }
79
80 try (InputStream clsStream = clazz.getResourceAsStream(name + JavaClass.EXTENSION)) {
81 return loadClass(clsStream, className);
82 } catch (final IOException e) {
83 return null;
84 }
85 }
86
87 private JavaClass loadClass(final InputStream inputStream, final String className) throws ClassNotFoundException {
88 try {
89 if (inputStream != null) {
90 final ClassParser parser = new ClassParser(inputStream, className);
91 final JavaClass clazz = parser.parse();
92 storeClass(clazz);
93 return clazz;
94 }
95 } catch (final IOException e) {
96 throw new ClassNotFoundException("Exception while looking for class " + className + ": " + e, e);
97 }
98 throw new ClassNotFoundException("ClassRepository could not load " + className);
99 }
100
101 /**
102 * Finds a JavaClass object by name. If it is already in this Repository, the Repository version is returned. Otherwise,
103 * the Repository's classpath is searched for the class (and it is added to the Repository if found).
104 *
105 * @param className the name of the class
106 * @return the JavaClass object
107 * @throws ClassNotFoundException if the class is not in the Repository, and could not be found on the classpath
108 */
109 @Override
110 public JavaClass loadClass(String className) throws ClassNotFoundException {
111 if (className == null || className.isEmpty()) {
112 throw new IllegalArgumentException("Invalid class name " + className);
113 }
114 className = Utility.pathToPackage(className); // Just in case, canonical form
115 final JavaClass clazz = findClass(className);
116 if (clazz != null) {
117 return clazz;
118 }
119 try (InputStream inputStream = classPath.getInputStream(className)) {
120 return loadClass(inputStream, className);
121 } catch (final IOException e) {
122 throw new ClassNotFoundException("Exception while looking for class " + className + ": " + e, e);
123 }
124 }
125
126 @Override
127 public abstract void removeClass(JavaClass javaClass);
128
129 @Override
130 public abstract void storeClass(JavaClass javaClass);
131 }