1package junit.runner;
2
3import java.util.*;
4import java.io.*;
5import java.net.URL;
6import java.util.zip.*;
7
8/**
9 * A custom class loader which enables the reloading
10 * of classes for each test run. The class loader
11 * can be configured with a list of package paths that
12 * should be excluded from loading. The loading
13 * of these packages is delegated to the system class
14 * loader. They will be shared across test runs.
15 * <p>
16 * The list of excluded package paths is specified in
17 * a properties file "excluded.properties" that is located in
18 * the same place as the TestCaseClassLoader class.
19 * <p>
20 * <b>Known limitation:</b> the TestCaseClassLoader cannot load classes
21 * from jar files.
22 * {@hide} - Not needed for 1.0 SDK
23 */
24public class TestCaseClassLoader extends ClassLoader {
25	/** scanned class path */
26	private Vector fPathItems;
27	/** default excluded paths */
28	private String[] defaultExclusions= {
29		"junit.framework.",
30		"junit.extensions.",
31		"junit.runner."
32	};
33	/** name of excluded properties file */
34	static final String EXCLUDED_FILE= "excluded.properties";
35	/** excluded paths */
36	private Vector fExcluded;
37
38	/**
39	 * Constructs a TestCaseLoader. It scans the class path
40	 * and the excluded package paths
41	 */
42	public TestCaseClassLoader() {
43		this(System.getProperty("java.class.path"));
44	}
45
46	/**
47	 * Constructs a TestCaseLoader. It scans the class path
48	 * and the excluded package paths
49	 */
50	public TestCaseClassLoader(String classPath) {
51		scanPath(classPath);
52		readExcludedPackages();
53	}
54
55	private void scanPath(String classPath) {
56		String separator= System.getProperty("path.separator");
57		fPathItems= new Vector(10);
58		StringTokenizer st= new StringTokenizer(classPath, separator);
59		while (st.hasMoreTokens()) {
60			fPathItems.addElement(st.nextToken());
61		}
62	}
63
64	public URL getResource(String name) {
65		return ClassLoader.getSystemResource(name);
66	}
67
68	public InputStream getResourceAsStream(String name) {
69		return ClassLoader.getSystemResourceAsStream(name);
70	}
71
72	public boolean isExcluded(String name) {
73		for (int i= 0; i < fExcluded.size(); i++) {
74			if (name.startsWith((String) fExcluded.elementAt(i))) {
75				return true;
76			}
77		}
78		return false;
79	}
80
81	public synchronized Class loadClass(String name, boolean resolve)
82		throws ClassNotFoundException {
83
84		Class c= findLoadedClass(name);
85		if (c != null)
86			return c;
87		//
88		// Delegate the loading of excluded classes to the
89		// standard class loader.
90		//
91		if (isExcluded(name)) {
92			try {
93				c= findSystemClass(name);
94				return c;
95			} catch (ClassNotFoundException e) {
96				// keep searching
97			}
98		}
99		if (c == null) {
100			byte[] data= lookupClassData(name);
101			if (data == null)
102				throw new ClassNotFoundException();
103			c= defineClass(name, data, 0, data.length);
104		}
105		if (resolve)
106			resolveClass(c);
107		return c;
108	}
109
110	private byte[] lookupClassData(String className) throws ClassNotFoundException {
111		byte[] data= null;
112		for (int i= 0; i < fPathItems.size(); i++) {
113			String path= (String) fPathItems.elementAt(i);
114			String fileName= className.replace('.', '/')+".class";
115			if (isJar(path)) {
116				data= loadJarData(path, fileName);
117			} else {
118				data= loadFileData(path, fileName);
119			}
120			if (data != null)
121				return data;
122		}
123		throw new ClassNotFoundException(className);
124	}
125
126	boolean isJar(String pathEntry) {
127		return pathEntry.endsWith(".jar") ||
128		       pathEntry.endsWith(".apk") ||
129                       pathEntry.endsWith(".zip");
130	}
131
132	private byte[] loadFileData(String path, String fileName) {
133		File file= new File(path, fileName);
134		if (file.exists()) {
135			return getClassData(file);
136		}
137		return null;
138	}
139
140	private byte[] getClassData(File f) {
141		try {
142			FileInputStream stream= new FileInputStream(f);
143			ByteArrayOutputStream out= new ByteArrayOutputStream(1000);
144			byte[] b= new byte[1000];
145			int n;
146			while ((n= stream.read(b)) != -1)
147				out.write(b, 0, n);
148			stream.close();
149			out.close();
150			return out.toByteArray();
151
152		} catch (IOException e) {
153		}
154		return null;
155	}
156
157	private byte[] loadJarData(String path, String fileName) {
158		ZipFile zipFile= null;
159		InputStream stream= null;
160		File archive= new File(path);
161		if (!archive.exists())
162			return null;
163		try {
164			zipFile= new ZipFile(archive);
165		} catch(IOException io) {
166			return null;
167		}
168		ZipEntry entry= zipFile.getEntry(fileName);
169		if (entry == null)
170			return null;
171		int size= (int) entry.getSize();
172		try {
173			stream= zipFile.getInputStream(entry);
174			byte[] data= new byte[size];
175			int pos= 0;
176			while (pos < size) {
177				int n= stream.read(data, pos, data.length - pos);
178				pos += n;
179			}
180			zipFile.close();
181			return data;
182		} catch (IOException e) {
183		} finally {
184			try {
185				if (stream != null)
186					stream.close();
187			} catch (IOException e) {
188			}
189		}
190		return null;
191	}
192
193	private void readExcludedPackages() {
194		fExcluded= new Vector(10);
195		for (int i= 0; i < defaultExclusions.length; i++)
196			fExcluded.addElement(defaultExclusions[i]);
197
198		InputStream is= getClass().getResourceAsStream(EXCLUDED_FILE);
199		if (is == null)
200			return;
201		Properties p= new Properties();
202		try {
203			p.load(is);
204		}
205		catch (IOException e) {
206			return;
207		} finally {
208			try {
209				is.close();
210			} catch (IOException e) {
211			}
212		}
213		for (Enumeration e= p.propertyNames(); e.hasMoreElements(); ) {
214			String key= (String)e.nextElement();
215			if (key.startsWith("excluded.")) {
216				String path= p.getProperty(key);
217				path= path.trim();
218				if (path.endsWith("*"))
219					path= path.substring(0, path.length()-1);
220				if (path.length() > 0)
221					fExcluded.addElement(path);
222			}
223		}
224	}
225}
226