001: /*
002: * ============================================================================
003: * GNU Lesser General Public License
004: * ============================================================================
005: *
006: * JasperReports - Free Java report-generating library.
007: * Copyright (C) 2001-2006 JasperSoft Corporation http://www.jaspersoft.com
008: *
009: * This library is free software; you can redistribute it and/or
010: * modify it under the terms of the GNU Lesser General Public
011: * License as published by the Free Software Foundation; either
012: * version 2.1 of the License, or (at your option) any later version.
013: *
014: * This library is distributed in the hope that it will be useful,
015: * but WITHOUT ANY WARRANTY; without even the implied warranty of
016: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
017: * Lesser General Public License for more details.
018: *
019: * You should have received a copy of the GNU Lesser General Public
020: * License along with this library; if not, write to the Free Software
021: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
022: *
023: * JasperSoft Corporation
024: * 303 Second Street, Suite 450 North
025: * San Francisco, CA 94107
026: * http://www.jaspersoft.com
027: */
028: package net.sf.jasperreports.engine.design;
029:
030: import java.io.ByteArrayOutputStream;
031: import java.io.File;
032: import java.io.IOException;
033: import java.io.InputStream;
034: import java.lang.reflect.Constructor;
035: import java.lang.reflect.InvocationTargetException;
036: import java.util.Enumeration;
037: import java.util.HashMap;
038: import java.util.Iterator;
039: import java.util.List;
040: import java.util.Locale;
041: import java.util.Map;
042: import java.util.Properties;
043:
044: import net.sf.jasperreports.engine.JRException;
045: import net.sf.jasperreports.engine.JRReport;
046: import net.sf.jasperreports.engine.JRRuntimeException;
047: import net.sf.jasperreports.engine.util.JRClassLoader;
048: import net.sf.jasperreports.engine.util.JRProperties;
049:
050: import org.apache.commons.logging.Log;
051: import org.apache.commons.logging.LogFactory;
052: import org.eclipse.jdt.core.compiler.IProblem;
053: import org.eclipse.jdt.internal.compiler.ClassFile;
054: import org.eclipse.jdt.internal.compiler.CompilationResult;
055: import org.eclipse.jdt.internal.compiler.Compiler;
056: import org.eclipse.jdt.internal.compiler.DefaultErrorHandlingPolicies;
057: import org.eclipse.jdt.internal.compiler.ICompilerRequestor;
058: import org.eclipse.jdt.internal.compiler.IErrorHandlingPolicy;
059: import org.eclipse.jdt.internal.compiler.IProblemFactory;
060: import org.eclipse.jdt.internal.compiler.classfmt.ClassFileReader;
061: import org.eclipse.jdt.internal.compiler.env.IBinaryType;
062: import org.eclipse.jdt.internal.compiler.env.ICompilationUnit;
063: import org.eclipse.jdt.internal.compiler.env.INameEnvironment;
064: import org.eclipse.jdt.internal.compiler.env.NameEnvironmentAnswer;
065: import org.eclipse.jdt.internal.compiler.impl.CompilerOptions;
066: import org.eclipse.jdt.internal.compiler.problem.DefaultProblemFactory;
067:
068: /**
069: * @author Teodor Danciu (teodord@users.sourceforge.net)
070: * @version $Id: JRJdtCompiler.java 1625 2007-03-09 17:21:31Z lucianc $
071: */
072: public class JRJdtCompiler extends JRAbstractJavaCompiler {
073: private static final String JDT_PROPERTIES_PREFIX = "org.eclipse.jdt.core.";
074:
075: /**
076: *
077: */
078: static final Log log = LogFactory.getLog(JRJdtCompiler.class);
079:
080: private final ClassLoader classLoader;
081:
082: Constructor constrNameEnvAnsBin;
083: Constructor constrNameEnvAnsCompUnit;
084:
085: boolean is2ArgsConstr;
086: Constructor constrNameEnvAnsBin2Args;
087: Constructor constrNameEnvAnsCompUnit2Args;
088:
089: public JRJdtCompiler() {
090: super (false);
091:
092: classLoader = getClassLoader();
093:
094: boolean success;
095: try //FIXME remove support for pre 3.1 jdt
096: {
097: Class classAccessRestriction = loadClass("org.eclipse.jdt.internal.compiler.env.AccessRestriction");
098: constrNameEnvAnsBin2Args = NameEnvironmentAnswer.class
099: .getConstructor(new Class[] { IBinaryType.class,
100: classAccessRestriction });
101: constrNameEnvAnsCompUnit2Args = NameEnvironmentAnswer.class
102: .getConstructor(new Class[] {
103: ICompilationUnit.class,
104: classAccessRestriction });
105: is2ArgsConstr = true;
106: success = true;
107: } catch (NoSuchMethodException e) {
108: success = false;
109: } catch (ClassNotFoundException ex) {
110: success = false;
111: }
112:
113: if (!success) {
114: try {
115: constrNameEnvAnsBin = NameEnvironmentAnswer.class
116: .getConstructor(new Class[] { IBinaryType.class });
117: constrNameEnvAnsCompUnit = NameEnvironmentAnswer.class
118: .getConstructor(new Class[] { ICompilationUnit.class });
119: is2ArgsConstr = false;
120: } catch (NoSuchMethodException ex) {
121: throw new JRRuntimeException(
122: "Not able to load JDT classes", ex);
123: }
124: }
125: }
126:
127: class CompilationUnit implements ICompilationUnit {
128: protected String srcCode;
129: protected String className;
130:
131: public CompilationUnit(String srcCode, String className) {
132: this .srcCode = srcCode;
133: this .className = className;
134: }
135:
136: public char[] getFileName() {
137: return className.toCharArray();
138: }
139:
140: public char[] getContents() {
141: return srcCode.toCharArray();
142: }
143:
144: public char[] getMainTypeName() {
145: return className.toCharArray();
146: }
147:
148: public char[][] getPackageName() {
149: return new char[0][0];
150: }
151: }
152:
153: /**
154: *
155: */
156: protected String compileUnits(final JRCompilationUnit[] units,
157: String classpath, File tempDirFile) {
158: final StringBuffer problemBuffer = new StringBuffer();
159:
160: INameEnvironment env = getNameEnvironment(units);
161:
162: final IErrorHandlingPolicy policy = DefaultErrorHandlingPolicies
163: .proceedWithAllProblems();
164:
165: final Map settings = getJdtSettings();
166:
167: final IProblemFactory problemFactory = new DefaultProblemFactory(
168: Locale.getDefault());
169:
170: final ICompilerRequestor requestor = getCompilerRequestor(
171: units, problemBuffer);
172:
173: ICompilationUnit[] compilationUnits = new ICompilationUnit[units.length];
174: for (int i = 0; i < compilationUnits.length; i++) {
175: compilationUnits[i] = new CompilationUnit(units[i]
176: .getSourceCode(), units[i].getName());
177: }
178:
179: Compiler compiler = new Compiler(env, policy, settings,
180: requestor, problemFactory);
181: compiler.compile(compilationUnits);
182:
183: if (problemBuffer.length() > 0) {
184: return problemBuffer.toString();
185: }
186:
187: return null;
188: }
189:
190: protected INameEnvironment getNameEnvironment(
191: final JRCompilationUnit[] units) {
192: final INameEnvironment env = new INameEnvironment() {
193: public NameEnvironmentAnswer findType(
194: char[][] compoundTypeName) {
195: StringBuffer result = new StringBuffer();
196: String sep = "";
197: for (int i = 0; i < compoundTypeName.length; i++) {
198: result.append(sep);
199: result.append(compoundTypeName[i]);
200: sep = ".";
201: }
202: return findType(result.toString());
203: }
204:
205: public NameEnvironmentAnswer findType(char[] typeName,
206: char[][] packageName) {
207: StringBuffer result = new StringBuffer();
208: String sep = "";
209: for (int i = 0; i < packageName.length; i++) {
210: result.append(sep);
211: result.append(packageName[i]);
212: sep = ".";
213: }
214: result.append(sep);
215: result.append(typeName);
216: return findType(result.toString());
217: }
218:
219: private int getClassIndex(String className) {
220: int classIdx;
221: for (classIdx = 0; classIdx < units.length; ++classIdx) {
222: if (className.equals(units[classIdx].getName())) {
223: break;
224: }
225: }
226:
227: if (classIdx >= units.length) {
228: classIdx = -1;
229: }
230:
231: return classIdx;
232: }
233:
234: private NameEnvironmentAnswer findType(String className) {
235: try {
236: int classIdx = getClassIndex(className);
237:
238: if (classIdx >= 0) {
239: ICompilationUnit compilationUnit = new CompilationUnit(
240: units[classIdx].getSourceCode(),
241: className);
242: if (is2ArgsConstr) {
243: return (NameEnvironmentAnswer) constrNameEnvAnsCompUnit2Args
244: .newInstance(new Object[] {
245: compilationUnit, null });
246: }
247:
248: return (NameEnvironmentAnswer) constrNameEnvAnsCompUnit
249: .newInstance(new Object[] { compilationUnit });
250: }
251:
252: String resourceName = className.replace('.', '/')
253: + ".class";
254: InputStream is = getResource(resourceName);
255: if (is != null) {
256: byte[] classBytes;
257: byte[] buf = new byte[8192];
258: ByteArrayOutputStream baos = new ByteArrayOutputStream(
259: buf.length);
260: int count;
261: while ((count = is.read(buf, 0, buf.length)) > 0) {
262: baos.write(buf, 0, count);
263: }
264: baos.flush();
265: classBytes = baos.toByteArray();
266: char[] fileName = className.toCharArray();
267: ClassFileReader classFileReader = new ClassFileReader(
268: classBytes, fileName, true);
269:
270: if (is2ArgsConstr) {
271: return (NameEnvironmentAnswer) constrNameEnvAnsBin2Args
272: .newInstance(new Object[] {
273: classFileReader, null });
274: }
275:
276: return (NameEnvironmentAnswer) constrNameEnvAnsBin
277: .newInstance(new Object[] { classFileReader });
278: }
279: } catch (IOException exc) {
280: log.error("Compilation error", exc);
281: } catch (org.eclipse.jdt.internal.compiler.classfmt.ClassFormatException exc) {
282: log.error("Compilation error", exc);
283: } catch (InvocationTargetException e) {
284: throw new JRRuntimeException(
285: "Not able to create NameEnvironmentAnswer",
286: e);
287: } catch (IllegalArgumentException e) {
288: throw new JRRuntimeException(
289: "Not able to create NameEnvironmentAnswer",
290: e);
291: } catch (InstantiationException e) {
292: throw new JRRuntimeException(
293: "Not able to create NameEnvironmentAnswer",
294: e);
295: } catch (IllegalAccessException e) {
296: throw new JRRuntimeException(
297: "Not able to create NameEnvironmentAnswer",
298: e);
299: }
300: return null;
301: }
302:
303: private boolean isPackage(String result) {
304: int classIdx = getClassIndex(result);
305: if (classIdx >= 0) {
306: return false;
307: }
308:
309: String resourceName = result.replace('.', '/')
310: + ".class";
311:
312: boolean isPackage = true;
313:
314: InputStream is = getResource(resourceName);
315:
316: if (is != null)// cannot just test for null; need to read from "is" to avoid bug
317: { // with sun.plugin.cache.EmptyInputStream on JRE 1.5 plugin
318: try // http://sourceforge.net/tracker/index.php?func=detail&aid=1478460&group_id=36382&atid=416703
319: {
320: isPackage = (is.read() > 0);
321: } catch (IOException e) {
322: //ignore
323: } finally {
324: try {
325: is.close();
326: } catch (IOException e) {
327: //ignore
328: }
329: }
330: }
331:
332: return isPackage;
333: }
334:
335: public boolean isPackage(char[][] parentPackageName,
336: char[] packageName) {
337: StringBuffer result = new StringBuffer();
338: String sep = "";
339: if (parentPackageName != null) {
340: for (int i = 0; i < parentPackageName.length; i++) {
341: result.append(sep);
342: result.append(parentPackageName[i]);
343: sep = ".";
344: }
345: }
346: if (Character.isUpperCase(packageName[0])) {
347: if (!isPackage(result.toString())) {
348: return false;
349: }
350: }
351: result.append(sep);
352: result.append(packageName);
353: return isPackage(result.toString());
354: }
355:
356: public void cleanup() {
357: }
358:
359: };
360:
361: return env;
362: }
363:
364: protected ICompilerRequestor getCompilerRequestor(
365: final JRCompilationUnit[] units,
366: final StringBuffer problemBuffer) {
367: final ICompilerRequestor requestor = new ICompilerRequestor() {
368: public void acceptResult(CompilationResult result) {
369: String className = ((CompilationUnit) result
370: .getCompilationUnit()).className;
371:
372: int classIdx;
373: for (classIdx = 0; classIdx < units.length; ++classIdx) {
374: if (className.equals(units[classIdx].getName())) {
375: break;
376: }
377: }
378:
379: if (result.hasErrors()) {
380: String sourceCode = units[classIdx].getSourceCode();
381:
382: IProblem[] problems = result.getErrors();
383: for (int i = 0; i < problems.length; i++) {
384: IProblem problem = problems[i];
385: //if (problem.isError())
386: {
387: problemBuffer.append(i + 1);
388: problemBuffer.append(". ");
389: problemBuffer.append(problem.getMessage());
390:
391: if (problem.getSourceStart() >= 0
392: && problem.getSourceEnd() >= 0) {
393: int problemStartIndex = sourceCode
394: .lastIndexOf("\n", problem
395: .getSourceStart()) + 1;
396: int problemEndIndex = sourceCode
397: .indexOf("\n", problem
398: .getSourceEnd());
399: if (problemEndIndex < 0) {
400: problemEndIndex = sourceCode
401: .length();
402: }
403:
404: problemBuffer.append("\n");
405: problemBuffer.append(sourceCode
406: .substring(problemStartIndex,
407: problemEndIndex));
408: problemBuffer.append("\n");
409: for (int j = problemStartIndex; j < problem
410: .getSourceStart(); j++) {
411: problemBuffer.append(" ");
412: }
413: if (problem.getSourceStart() == problem
414: .getSourceEnd()) {
415: problemBuffer.append("^");
416: } else {
417: problemBuffer.append("<");
418: for (int j = problem
419: .getSourceStart() + 1; j < problem
420: .getSourceEnd(); j++) {
421: problemBuffer.append("-");
422: }
423: problemBuffer.append(">");
424: }
425: }
426:
427: problemBuffer.append("\n");
428: }
429: }
430: problemBuffer.append(problems.length);
431: problemBuffer.append(" errors\n");
432: }
433: if (problemBuffer.length() == 0) {
434: ClassFile[] resultClassFiles = result
435: .getClassFiles();
436: for (int i = 0; i < resultClassFiles.length; i++) {
437: units[classIdx]
438: .setCompileData(resultClassFiles[i]
439: .getBytes());
440: }
441: }
442: }
443: };
444:
445: return requestor;
446: }
447:
448: protected Map getJdtSettings() {
449: final Map settings = new HashMap();
450: settings.put(CompilerOptions.OPTION_LineNumberAttribute,
451: CompilerOptions.GENERATE);
452: settings.put(CompilerOptions.OPTION_SourceFileAttribute,
453: CompilerOptions.GENERATE);
454: settings.put(CompilerOptions.OPTION_ReportDeprecation,
455: CompilerOptions.IGNORE);
456: // if (ctxt.getOptions().getJavaEncoding() != null)
457: // {
458: // settings.put(CompilerOptions.OPTION_Encoding, ctxt.getOptions().getJavaEncoding());
459: // }
460: // if (ctxt.getOptions().getClassDebugInfo())
461: // {
462: // settings.put(CompilerOptions.OPTION_LocalVariableAttribute, CompilerOptions.GENERATE);
463: // }
464:
465: List properties = JRProperties
466: .getProperties(JDT_PROPERTIES_PREFIX);
467: for (Iterator it = properties.iterator(); it.hasNext();) {
468: JRProperties.PropertySuffix property = (JRProperties.PropertySuffix) it
469: .next();
470: String propVal = property.getValue();
471: if (propVal != null && propVal.length() > 0) {
472: settings.put(property.getKey(), propVal);
473: }
474: }
475:
476: Properties systemProps = System.getProperties();
477: for (Enumeration it = systemProps.propertyNames(); it
478: .hasMoreElements();) {
479: String propName = (String) it.nextElement();
480: if (propName.startsWith(JDT_PROPERTIES_PREFIX)) {
481: String propVal = systemProps.getProperty(propName);
482: if (propVal != null && propVal.length() > 0) {
483: settings.put(propName, propVal);
484: }
485: }
486: }
487:
488: return settings;
489: }
490:
491: /**
492: *
493: */
494: private ClassLoader getClassLoader() {
495: ClassLoader clsLoader = Thread.currentThread()
496: .getContextClassLoader();
497:
498: if (clsLoader != null) {
499: try {
500: Class.forName(JRJdtCompiler.class.getName(), true,
501: clsLoader);
502: } catch (ClassNotFoundException e) {
503: clsLoader = null;
504: //if (log.isWarnEnabled())
505: // log.warn("Failure using Thread.currentThread().getContextClassLoader() in JRJdtCompiler class. Using JRJdtCompiler.class.getClassLoader() instead.");
506: }
507: }
508:
509: if (clsLoader == null) {
510: clsLoader = JRClassLoader.class.getClassLoader();
511: }
512:
513: return clsLoader;
514: }
515:
516: protected InputStream getResource(String resourceName) {
517: if (classLoader == null) {
518: return JRJdtCompiler.class.getResourceAsStream("/"
519: + resourceName);
520: }
521: return classLoader.getResourceAsStream(resourceName);
522: }
523:
524: protected Class loadClass(String className)
525: throws ClassNotFoundException {
526: if (classLoader == null) {
527: return Class.forName(className);
528: }
529: return classLoader.loadClass(className);
530: }
531:
532: protected void checkLanguage(String language) throws JRException {
533: if (!JRReport.LANGUAGE_JAVA.equals(language)) {
534: throw new JRException("Language \"" + language
535: + "\" not supported by this report compiler.\n"
536: + "Expecting \"java\" instead.");
537: }
538: }
539:
540: protected JRCompilationSourceCode generateSourceCode(
541: JRSourceCompileTask sourceTask) throws JRException {
542: return JRClassGenerator.generateClass(sourceTask);
543: }
544:
545: protected String getSourceFileName(String unitName) {
546: return unitName + ".java";
547: }
548:
549: protected String getCompilerClass() {
550: return JRJavacCompiler.class.getName();
551: }
552: }
|