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.File;
034: import java.util.*;
035:
036: /**
037: * @author Taras Puchko
038: */
039: public class Retrotranslator {
040:
041: private LinkedList<FileContainer> src = new LinkedList<FileContainer>();
042: private FileContainer dest;
043: private boolean stripsign;
044: private boolean stripannot;
045: private boolean retainapi;
046: private boolean retainflags;
047: private boolean verbose;
048: private boolean lazy;
049: private boolean advanced;
050: private boolean verify;
051: private boolean uptodatecheck;
052: private boolean smart;
053: private ReflectionMode reflectionMode = ReflectionMode.NORMAL;
054: private List<File> classpath = new ArrayList<File>();
055: private MessageLogger logger;
056: private SourceMask sourceMask = new SourceMask(null);
057: private String embed;
058: private String support;
059: private String backport;
060: private ClassVersion target = ClassVersion.VERSION_14;
061: private ClassLoader classLoader;
062:
063: public Retrotranslator() {
064: }
065:
066: public void addSrcdir(File srcdir) {
067: if (!srcdir.isDirectory())
068: throw new IllegalArgumentException("Invalid srcdir: "
069: + srcdir);
070: src.add(new FolderFileContainer(srcdir));
071: }
072:
073: public void addSrcjar(File srcjar) {
074: if (!srcjar.isFile())
075: throw new IllegalArgumentException("Invalid srcjar: "
076: + srcjar);
077: src.add(new JarFileContainer(srcjar));
078: }
079:
080: public void addSourceFiles(File srcdir, List<String> fileNames) {
081: if (!srcdir.isDirectory())
082: throw new IllegalArgumentException("Invalid srcdir: "
083: + srcdir);
084: src.add(new FolderFileContainer(srcdir, fileNames));
085: }
086:
087: public void setDestdir(File destdir) {
088: if (!destdir.isDirectory())
089: throw new IllegalArgumentException("Invalid destdir: "
090: + destdir);
091: if (dest != null)
092: throw new IllegalArgumentException(
093: "Destination already set.");
094: dest = new FolderFileContainer(destdir);
095: }
096:
097: public void setDestjar(File destjar) {
098: if (destjar.isDirectory())
099: throw new IllegalArgumentException("Invalid destjar: "
100: + destjar);
101: if (dest != null)
102: throw new IllegalArgumentException(
103: "Destination already set.");
104: dest = new JarFileContainer(destjar);
105: }
106:
107: public void setStripsign(boolean stripsign) {
108: this .stripsign = stripsign;
109: }
110:
111: public void setStripannot(boolean stripannot) {
112: this .stripannot = stripannot;
113: }
114:
115: public void setRetainapi(boolean retainapi) {
116: this .retainapi = retainapi;
117: }
118:
119: public void setRetainflags(boolean retainflags) {
120: this .retainflags = retainflags;
121: }
122:
123: public void setVerbose(boolean verbose) {
124: this .verbose = verbose;
125: }
126:
127: public void setLazy(boolean lazy) {
128: this .lazy = lazy;
129: }
130:
131: public void setAdvanced(boolean advanced) {
132: this .advanced = advanced;
133: }
134:
135: public void setVerify(boolean verify) {
136: this .verify = verify;
137: }
138:
139: public void setUptodatecheck(boolean uptodatecheck) {
140: this .uptodatecheck = uptodatecheck;
141: }
142:
143: public void setSmart(boolean smart) {
144: this .smart = smart;
145: }
146:
147: public void addClasspathElement(File classpathElement) {
148: this .classpath.add(classpathElement);
149: }
150:
151: public void addClasspath(String classpath) {
152: StringTokenizer tokenizer = new StringTokenizer(classpath,
153: File.pathSeparator);
154: while (tokenizer.hasMoreTokens()) {
155: addClasspathElement(new File(tokenizer.nextToken()));
156: }
157: }
158:
159: public void setSrcmask(String srcmask) {
160: sourceMask = new SourceMask(srcmask);
161: }
162:
163: public void setReflection(String reflection) {
164: reflectionMode = ReflectionMode.valueOf(reflection);
165: }
166:
167: public void setEmbed(String embed) {
168: this .embed = embed;
169: }
170:
171: public void setSupport(String support) {
172: this .support = support;
173: }
174:
175: public void setBackport(String backport) {
176: this .backport = backport;
177: }
178:
179: public void setTarget(String target) {
180: this .target = ClassVersion.valueOf(target);
181: }
182:
183: public void setLogger(MessageLogger logger) {
184: this .logger = logger;
185: }
186:
187: public void setClassLoader(ClassLoader classLoader) {
188: this .classLoader = classLoader;
189: }
190:
191: public boolean run() {
192: if (src.isEmpty())
193: throw new IllegalArgumentException("No files to translate.");
194: SystemLogger systemLogger = new SystemLogger(
195: getMessageLogger(), verbose);
196: TargetEnvironment environment = createEnvironment(null,
197: systemLogger);
198: EmbeddingConverter converter = null;
199: if (embed != null) {
200: if (lazy) {
201: throw new IllegalArgumentException(
202: "Embedding cannot be lazy.");
203: }
204: converter = new EmbeddingConverter(target, embed,
205: environment, systemLogger);
206: }
207: OperationMode mode = new OperationMode(advanced, support,
208: smart, target);
209: ReplacementLocatorFactory factory = new ReplacementLocatorFactory(
210: mode, retainapi, backport, environment);
211: ClassTransformer classTransformer = new ClassTransformer(lazy,
212: stripsign, stripannot, retainflags, reflectionMode,
213: systemLogger, converter, factory);
214: TextFileTransformer fileTransformer = new TextFileTransformer(
215: factory, converter);
216: FileTranslator translator = new FileTranslator(
217: classTransformer, fileTransformer, converter,
218: systemLogger, sourceMask, uptodatecheck, mode);
219: boolean modified = false;
220: for (FileContainer container : src) {
221: modified |= translator.transform(container,
222: dest != null ? dest : container);
223: }
224: if (converter != null && dest != null) {
225: converter.embed(dest, classTransformer);
226: }
227: if (dest != null) {
228: dest.flush(systemLogger);
229: }
230: if (!verify) {
231: return systemLogger.isReliable();
232: }
233: if (!modified) {
234: logger.log(new Message(Level.INFO,
235: "Skipped verification of up-to-date file(s)."));
236: return systemLogger.isReliable();
237: }
238: verify(systemLogger);
239: return systemLogger.isReliable();
240: }
241:
242: private TargetEnvironment createEnvironment(
243: FileContainer destination, SystemLogger logger) {
244: ClassLoader loader = classLoader;
245: if (loader == null && classpath.isEmpty()) {
246: loader = TransformerTools.getDefaultClassLoader();
247: }
248: TargetEnvironment environment = new TargetEnvironment(loader,
249: logger, false);
250: if (destination != null) {
251: environment.appendPath(destination.getLocation());
252: } else {
253: for (FileContainer container : src) {
254: environment.appendPath(container.getLocation());
255: }
256: }
257: for (File file : classpath) {
258: environment.appendPath(file);
259: }
260: return environment;
261: }
262:
263: private MessageLogger getMessageLogger() {
264: if (logger != null) {
265: return logger;
266: }
267: return new AbstractLogger() {
268: protected void log(String text, Level level) {
269: System.out.println(text);
270: }
271: };
272: }
273:
274: private void verify(SystemLogger systemLogger) {
275: TargetEnvironment environment = createEnvironment(dest,
276: systemLogger);
277: try {
278: if (dest != null) {
279: verify(environment, dest, systemLogger);
280: } else {
281: for (FileContainer container : src) {
282: verify(environment, container, systemLogger);
283: }
284: }
285: } finally {
286: environment.close();
287: }
288: }
289:
290: private void verify(TargetEnvironment environment,
291: FileContainer container, SystemLogger systemLogger) {
292: systemLogger.log(new Message(Level.INFO, "Verifying "
293: + container.getFileCount() + " file(s) in " + container
294: + "."));
295: int warningCount = 0;
296: int fileCount = 0;
297: for (final FileEntry entry : container.getEntries()) {
298: if (sourceMask.matches(entry.getName())
299: && (!lazy || entry.isModified())) {
300: byte[] content = entry.getContent();
301: if (TransformerTools.isClassFile(content)) {
302: systemLogger.setFile(container.getLocation(), entry
303: .getName());
304: systemLogger.logForFile(Level.VERBOSE,
305: "Verification");
306: fileCount++;
307: warningCount += new ReferenceVerifyingVisitor(
308: target, environment, systemLogger)
309: .verify(content);
310: systemLogger.setFile(null, null);
311: }
312: }
313: }
314: systemLogger.log(new Message(Level.INFO, "Verified "
315: + fileCount
316: + " file(s)"
317: + (warningCount == 0 ? "." : " with " + warningCount
318: + " warning(s).")));
319: }
320:
321: private boolean execute(String[] args) {
322: int i = 0;
323: while (i < args.length) {
324: String string = args[i++];
325: if (string.equals("-srcdir") && i < args.length) {
326: addSrcdir(new File(args[i++]));
327: } else if (string.equals("-srcjar") && i < args.length) {
328: addSrcjar(new File(args[i++]));
329: } else if (string.equals("-destdir") && i < args.length) {
330: setDestdir(new File(args[i++]));
331: } else if (string.equals("-destjar") && i < args.length) {
332: setDestjar(new File(args[i++]));
333: } else if (string.equals("-stripsign")) {
334: setStripsign(true);
335: } else if (string.equals("-stripannot")) {
336: setStripannot(true);
337: } else if (string.equals("-retainapi")) {
338: setRetainapi(true);
339: } else if (string.equals("-retainflags")) {
340: setRetainflags(true);
341: } else if (string.equals("-verbose")) {
342: setVerbose(true);
343: } else if (string.equals("-lazy")) {
344: setLazy(true);
345: } else if (string.equals("-advanced")) {
346: setAdvanced(true);
347: } else if (string.equals("-verify")) {
348: setVerify(true);
349: } else if (string.equals("-uptodatecheck")) {
350: setUptodatecheck(true);
351: } else if (string.equals("-smart")) {
352: setSmart(true);
353: } else if (string.equals("-classpath") && i < args.length) {
354: addClasspath(args[i++]);
355: } else if (string.equals("-srcmask") && i < args.length) {
356: setSrcmask(args[i++]);
357: } else if (string.equals("-embed") && i < args.length) {
358: setEmbed(args[i++]);
359: } else if (string.equals("-support") && i < args.length) {
360: setSupport(args[i++]);
361: } else if (string.equals("-backport") && i < args.length) {
362: setBackport(args[i++]);
363: } else if (string.equals("-target") && i < args.length) {
364: setTarget(args[i++]);
365: } else if (string.equals("-reflection") && i < args.length) {
366: setReflection(args[i++]);
367: } else {
368: throw new IllegalArgumentException("Unknown option: "
369: + string);
370: }
371: }
372: return run();
373: }
374:
375: private static void printUsage() {
376: String version = Retrotranslator.class.getPackage()
377: .getImplementationVersion();
378: String suffix = (version == null) ? "" : "-" + version;
379: System.out
380: .println("Usage: java -jar retrotranslator-transformer"
381: + suffix
382: + ".jar [-srcdir <path> | -srcjar <file>]"
383: + " [-destdir <path> | -destjar <file>] [-support <features>] [-lazy] [-reflection <mode>] [-stripannot]"
384: + " [-stripsign] [-advanced] [-retainapi] [-retainflags] [-verify] [-uptodatecheck] [-target <version>]"
385: + " [-classpath <path>] [-srcmask <mask>] [-embed <package>] [-backport <packages>] [-verbose] [-smart]");
386: }
387:
388: public static void main(String[] args) {
389: if (args.length == 0) {
390: printUsage();
391: return;
392: }
393: try {
394: if (!new Retrotranslator().execute(args)) {
395: System.exit(2);
396: }
397: } catch (IllegalArgumentException e) {
398: System.out.println(e.getMessage());
399: printUsage();
400: System.exit(1);
401: }
402: }
403:
404: }
|