OSDN Git Service

Merge remote branch 'origin/HEAD'
[stigmata/digger.git] / src / main / java / jp / sourceforge / stigmata / digger / ClasspathContext.java
1 package jp.sourceforge.stigmata.digger;
2
3 import java.net.URL;
4 import java.util.ArrayList;
5 import java.util.Iterator;
6 import java.util.List;
7
8 import jp.sourceforge.stigmata.digger.util.WarClassLoader;
9
10 /**
11  * Context object of Classpath.
12  * 
13  * @author Haruaki TAMADA
14  */
15 public class ClasspathContext implements Iterable<URL>{
16     private ClasspathContext parent;
17     private List<URL> classpath = new ArrayList<URL>();
18     private ClassLoader loader = null;
19     private boolean includeSystemClass = true;
20
21     public ClasspathContext(){
22     }
23
24     /**
25      * constructor with parent classpath context.
26      */
27     public ClasspathContext(ClasspathContext parent){
28         this.parent = parent;
29         includeSystemClass = getParent().isIncludeSystemClasses();
30     }
31
32     /**
33      * returns parent classpath context.
34      */
35     public ClasspathContext getParent(){
36         return parent;
37     }
38
39     /**
40      * If this method returns true, this object searches byte code 
41      * by original ClassLoader and current ClassLoader.
42      * If this method returns false, this object searches byte code
43      * by original ClassLoader only. Not search from current ClassLoader. 
44      */
45     public boolean isIncludeSystemClasses(){
46         return includeSystemClass;
47     }
48
49     /**
50      * Set searching byte code by current ClassLoader.
51      * @see isIncludeSystemClasses
52      */
53     public synchronized void setIncludeSystemClasses(boolean flag){
54         if(includeSystemClass != flag){
55             loader = null;
56         }
57         this.includeSystemClass = flag;
58         if(parent != null){
59             parent.setIncludeSystemClasses(flag);
60         }
61     }
62
63     /**
64      * adds given url to this context.  If this context already has given url or
65      * parent context has given url, this method do nothing.
66      */
67     public synchronized void addClasspath(URL url){
68         if(!contains(url)){
69             classpath.add(url);
70             loader = null;
71         }
72     }
73
74     /**
75      * returns that this context or parent context have given url.
76      */
77     public synchronized boolean contains(URL url){
78         return (parent != null && parent.contains(url)) || classpath.contains(url); 
79     }
80
81     /**
82      * returns a size of classpath list, which this context and parent context have.
83      */
84     public synchronized int getClasspathSize(){
85         int count = classpath.size();
86         if(parent != null){
87             count += parent.getClasspathSize();
88         }
89         return count;
90     }
91
92     /**
93      * returns an array of all of classpathes include parent context.
94      */
95     public synchronized URL[] getClasspathList(){
96         List<URL> list = new ArrayList<URL>();
97         for(URL url: this){
98             list.add(url);
99         }
100         return list.toArray(new URL[list.size()]);
101     }
102
103     /**
104      * clears all of classpathes of this context. not clear parent context.
105      * If you want to clear this context and parent context, use {@link #clearAll <code>clearAll</code>} method.
106      * @see clearAll
107      */
108     public synchronized void clear(){
109         classpath.clear();
110     }
111
112     /**
113      * clears all of classpathes of this context and parent context.
114      */
115     public synchronized void clearAll(){
116         clear();
117         if(parent != null){
118             parent.clearAll();
119         }
120     }
121
122     /**
123      * returns an iterator of classpath list.
124      */
125     public synchronized Iterator<URL> iterator(){
126         if(parent == null){
127             return classpath.iterator();
128         }
129         else{
130             final Iterator<URL> parentIterator = parent.iterator();
131             final Iterator<URL> thisIterator = classpath.iterator();
132             return new Iterator<URL>(){
133                 public boolean hasNext(){
134                     boolean next = parentIterator.hasNext();
135                     if(!next){
136                         next = thisIterator.hasNext();
137                     }
138                     return next;
139                 }
140                 public URL next(){
141                     URL nextObject = null;
142                     if(parentIterator.hasNext()){
143                         nextObject = parentIterator.next();
144                     }
145                     else{
146                         nextObject = thisIterator.next();
147                     }
148                     return nextObject;
149                 }
150                 public void remove(){
151                 }
152             };
153         }
154     }
155
156
157     /**
158      * construct and returns a ClassLoader object which loads from classpath list.
159      */
160     public synchronized ClassLoader createClassLoader(){
161         if(loader == null){
162             List<URL> list = new ArrayList<URL>();
163             for(URL url: this){
164                 list.add(url);
165             }
166
167             ClassLoader parentClassLoader = null;
168             if(parent != null){
169                 parentClassLoader = parent.createClassLoader();
170             }
171             else{
172                 if(isIncludeSystemClasses()){
173                      parentClassLoader = getClass().getClassLoader();
174                 }
175                 else{
176                     parentClassLoader = null;
177                 }
178             }
179             loader = new WarClassLoader(list.toArray(new URL[list.size()]), parentClassLoader);
180         }
181         return loader;
182     }
183
184     /**
185      * returns a {@link ClassFileEntry <code>ClassFileEntry</code>} object
186      * which is named given className.
187      * 
188      * @return ClassFileEntry object, if not found given class, then returns null.
189      */
190     public synchronized ClassFileEntry findEntry(String className){
191         ClassLoader loader = createClassLoader();
192
193         URL resource = loader.getResource(className.replace('.', '/') + ".class");
194
195         if(resource != null){
196             return new ClassFileEntry(className, resource);
197         }
198         return null;
199     }
200
201     /**
202      * returns this context has given class entry or not.
203      */
204     public synchronized boolean hasEntry(String className){
205         ClassLoader loader = createClassLoader();
206         return loader.getResource(className.replace('.', '/') + ".class") != null;
207     }
208
209     /**
210      * finds and returns a {@link Class <code>Class</code>} object
211      * which is named given className.
212      */
213     public synchronized Class<?> findClass(String className) throws ClassNotFoundException{
214         try{
215             ClassLoader loader = createClassLoader();
216
217             Class<?> clazz = loader.loadClass(className);
218
219             return clazz;
220         } catch(NoClassDefFoundError e){
221             throw new ClassNotFoundException(e.getMessage(), e);
222         }
223     }
224 }