diff options
author | jay | 2018-07-22 12:14:20 +0000 |
---|---|---|
committer | Jay Arthanareeswaran | 2018-07-23 05:54:41 +0000 |
commit | 7081db6a6447b3b4b844809179248a1964db08df (patch) | |
tree | f8eb8e816c2f871ff6bccebc5f05246d7b6652d1 | |
parent | 9b3ec1dae88eedd02134afe6ae9d7987b12d24cd (diff) | |
download | eclipse.jdt.core-7081db6a6447b3b4b844809179248a1964db08df.tar.gz eclipse.jdt.core-7081db6a6447b3b4b844809179248a1964db08df.tar.xz eclipse.jdt.core-7081db6a6447b3b4b844809179248a1964db08df.zip |
Bug 213539: Support Filer.createClassFile() in IDEI20180723-2000
Change-Id: I0158572470d4070d5421ccd0727b200f9805ef24
Signed-off-by: jay <jarthana@in.ibm.com>
9 files changed, 351 insertions, 13 deletions
diff --git a/org.eclipse.jdt.apt.pluggable.core/src/org/eclipse/jdt/internal/apt/pluggable/core/dispatch/IdeProcessingEnvImpl.java b/org.eclipse.jdt.apt.pluggable.core/src/org/eclipse/jdt/internal/apt/pluggable/core/dispatch/IdeProcessingEnvImpl.java index 396ef9e791..f8d1686eeb 100644 --- a/org.eclipse.jdt.apt.pluggable.core/src/org/eclipse/jdt/internal/apt/pluggable/core/dispatch/IdeProcessingEnvImpl.java +++ b/org.eclipse.jdt.apt.pluggable.core/src/org/eclipse/jdt/internal/apt/pluggable/core/dispatch/IdeProcessingEnvImpl.java @@ -182,4 +182,7 @@ public abstract class IdeProcessingEnvImpl extends BaseProcessingEnvImpl { public boolean isTestCode() { return _isTestCode; } + public Compiler getCompiler() { + return _compiler; + } } diff --git a/org.eclipse.jdt.apt.pluggable.core/src/org/eclipse/jdt/internal/apt/pluggable/core/filer/IdeClassOutputStream.java b/org.eclipse.jdt.apt.pluggable.core/src/org/eclipse/jdt/internal/apt/pluggable/core/filer/IdeClassOutputStream.java new file mode 100644 index 0000000000..17295a1ac9 --- /dev/null +++ b/org.eclipse.jdt.apt.pluggable.core/src/org/eclipse/jdt/internal/apt/pluggable/core/filer/IdeClassOutputStream.java @@ -0,0 +1,109 @@ +/******************************************************************************* + * Copyright (c) 2018 IBM Corporation. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ + +package org.eclipse.jdt.internal.apt.pluggable.core.filer; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; + +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IResource; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.jdt.apt.core.internal.util.FileSystemUtil; +import org.eclipse.jdt.core.compiler.CharOperation; +import org.eclipse.jdt.internal.apt.pluggable.core.Apt6Plugin; +import org.eclipse.jdt.internal.apt.pluggable.core.dispatch.IdeProcessingEnvImpl; +import org.eclipse.jdt.internal.compiler.Compiler; +import org.eclipse.jdt.internal.compiler.classfmt.ClassFileReader; +import org.eclipse.jdt.internal.compiler.env.IBinaryType; +import org.eclipse.jdt.internal.compiler.lookup.BinaryTypeBinding; +import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding; + +/** + * @see IdeClassOutputStream + */ +public class IdeClassOutputStream extends ByteArrayOutputStream +{ + private final IdeProcessingEnvImpl _env; + private final IFile _file; + + public IdeClassOutputStream(IdeProcessingEnvImpl env, IFile file) { + _env = env; + _file = file; + } + + @Override + public void close() throws IOException { + super.close(); + byte[] byteArray = toByteArray(); + InputStream contents = new ByteArrayInputStream(byteArray); + Compiler compiler = this._env.getCompiler(); + + IBinaryType binaryType = null; + try { + binaryType = ClassFileReader.read(this._file.getLocation().toString()); + if (binaryType == null) { + saveToDisk(contents, true); + } else { + saveToDisk(contents, false); + } + binaryType = ClassFileReader.read(this._file.getLocation().toString()); + char[] name = binaryType.getName(); + ReferenceBinding type = compiler.lookupEnvironment.getType(CharOperation.splitOn('/', name)); + if (type != null && type.isValidBinding()) { + if (type.isBinaryBinding()) { + _env.addNewClassFile(type); + } else { + BinaryTypeBinding binaryBinding = new BinaryTypeBinding(type.getPackage(), binaryType, compiler.lookupEnvironment, true); + if (binaryBinding != null) + _env.addNewClassFile(binaryBinding); + } + } + } catch(Exception ex) { + // move on + } + finally { + closeInputStream(contents); + } + } + + private void closeInputStream(InputStream stream) { + if (stream != null) { + try { + stream.close(); + } + catch (IOException ioe) {} + } + } + private void saveToDisk(InputStream toSave, boolean create) throws IOException{ + try { + FileSystemUtil.makeDerivedParentFolders(_file.getParent()); + if (create) { + _file.create(toSave, IResource.FORCE | IResource.DERIVED, null); + } + else { + _file.setContents(toSave, true, false, null); + } + } + catch (CoreException ce) { + if (_file.exists()) { + // Do nothing. This is a case-insensitive file system mismatch, + // and the underlying platform has saved the contents already. + } + else { + Apt6Plugin.log(ce, "Could not create generated non-Java file " + _file.getName()); //$NON-NLS-1$ + throw new IOException(ce); + } + } + } +} diff --git a/org.eclipse.jdt.apt.pluggable.core/src/org/eclipse/jdt/internal/apt/pluggable/core/filer/IdeFilerImpl.java b/org.eclipse.jdt.apt.pluggable.core/src/org/eclipse/jdt/internal/apt/pluggable/core/filer/IdeFilerImpl.java index 309fd8652f..c1ff14d9f6 100644 --- a/org.eclipse.jdt.apt.pluggable.core/src/org/eclipse/jdt/internal/apt/pluggable/core/filer/IdeFilerImpl.java +++ b/org.eclipse.jdt.apt.pluggable.core/src/org/eclipse/jdt/internal/apt/pluggable/core/filer/IdeFilerImpl.java @@ -32,6 +32,7 @@ import org.eclipse.core.resources.IWorkspace; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Path; import org.eclipse.jdt.apt.core.internal.AptCompilationParticipant; import org.eclipse.jdt.apt.core.internal.generatedfile.GeneratedSourceFolderManager; import org.eclipse.jdt.core.JavaModelException; @@ -61,8 +62,27 @@ public class IdeFilerImpl implements Filer { @Override public JavaFileObject createClassFile(CharSequence name, Element... originatingElements) throws IOException { - //TODO - throw new UnsupportedOperationException("Creating class files is not yet implemented"); //$NON-NLS-1$ + + // Pre-emptively check parameters here, rather than later on when the resource is written and closed. + if (null == name) { + throw new IllegalArgumentException("Name is null"); + } + + IFile file = _env.getAptProject().getGeneratedFileManager(_env.isTestCode()).getIFileForTypeName(name.toString()); + + GeneratedSourceFolderManager gsfm = _env.getAptProject().getGeneratedSourceFolderManager(_env.isTestCode()); + IPath path = null; + try { + path = gsfm.getBinaryOutputLocation(); + } catch (JavaModelException e) { + Apt6Plugin.log(e, "Failure getting the binary output location"); //$NON-NLS-1$ + throw new IOException(e); + } + file = getFileFromOutputLocation(StandardLocation.CLASS_OUTPUT, "", name + ".class"); + path = path.append(name.toString()); + path = new Path(path.toString() + ".class"); + + return new IdeOutputClassFileObject(_env, file, name.toString()); } /* (non-Javadoc) diff --git a/org.eclipse.jdt.apt.pluggable.core/src/org/eclipse/jdt/internal/apt/pluggable/core/filer/IdeOutputClassFileObject.java b/org.eclipse.jdt.apt.pluggable.core/src/org/eclipse/jdt/internal/apt/pluggable/core/filer/IdeOutputClassFileObject.java new file mode 100644 index 0000000000..ba48f1e62e --- /dev/null +++ b/org.eclipse.jdt.apt.pluggable.core/src/org/eclipse/jdt/internal/apt/pluggable/core/filer/IdeOutputClassFileObject.java @@ -0,0 +1,102 @@ +/******************************************************************************* + * Copyright (c) 2018 IBM Corporation. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ + +package org.eclipse.jdt.internal.apt.pluggable.core.filer; + +import java.io.IOException; +import java.io.OutputStream; +import java.io.PrintWriter; +import java.io.Writer; +import java.net.URI; + +import javax.lang.model.element.Modifier; +import javax.lang.model.element.NestingKind; +import javax.tools.JavaFileObject; + +import org.eclipse.core.resources.IFile; +import org.eclipse.jdt.internal.apt.pluggable.core.dispatch.IdeProcessingEnvImpl; + +/** + * Implementation of FileObject for generating resource files in the IDE. + * This is used for files that are neither class files nor Java source files. + * @see IdeOutputJavaFileObject + */ +public class IdeOutputClassFileObject extends IdeOutputFileObject implements JavaFileObject { + + private final IdeProcessingEnvImpl _env; + private final String _name; + private final IFile _file; + + public IdeOutputClassFileObject(IdeProcessingEnvImpl env, IFile file, String name) { + _env = env; + _name = name; + _file = file; + } + + /* (non-Javadoc) + * @see javax.tools.FileObject#getName() + */ + @Override + public String getName() + { + return _name; + } + + /* (non-Javadoc) + * @see javax.tools.FileObject#openOutputStream() + */ + @Override + public OutputStream openOutputStream() throws IOException + { + return new IdeClassOutputStream(_env, _file); + } + + /* (non-Javadoc) + * @see javax.tools.FileObject#openWriter() + */ + @Override + public Writer openWriter() throws IOException + { + return new PrintWriter(openOutputStream()); + } + + /* (non-Javadoc) + * @see javax.tools.FileObject#toUri() + */ + @Override + public URI toUri() { + return _file.getLocationURI(); + } + + @Override + public Kind getKind() { + return Kind.CLASS; + } + + @Override + public boolean isNameCompatible(String simpleName, Kind kind) { + //TODO + throw new UnsupportedOperationException("Not yet implemented"); + } + + @Override + public NestingKind getNestingKind() { + //TODO + throw new UnsupportedOperationException("Not yet implemented"); + } + + @Override + public Modifier getAccessLevel() { + //TODO + throw new UnsupportedOperationException("Not yet implemented"); + } + +} diff --git a/org.eclipse.jdt.apt.pluggable.tests/lib/annotations.jar b/org.eclipse.jdt.apt.pluggable.tests/lib/annotations.jar Binary files differindex a09e550584..07bd93286a 100644 --- a/org.eclipse.jdt.apt.pluggable.tests/lib/annotations.jar +++ b/org.eclipse.jdt.apt.pluggable.tests/lib/annotations.jar diff --git a/org.eclipse.jdt.apt.pluggable.tests/src/org/eclipse/jdt/apt/pluggable/tests/FilerTests.java b/org.eclipse.jdt.apt.pluggable.tests/src/org/eclipse/jdt/apt/pluggable/tests/FilerTests.java index 3834cef8ea..424b3f4ccd 100644 --- a/org.eclipse.jdt.apt.pluggable.tests/src/org/eclipse/jdt/apt/pluggable/tests/FilerTests.java +++ b/org.eclipse.jdt.apt.pluggable.tests/src/org/eclipse/jdt/apt/pluggable/tests/FilerTests.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2007 - 2009 BEA Systems, Inc. and others + * Copyright (c) 2007 - 2018 BEA Systems, Inc. and others * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -12,11 +12,9 @@ package org.eclipse.jdt.apt.pluggable.tests; import java.io.ByteArrayInputStream; +import java.io.File; import java.io.InputStream; -import junit.framework.Test; -import junit.framework.TestSuite; - import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IFolder; import org.eclipse.core.resources.IMarker; @@ -27,6 +25,10 @@ import org.eclipse.jdt.apt.pluggable.tests.processors.filertester.FilerTesterPro import org.eclipse.jdt.core.IJavaProject; import org.eclipse.jdt.core.compiler.CategorizedProblem; import org.eclipse.jdt.core.tests.builder.Problem; +import org.eclipse.jdt.internal.compiler.ClassFile; + +import junit.framework.Test; +import junit.framework.TestSuite; /** * Basic tests for the Filer interface in the IDE. @@ -356,4 +358,44 @@ public class FilerTests extends TestBase assertEquals("Processor reported errors", ProcessorTestStatus.NO_ERRORS, ProcessorTestStatus.getErrors()); } + public void testCreateClass1() throws Exception { + ProcessorTestStatus.reset(); + IJavaProject jproj = createJavaProject(_projectName); + disableJava5Factories(jproj); + IProject proj = jproj.getProject(); + IPath projPath = proj.getFullPath(); + + env.addClass(projPath.append("src"), "p", "Trigger", + "package p;\n" + + "import org.eclipse.jdt.apt.pluggable.tests.annotations.FilerTestTrigger;\n" + + "@FilerTestTrigger(test = \"testCreateClass1\", arg0 = \"p\", arg1 = \"Test.java\")" + + "public class Trigger {\n" + + "}" + ); + AptConfig.setEnabled(jproj, true); + + fullBuild(); + final String[] expectedClasses = {"p.Trigger" }; + expectingUniqueCompiledClasses(expectedClasses); + IPath path = proj.getLocation().append("bin/p/Trigger.class"); + File file = new File(path.toOSString()); + assertTrue("File should exist", file.exists()); + long lastModified = file.lastModified(); + Thread.sleep(500); + ClassFile[] classFiles = this.debugRequestor.getClassFiles(); + FilerTesterProc.classContent = classFiles[0].getBytes(); + env.addClass(projPath.append(".apt_generated"), "g", "Test", + "package g;\n" + + "import org.eclipse.jdt.apt.pluggable.tests.annotations.FilerTestTrigger;\n" + + "@FilerTestTrigger(test = \"testCreateClass1\",arg0 = \"g\",arg1 = \"Test.java\") " + + "public class Test { }" + ); + + incrementalBuild(); + assertEquals("should have triggered 5 rounds", 5, FilerTesterProc.roundNo); + file = new File(path.toOSString()); + assertTrue("File should exist", file.exists()); + long lastModified2 = file.lastModified(); + assertTrue("file should have been overwritten", (lastModified2 > lastModified)); + } } diff --git a/org.eclipse.jdt.apt.pluggable.tests/src/org/eclipse/jdt/apt/pluggable/tests/annotations/FilerTestTrigger.java b/org.eclipse.jdt.apt.pluggable.tests/src/org/eclipse/jdt/apt/pluggable/tests/annotations/FilerTestTrigger.java index 9f596dcb83..b16a698267 100644 --- a/org.eclipse.jdt.apt.pluggable.tests/src/org/eclipse/jdt/apt/pluggable/tests/annotations/FilerTestTrigger.java +++ b/org.eclipse.jdt.apt.pluggable.tests/src/org/eclipse/jdt/apt/pluggable/tests/annotations/FilerTestTrigger.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2007 BEA Systems, Inc. + * Copyright (c) 2007, 2018 BEA Systems, Inc. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -12,10 +12,14 @@ package org.eclipse.jdt.apt.pluggable.tests.annotations; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + /** * * @since 3.4 */ +@Retention(RetentionPolicy.RUNTIME) public @interface FilerTestTrigger { /** Name of test method to run */ String test(); diff --git a/org.eclipse.jdt.apt.pluggable.tests/src/org/eclipse/jdt/apt/pluggable/tests/processors/filertester/FilerTesterProc.java b/org.eclipse.jdt.apt.pluggable.tests/src/org/eclipse/jdt/apt/pluggable/tests/processors/filertester/FilerTesterProc.java index 9736c94472..31cd126bf6 100644 --- a/org.eclipse.jdt.apt.pluggable.tests/src/org/eclipse/jdt/apt/pluggable/tests/processors/filertester/FilerTesterProc.java +++ b/org.eclipse.jdt.apt.pluggable.tests/src/org/eclipse/jdt/apt/pluggable/tests/processors/filertester/FilerTesterProc.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2007 - 2009 BEA Systems, Inc. and others + * Copyright (c) 2007 - 2018 BEA Systems, Inc. and others * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -12,8 +12,10 @@ package org.eclipse.jdt.apt.pluggable.tests.processors.filertester; +import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; +import java.io.OutputStream; import java.io.PrintWriter; import java.io.Reader; import java.lang.reflect.InvocationTargetException; @@ -38,6 +40,7 @@ import javax.tools.StandardLocation; import org.eclipse.jdt.apt.pluggable.tests.ProcessorTestStatus; import org.eclipse.jdt.apt.pluggable.tests.annotations.FilerTestTrigger; +import org.eclipse.jdt.internal.apt.pluggable.core.filer.IdeOutputClassFileObject; /** * Testing annotation processors through JUnit in the IDE is complex, because each test requires @@ -55,6 +58,8 @@ public class FilerTesterProc extends AbstractProcessor { private ProcessingEnvironment _processingEnv; private Filer _filer; + public static int roundNo = 0; + public static byte[] classContent = null; public static final String resource01FileContents = "package g;\n" + @@ -231,6 +236,53 @@ public class FilerTesterProc extends AbstractProcessor { checkGenUri(foGenSrc, "G", javaStr, "generated source file"); } + public void testBug534979(Element e, String pkg, String relName) throws Exception { + JavaFileObject jfo = _filer.createSourceFile(e.getEnclosingElement().getSimpleName() + "/" + e.getSimpleName()); + PrintWriter pw = null; + try { + pw = new PrintWriter(jfo.openWriter()); + pw.println("package " + pkg + ";\npublic class " + e.getSimpleName() + "{ }"); + } + finally { + if (pw != null) + pw.close(); + } + } + public void testCreateClass1(Element e, String pkg, String relName) throws Exception { + Filer filer = processingEnv.getFiler(); + try { + if (++roundNo == 1) + return; + if (roundNo == 2) { + JavaFileObject jfo = filer.createSourceFile("p/Test", e.getEnclosingElement()); + PrintWriter pw = null; + try { + pw = new PrintWriter(jfo.openWriter()); + pw.write("package p;\n " + + "import org.eclipse.jdt.apt.pluggable.tests.annotations.FilerTestTrigger;\n" + + "@FilerTestTrigger(test = \"testCreateClass1\", arg0 = \"p\", arg1 = \"Test.java\")" + + "public class Test {}"); + } finally { + pw.close(); + } + } else if(roundNo == 3) { + if (classContent == null) { + throw new IOException("Class file should have been present"); + } + IdeOutputClassFileObject jfo = (IdeOutputClassFileObject) filer.createClassFile("p/Trigger"); + OutputStream out = null; + try { + out = jfo.openOutputStream(); + out.write(classContent); + } catch (Exception ex) { + } finally { + out.close(); + } + } + } finally { + } + } + private void checkGenUri(FileObject fo, String name, String content, String category) throws Exception { PrintWriter pw = null; try { @@ -326,5 +378,4 @@ public class FilerTesterProc extends AbstractProcessor { ProcessorTestStatus.fail("getCharContent() did not return expected contents"); } } - } diff --git a/org.eclipse.jdt.core.tests.builder/src/org/eclipse/jdt/core/tests/builder/EfficiencyCompilerRequestor.java b/org.eclipse.jdt.core.tests.builder/src/org/eclipse/jdt/core/tests/builder/EfficiencyCompilerRequestor.java index 90bda84ed6..90c5dd6778 100644 --- a/org.eclipse.jdt.core.tests.builder/src/org/eclipse/jdt/core/tests/builder/EfficiencyCompilerRequestor.java +++ b/org.eclipse.jdt.core.tests.builder/src/org/eclipse/jdt/core/tests/builder/EfficiencyCompilerRequestor.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2015 IBM Corporation and others. + * Copyright (c) 2000, 2018 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -21,6 +21,7 @@ public class EfficiencyCompilerRequestor implements IDebugRequestor { private ArrayList<String> compiledClasses = new ArrayList<>(); private ArrayList<String> compiledFiles = new ArrayList<>(); + private ArrayList<ClassFile> classes = new ArrayList<>(); public void acceptDebugResult(CompilationResult result){ @@ -34,22 +35,28 @@ public class EfficiencyCompilerRequestor implements IDebugRequestor { } }); for (int i = 0; i < classFiles.length; i++) { - String className = new String(classFiles[i].fileName()); + ClassFile c = classFiles[i]; + this.classes.add(c); + String className = new String(c.fileName()); this.compiledClasses.add(className.replace('/', '.')); } } String[] getCompiledClasses(){ - return this.compiledClasses.toArray(new String[0]); + return this.compiledClasses.toArray(new String[this.compiledClasses.size()]); } String[] getCompiledFiles(){ - return this.compiledFiles.toArray(new String[0]); + return this.compiledFiles.toArray(new String[this.compiledFiles.size()]); + } + public ClassFile[] getClassFiles() { + return this.classes.toArray(new ClassFile[this.classes.size()]); } public void clearResult(){ this.compiledClasses.clear(); this.compiledFiles.clear(); + this.classes.clear(); } public void reset() { |