001: /***
002: * Retrotranslator: a Java bytecode transformer that translates Java classes
003: * compiled with JDK 5.0 into classes that can be run on JVM 1.4.
004: *
005: * Copyright (c) 2005 - 2008 Taras Puchko
006: * All rights reserved.
007: *
008: * Redistribution and use in source and binary forms, with or without
009: * modification, are permitted provided that the following conditions
010: * are met:
011: * 1. Redistributions of source code must retain the above copyright
012: * notice, this list of conditions and the following disclaimer.
013: * 2. Redistributions in binary form must reproduce the above copyright
014: * notice, this list of conditions and the following disclaimer in the
015: * documentation and/or other materials provided with the distribution.
016: * 3. Neither the name of the copyright holders nor the names of its
017: * contributors may be used to endorse or promote products derived from
018: * this software without specific prior written permission.
019: *
020: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
021: * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
022: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
023: * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
024: * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
025: * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
026: * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
027: * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
028: * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
029: * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
030: * THE POSSIBILITY OF SUCH DAMAGE.
031: */package net.sf.retrotranslator.transformer;
032:
033: import java.io.*;
034: import java.lang.ref.SoftReference;
035: import java.util.*;
036: import java.util.zip.*;
037: import java.net.URL;
038: import net.sf.retrotranslator.runtime.asm.ClassReader;
039: import net.sf.retrotranslator.runtime.impl.*;
040:
041: /**
042: * @author Taras Puchko
043: */
044: class TargetEnvironment {
045:
046: private static byte[] NO_CONTENT = new byte[0];
047:
048: private final ClassLoader classLoader;
049: private final SystemLogger logger;
050: private final boolean contextual;
051: private final List<Entry> entries = new Vector<Entry>();
052: private SoftReference<Map<String, byte[]>> cacheReference;
053:
054: public TargetEnvironment(ClassLoader classLoader,
055: SystemLogger logger, boolean contextual) {
056: this .classLoader = classLoader;
057: this .logger = logger;
058: this .contextual = contextual;
059: }
060:
061: public void appendPath(File element) {
062: if (!element.exists()) {
063: throw new RuntimeException(element.getPath()
064: + " not found.");
065: }
066: entries.add(element.isDirectory() ? new DirectoryEntry(element)
067: : new ZipFileEntry(element));
068: }
069:
070: public ClassReader getClassReader(String name)
071: throws ClassNotFoundException {
072: byte[] content = getClassContent(name);
073: if (content == null) {
074: throw new ClassNotFoundException(name);
075: }
076: return new ClassReader(content);
077: }
078:
079: public ClassReader findClassReader(String className) {
080: try {
081: return getClassReader(className);
082: } catch (ClassNotFoundException e) {
083: logger.logForFile(Level.VERBOSE, "Cannot find "
084: + RuntimeTools.getDisplayClassName(className));
085: return null;
086: }
087: }
088:
089: public byte[] getClassContent(String name) {
090: Map<String, byte[]> cache = getCache();
091: byte[] content = cache.get(name);
092: if (content != null) {
093: return content != NO_CONTENT ? content : null;
094: }
095: List<byte[]> resources = getResources(name
096: + RuntimeTools.CLASS_EXTENSION, 1);
097: content = resources.isEmpty() ? null : resources.get(0);
098: cache.put(name, content != null ? content : NO_CONTENT);
099: return content;
100: }
101:
102: private synchronized Map<String, byte[]> getCache() {
103: Map<String, byte[]> cache = cacheReference == null ? null
104: : cacheReference.get();
105: if (cache == null) {
106: cache = new Hashtable<String, byte[]>();
107: cacheReference = new SoftReference<Map<String, byte[]>>(
108: cache);
109: }
110: return cache;
111: }
112:
113: public void close() {
114: for (Entry entry : entries) {
115: try {
116: entry.close();
117: } catch (Exception e) {
118: e.printStackTrace();
119: }
120: }
121: }
122:
123: public Collection<String> readRegistry(String name,
124: ClassVersion target) {
125: try {
126: LinkedHashSet<String> result = new LinkedHashSet<String>();
127: String resourceName = "net/sf/retrotranslator/registry/"
128: + name + target.getName().replace(".", "")
129: + ".properties";
130: for (byte[] resource : getResources(resourceName,
131: Integer.MAX_VALUE)) {
132: String content = new String(resource, "UTF-8");
133: BufferedReader reader = new BufferedReader(
134: new StringReader(content));
135: String line;
136: while ((line = reader.readLine()) != null) {
137: line = line.trim();
138: if (line.length() > 0) {
139: result.add(line);
140: }
141: }
142: }
143: return result;
144: } catch (IOException e) {
145: throw new RuntimeException(e);
146: }
147: }
148:
149: private List<byte[]> getResources(final String name,
150: final int maxCount) {
151: final List<byte[]> result = new ArrayList<byte[]>();
152:
153: class ResourceLoader {
154: boolean addResource(InputStream stream) {
155: if (stream == null) {
156: return false;
157: }
158: result.add(RuntimeTools.readAndClose(stream));
159: return result.size() >= maxCount;
160: }
161:
162: boolean addResources(ClassLoader loader) {
163: if (loader == null) {
164: return false;
165: }
166: try {
167: Enumeration<URL> resources = loader
168: .getResources(name);
169: while (resources.hasMoreElements()) {
170: URL url = resources.nextElement();
171: if (addResource(url.openStream())) {
172: return true;
173: }
174: }
175: } catch (IOException e) {
176: throw new RuntimeException(e);
177: }
178: return false;
179: }
180: }
181:
182: ResourceLoader loader = new ResourceLoader();
183: for (Entry entry : entries) {
184: InputStream stream = entry.getResourceAsStream(name);
185: if (loader.addResource(stream)) {
186: return result;
187: }
188: }
189: if (loader.addResources(classLoader)) {
190: return result;
191: }
192: if (contextual) {
193: loader.addResources(Thread.currentThread()
194: .getContextClassLoader());
195: }
196: return result;
197: }
198:
199: private static interface Entry {
200: InputStream getResourceAsStream(String name);
201:
202: void close();
203: }
204:
205: private static class DirectoryEntry implements Entry {
206: private File directory;
207:
208: public DirectoryEntry(File directory) {
209: this .directory = directory;
210: }
211:
212: public InputStream getResourceAsStream(String name) {
213: try {
214: return new FileInputStream(new File(directory, name));
215: } catch (FileNotFoundException e) {
216: return null;
217: }
218: }
219:
220: public void close() {
221: }
222: }
223:
224: private static class ZipFileEntry implements Entry {
225: private final File file;
226: private ZipFile zipFile;
227:
228: public ZipFileEntry(File file) {
229: this .file = file;
230: }
231:
232: public InputStream getResourceAsStream(String name) {
233: if (zipFile == null) {
234: openZipFile();
235: }
236: try {
237: ZipEntry entry = zipFile.getEntry(name);
238: return entry == null ? null : zipFile
239: .getInputStream(entry);
240: } catch (IOException e) {
241: throw new RuntimeException(e);
242: }
243: }
244:
245: private void openZipFile() {
246: try {
247: zipFile = new ZipFile(file);
248: } catch (IOException e) {
249: throw new RuntimeException("Cannot open zip file: "
250: + file.getAbsolutePath(), e);
251: }
252: }
253:
254: public void close() {
255: try {
256: if (zipFile != null) {
257: zipFile.close();
258: }
259: } catch (IOException e) {
260: throw new RuntimeException(e);
261: }
262: }
263: }
264:
265: }
|