View Javadoc
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 }