001: /**
002: * Copyright (C) 2001-2004 France Telecom R&D
003: *
004: * This library is free software; you can redistribute it and/or
005: * modify it under the terms of the GNU Lesser General Public
006: * License as published by the Free Software Foundation; either
007: * version 2 of the License, or (at your option) any later version.
008: *
009: * This library is distributed in the hope that it will be useful,
010: * but WITHOUT ANY WARRANTY; without even the implied warranty of
011: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
012: * Lesser General Public License for more details.
013: *
014: * You should have received a copy of the GNU Lesser General Public
015: * License along with this library; if not, write to the Free Software
016: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
017: */package org.objectweb.speedo.genclass.merger;
018:
019: import org.apache.tools.ant.BuildException;
020: import org.apache.tools.ant.taskdefs.MatchingTask;
021: import org.objectweb.asm.ClassReader;
022: import org.objectweb.asm.ClassVisitor;
023: import org.objectweb.asm.ClassWriter;
024: import org.objectweb.jorm.util.lib.StringReplace;
025: import org.objectweb.speedo.api.ExceptionHelper;
026: import org.objectweb.speedo.api.SpeedoException;
027: import org.objectweb.speedo.api.SpeedoProperties;
028: import org.objectweb.speedo.genclass.GenClass;
029: import org.objectweb.speedo.generation.enhancer.common.DuplicatedMethodVerifier;
030: import org.objectweb.speedo.generation.enhancer.common.InterfaceAgregatorVisitor;
031: import org.objectweb.speedo.lib.Personality;
032: import org.objectweb.util.monolog.Monolog;
033: import org.objectweb.util.monolog.api.BasicLevel;
034: import org.objectweb.util.monolog.api.Logger;
035:
036: import java.io.File;
037: import java.io.FileInputStream;
038: import java.io.FileOutputStream;
039: import java.io.IOException;
040:
041: /**
042: *
043: * @author S.Chassande-Barrioz
044: */
045: public class GenClassMerger extends MatchingTask {
046: public final static String LOGGER_NAME = SpeedoProperties.LOGGER_NAME
047: + ".genclassmerger";
048: public final static String GEN_CLASS_NAME = GenClass.class
049: .getName().replace('.', '/');
050: private File src = null;
051: protected Logger logger = null;
052: protected Personality personality;
053:
054: public GenClassMerger() {
055: personality = null;
056: }
057:
058: public GenClassMerger(Personality p) {
059: personality = p;
060: }
061:
062: public File getSrc() {
063: return src;
064: }
065:
066: public void setSrc(File src) {
067: this .src = src;
068: }
069:
070: protected String getLoggerName() {
071: return LOGGER_NAME;
072: }
073:
074: public void execute() throws BuildException {
075: if (logger == null) {
076: logger = Monolog.initialize().getLogger(getLoggerName());
077: }
078: String[] pdFiles = super .getDirectoryScanner(src)
079: .getIncludedFiles();
080: for (int i = 0; i < pdFiles.length; i++) {
081: try {
082: mergeGenClass(newGCInfo(pdFiles[i]));
083: } catch (SpeedoException e) {
084: String msg = "Error while merging the file "
085: + pdFiles[i] + " merged";
086: Exception ie = ExceptionHelper.getNested(e);
087: throw new BuildException(msg, ie);
088: } catch (RuntimeException e) {
089: logger.log(BasicLevel.ERROR, e.getMessage(), e);
090: throw new BuildException("Error", e);
091: }
092: }
093: }
094:
095: public void mergeGenClass(GCInfo gc) throws SpeedoException {
096: logger.log(BasicLevel.DEBUG, "Treat the class "
097: + gc.classToWrite);
098: ClassReader cr = loadJavaClass(gc.classToWrite, false);
099: if (cr != null) {
100: //The file exists ==> analyze it.
101: logger.log(BasicLevel.DEBUG, "Analyze the class "
102: + gc.classToWrite);
103: GenClassAnalyzer gcInfo = new GenClassAnalyzer(logger, gc);
104: cr.accept(gcInfo, false);
105: if (!requireEnhancement(gc)) {
106: return;
107: }
108: }
109: logger.log(BasicLevel.INFO, "Enhance the generic class "
110: + gc.classToWrite);
111: ClassWriter cw = new ClassWriter(false);
112: ClassVisitor current = cw;
113: current = new InterfaceAgregatorVisitor(current, logger,
114: gc.classToWrite, personality);
115: current = new DuplicatedMethodVerifier(current, logger,
116: personality);
117:
118: writeFirstClass(gc, current);
119: writeSecondClass(gc, current);
120: writeJavaClass(gc.classToWrite, cw);
121:
122: checkAfter(gc);
123: }
124:
125: protected void writeFirstClass(final GCInfo gc, ClassVisitor current)
126: throws SpeedoException {
127: //Write the code of the first class
128: GenClassAdapter gca = new GenClassAdapter(current, logger, gc,
129: personality);
130: loadJavaClass(gc.firstClass, false).accept(gca, false);
131: }
132:
133: protected void writeSecondClass(final GCInfo gc,
134: ClassVisitor current) throws SpeedoException {
135: //Write the code of the second class
136: GenClassAdapter gca = new GenClassAdapter(current, logger, gc,
137: personality);
138: gca.setVisitConstructor(false);
139: loadJavaClass(gc.secondClass, false).accept(gca, false);
140: }
141:
142: protected String getClassToWrite(String gcn) {
143: return StringReplace.replaceChar('\\', '/', gcn.substring(0,
144: gcn.length() - 6));
145: }
146:
147: protected String getFirstClass(String gcn) {
148: return getClassToWrite(gcn);
149: }
150:
151: protected String getSecondClass(String gcn) {
152: return GEN_CLASS_NAME;
153: }
154:
155: protected boolean requireEnhancement(GCInfo gc) {
156: if (!gc.isAbstract) {
157: //do nothing
158: logger.log(BasicLevel.DEBUG, "Class " + gc.gcn
159: + " already complete.");
160: return false;
161: }
162: return true;
163: }
164:
165: protected void checkAfter(GCInfo gc) throws SpeedoException {
166: //Test the class loading
167: logger.log(BasicLevel.DEBUG, "check after the class "
168: + gc.classToWrite);
169: String cn = StringReplace
170: .replaceChar('/', '.', gc.classToWrite);
171: cn = StringReplace.replaceChar('\\', '.', cn);
172: try {
173: Class.forName(cn);
174: } catch (Throwable e) {
175: String msg = "Merged class '" + gc.classToWrite
176: + "' cannot be loaded: " + e.getMessage();
177: logger.log(BasicLevel.ERROR, msg, e);
178: if (e instanceof Exception) {
179: throw new SpeedoException(msg, (Exception) e);
180: } else {
181: throw new SpeedoException(msg);
182: }
183: }
184: }
185:
186: /**
187: * Loads a specified class.
188: * @param fn is the file name of the .class to load. the file name is
189: * a relative patht to the 'src' directory.
190: * @param remove indicates if the .class must be removed
191: * @return the JavaClass loaded
192: * @exception SpeedoException if the file cannot be loaded
193: */
194: protected ClassReader loadJavaClass(final String fn,
195: final boolean remove) throws SpeedoException {
196: String filename = fn;
197: if (!filename.endsWith(".class")) {
198: filename = filename + ".class";
199: }
200: logger.log(BasicLevel.DEBUG, "Load the class " + filename);
201: try {
202: File f = new File(src, filename);
203: if (!f.exists()) {
204: return null;
205: }
206: FileInputStream fis = new FileInputStream(f);
207: ClassReader jclass = new ClassReader(fis);
208: fis.close();
209: if (remove) {
210: f.delete();
211: }
212: return jclass;
213: } catch (IOException e) {
214: throw new SpeedoException("Error during loading "
215: + filename, e);
216: }
217: }
218:
219: public GCInfo newGCInfo(String gcn) {
220: return new GCInfo(gcn, getFirstClass(gcn), getSecondClass(gcn),
221: getClassToWrite(gcn));
222: }
223:
224: /**
225: * Saves the new bytecode of the specified Java class under a specified base
226: * directory.
227: *
228: * @param jclass the Java class that has to be saved
229: * @exception SpeedoException if the file cannot be written
230: */
231: protected void writeJavaClass(final String fn,
232: final ClassWriter jclass) throws SpeedoException {
233: String filename = fn;
234: if (!filename.endsWith(".class")) {
235: filename = filename + ".class";
236: }
237: logger.log(BasicLevel.DEBUG, "Write the class " + filename);
238: try {
239: File outputFile = new File(src, filename);
240: if (!outputFile.getParentFile().exists()) {
241: outputFile.getParentFile().mkdirs();
242: }
243: outputFile.createNewFile();
244: FileOutputStream fos = new FileOutputStream(outputFile);
245: fos.write(jclass.toByteArray());
246: fos.close();
247: } catch (IOException e) {
248: throw new SpeedoException("Cannot write " + filename, e);
249: }
250: }
251: }
|