001 /* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017 package org.apache.commons.jci.compilers; 018 019 import java.io.ByteArrayOutputStream; 020 import java.io.IOException; 021 import java.io.InputStream; 022 import java.util.ArrayList; 023 import java.util.Collection; 024 import java.util.Locale; 025 import java.util.Map; 026 import java.util.StringTokenizer; 027 028 import org.apache.commons.jci.problems.CompilationProblem; 029 import org.apache.commons.jci.readers.ResourceReader; 030 import org.apache.commons.jci.stores.ResourceStore; 031 import org.apache.commons.jci.utils.ConversionUtils; 032 import org.apache.commons.logging.Log; 033 import org.apache.commons.logging.LogFactory; 034 import org.eclipse.jdt.core.compiler.IProblem; 035 import org.eclipse.jdt.internal.compiler.ClassFile; 036 import org.eclipse.jdt.internal.compiler.CompilationResult; 037 import org.eclipse.jdt.internal.compiler.Compiler; 038 import org.eclipse.jdt.internal.compiler.DefaultErrorHandlingPolicies; 039 import org.eclipse.jdt.internal.compiler.ICompilerRequestor; 040 import org.eclipse.jdt.internal.compiler.IErrorHandlingPolicy; 041 import org.eclipse.jdt.internal.compiler.IProblemFactory; 042 import org.eclipse.jdt.internal.compiler.classfmt.ClassFileReader; 043 import org.eclipse.jdt.internal.compiler.classfmt.ClassFormatException; 044 import org.eclipse.jdt.internal.compiler.env.ICompilationUnit; 045 import org.eclipse.jdt.internal.compiler.env.INameEnvironment; 046 import org.eclipse.jdt.internal.compiler.env.NameEnvironmentAnswer; 047 import org.eclipse.jdt.internal.compiler.impl.CompilerOptions; 048 import org.eclipse.jdt.internal.compiler.problem.DefaultProblemFactory; 049 050 /** 051 * Eclipse compiler implemenation 052 * 053 * @author tcurdt 054 */ 055 public final class EclipseJavaCompiler extends AbstractJavaCompiler { 056 057 private final Log log = LogFactory.getLog(EclipseJavaCompiler.class); 058 private final EclipseJavaCompilerSettings defaultSettings; 059 060 public EclipseJavaCompiler() { 061 this(new EclipseJavaCompilerSettings()); 062 } 063 064 public EclipseJavaCompiler( final Map<String, String> pSettings ) { 065 defaultSettings = new EclipseJavaCompilerSettings(pSettings); 066 } 067 068 public EclipseJavaCompiler( final EclipseJavaCompilerSettings pSettings ) { 069 defaultSettings = pSettings; 070 } 071 072 final class CompilationUnit implements ICompilationUnit { 073 074 final private String clazzName; 075 final private String fileName; 076 final private char[] typeName; 077 final private char[][] packageName; 078 final private ResourceReader reader; 079 080 CompilationUnit( final ResourceReader pReader, final String pSourceFile ) { 081 reader = pReader; 082 clazzName = ConversionUtils.convertResourceToClassName(pSourceFile); 083 fileName = pSourceFile; 084 int dot = clazzName.lastIndexOf('.'); 085 if (dot > 0) { 086 typeName = clazzName.substring(dot + 1).toCharArray(); 087 } else { 088 typeName = clazzName.toCharArray(); 089 } 090 091 log.debug("className=" + clazzName); 092 log.debug("fileName=" + fileName); 093 log.debug("typeName=" + new String(typeName)); 094 095 final StringTokenizer izer = new StringTokenizer(clazzName, "."); 096 packageName = new char[izer.countTokens() - 1][]; 097 for (int i = 0; i < packageName.length; i++) { 098 packageName[i] = izer.nextToken().toCharArray(); 099 log.debug("package[" + i + "]=" + new String(packageName[i])); 100 } 101 } 102 103 public char[] getFileName() { 104 return fileName.toCharArray(); 105 } 106 107 public char[] getContents() { 108 final byte[] content = reader.getBytes(fileName); 109 110 if (content == null) { 111 return null; 112 //throw new RuntimeException("resource " + fileName + " could not be found"); 113 } 114 115 return new String(content).toCharArray(); 116 } 117 118 public char[] getMainTypeName() { 119 return typeName; 120 } 121 122 public char[][] getPackageName() { 123 return packageName; 124 } 125 126 public boolean ignoreOptionalProblems() { 127 return false; 128 } 129 } 130 131 132 public org.apache.commons.jci.compilers.CompilationResult compile( 133 final String[] pSourceFiles, 134 final ResourceReader pReader, 135 final ResourceStore pStore, 136 final ClassLoader pClassLoader, 137 final JavaCompilerSettings pSettings 138 ) { 139 140 final Map<String, String> settingsMap = new EclipseJavaCompilerSettings(pSettings).toNativeSettings(); 141 142 final Collection<CompilationProblem> problems = new ArrayList<CompilationProblem>(); 143 144 final ICompilationUnit[] compilationUnits = new ICompilationUnit[pSourceFiles.length]; 145 for (int i = 0; i < compilationUnits.length; i++) { 146 final String sourceFile = pSourceFiles[i]; 147 148 if (pReader.isAvailable(sourceFile)) { 149 compilationUnits[i] = new CompilationUnit(pReader, sourceFile); 150 log.debug("compiling " + sourceFile); 151 } else { 152 // log.error("source not found " + sourceFile); 153 154 final CompilationProblem problem = new CompilationProblem() { 155 156 public int getEndColumn() { 157 return 0; 158 } 159 160 public int getEndLine() { 161 return 0; 162 } 163 164 public String getFileName() { 165 return sourceFile; 166 } 167 168 public String getMessage() { 169 return "Source " + sourceFile + " could not be found"; 170 } 171 172 public int getStartColumn() { 173 return 0; 174 } 175 176 public int getStartLine() { 177 return 0; 178 } 179 180 public boolean isError() { 181 return true; 182 } 183 184 @Override 185 public String toString() { 186 return getMessage(); 187 } 188 }; 189 190 if (problemHandler != null) { 191 problemHandler.handle(problem); 192 } 193 194 problems.add(problem); 195 } 196 } 197 198 if (problems.size() > 0) { 199 final CompilationProblem[] result = new CompilationProblem[problems.size()]; 200 problems.toArray(result); 201 return new org.apache.commons.jci.compilers.CompilationResult(result); 202 } 203 204 final IErrorHandlingPolicy policy = DefaultErrorHandlingPolicies.proceedWithAllProblems(); 205 final IProblemFactory problemFactory = new DefaultProblemFactory(Locale.getDefault()); 206 final INameEnvironment nameEnvironment = new INameEnvironment() { 207 208 public NameEnvironmentAnswer findType( final char[][] pCompoundTypeName ) { 209 final StringBuilder result = new StringBuilder(); 210 for (int i = 0; i < pCompoundTypeName.length; i++) { 211 if (i != 0) { 212 result.append('.'); 213 } 214 result.append(pCompoundTypeName[i]); 215 } 216 217 //log.debug("finding compoundTypeName=" + result.toString()); 218 219 return findType(result.toString()); 220 } 221 222 public NameEnvironmentAnswer findType( final char[] pTypeName, final char[][] pPackageName ) { 223 final StringBuilder result = new StringBuilder(); 224 for (int i = 0; i < pPackageName.length; i++) { 225 result.append(pPackageName[i]); 226 result.append('.'); 227 } 228 229 // log.debug("finding typeName=" + new String(typeName) + " packageName=" + result.toString()); 230 231 result.append(pTypeName); 232 return findType(result.toString()); 233 } 234 235 private NameEnvironmentAnswer findType( final String pClazzName ) { 236 237 if (isPackage(pClazzName)) { 238 return null; 239 } 240 241 log.debug("finding " + pClazzName); 242 243 final String resourceName = ConversionUtils.convertClassToResourcePath(pClazzName); 244 245 final byte[] clazzBytes = pStore.read(resourceName); 246 if (clazzBytes != null) { 247 log.debug("loading from store " + pClazzName); 248 249 final char[] fileName = pClazzName.toCharArray(); 250 try { 251 final ClassFileReader classFileReader = new ClassFileReader(clazzBytes, fileName, true); 252 return new NameEnvironmentAnswer(classFileReader, null); 253 } catch (final ClassFormatException e) { 254 log.error("wrong class format", e); 255 return null; 256 } 257 } 258 259 log.debug("not in store " + pClazzName); 260 261 final InputStream is = pClassLoader.getResourceAsStream(resourceName); 262 if (is == null) { 263 log.debug("class " + pClazzName + " not found"); 264 return null; 265 } 266 267 final byte[] buffer = new byte[8192]; 268 final ByteArrayOutputStream baos = new ByteArrayOutputStream(buffer.length); 269 int count; 270 try { 271 while ((count = is.read(buffer, 0, buffer.length)) > 0) { 272 baos.write(buffer, 0, count); 273 } 274 baos.flush(); 275 final char[] fileName = pClazzName.toCharArray(); 276 final ClassFileReader classFileReader = new ClassFileReader(baos.toByteArray(), fileName, true); 277 return new NameEnvironmentAnswer(classFileReader, null); 278 } catch (final IOException e) { 279 log.error("could not read class", e); 280 return null; 281 } catch (final ClassFormatException e) { 282 log.error("wrong class format", e); 283 return null; 284 } finally { 285 try { 286 baos.close(); 287 } catch (final IOException oe) { 288 log.error("could not close output stream", oe); 289 } 290 try { 291 is.close(); 292 } catch (final IOException ie) { 293 log.error("could not close input stream", ie); 294 } 295 } 296 } 297 298 private boolean isPackage( final String pClazzName ) { 299 300 // reject this early as it is cheap 301 if (pClazzName.contains("-")) { // "-" is not valid in package names 302 return false; 303 } 304 305 final InputStream is = pClassLoader.getResourceAsStream(ConversionUtils.convertClassToResourcePath(pClazzName)); 306 if (is != null) { 307 log.debug("found the class for " + pClazzName + "- no package"); 308 try { 309 is.close(); 310 } catch (final IOException ie) { 311 log.error("could not close input stream", ie); 312 } 313 return false; 314 } 315 316 // FIXME: this should not be tied to the extension 317 final String source = pClazzName.replace('.', '/') + ".java"; 318 if (pReader.isAvailable(source)) { 319 log.debug("found the source " + source + " for " + pClazzName + " - no package "); 320 return false; 321 } 322 323 /* 324 * See https://issues.apache.org/jira/browse/JCI-59 325 * At present, the code assumes that anything else is a package name 326 * This is wrong, as for example jci.AdditionalTopLevel is not a package name. 327 * It's not clear how to fix this in general. 328 * It would seem to need access to the input classpath and/or the generated classes. 329 */ 330 return true; 331 } 332 333 public boolean isPackage( char[][] parentPackageName, char[] pPackageName ) { 334 final StringBuilder result = new StringBuilder(); 335 if (parentPackageName != null) { 336 for (int i = 0; i < parentPackageName.length; i++) { 337 if (i != 0) { 338 result.append('.'); 339 } 340 result.append(parentPackageName[i]); 341 } 342 } 343 344 // log.debug("isPackage parentPackageName=" + result.toString() + " packageName=" + new String(packageName)); 345 346 if (parentPackageName != null && parentPackageName.length > 0) { 347 result.append('.'); 348 } 349 result.append(pPackageName); 350 return isPackage(result.toString()); 351 } 352 353 public void cleanup() { 354 log.debug("cleanup"); 355 } 356 }; 357 358 final ICompilerRequestor compilerRequestor = new ICompilerRequestor() { 359 public void acceptResult( final CompilationResult pResult ) { 360 if (pResult.hasProblems()) { 361 for (IProblem iproblem : pResult.getProblems()) { 362 final CompilationProblem problem = new EclipseCompilationProblem(iproblem); 363 if (problemHandler != null) { 364 problemHandler.handle(problem); 365 } 366 problems.add(problem); 367 } 368 } 369 if (!pResult.hasErrors()) { 370 final ClassFile[] clazzFiles = pResult.getClassFiles(); 371 for (ClassFile clazzFile : clazzFiles) { 372 final char[][] compoundName = clazzFile.getCompoundName(); 373 final StringBuilder clazzName = new StringBuilder(); 374 for (int j = 0; j < compoundName.length; j++) { 375 if (j != 0) { 376 clazzName.append('.'); 377 } 378 clazzName.append(compoundName[j]); 379 } 380 pStore.write(clazzName.toString().replace('.', '/') + ".class", clazzFile.getBytes()); 381 } 382 } 383 } 384 }; 385 386 final Compiler compiler = new Compiler(nameEnvironment, policy, new CompilerOptions(settingsMap), compilerRequestor, problemFactory); 387 388 compiler.compile(compilationUnits); 389 390 final CompilationProblem[] result = new CompilationProblem[problems.size()]; 391 problems.toArray(result); 392 return new org.apache.commons.jci.compilers.CompilationResult(result); 393 } 394 395 public JavaCompilerSettings createDefaultSettings() { 396 return new EclipseJavaCompilerSettings(defaultSettings); 397 } 398 }