--- a/java.source/apichanges.xml +++ a/java.source/apichanges.xml @@ -108,6 +108,20 @@ + + + Added GeneratorUtilities.createFromTemplate. + + + + + + Added the method createFromTemplate to class GeneratorUtilities, + which will create a new CompilationUnitTree from a template. + + + + Added GeneratorUtilities.appendToAnnotationValue. --- a/java.source/src/org/netbeans/api/java/source/AssignComments.java +++ a/java.source/src/org/netbeans/api/java/source/AssignComments.java @@ -106,7 +106,7 @@ boolean oldMapComments = mapComments; try { mapComments |= tree == commentMapTarget; - if ((commentMapTarget != null) && info.getTreeUtilities().isSynthetic(new TreePath(new TreePath(info.getCompilationUnit()), tree))) + if ((commentMapTarget != null) && info.getTreeUtilities().isSynthetic(new TreePath(new TreePath(unit), tree))) return null; if (commentMapTarget != null) { mapComments2(tree, true); --- a/java.source/src/org/netbeans/api/java/source/GeneratorUtilities.java +++ a/java.source/src/org/netbeans/api/java/source/GeneratorUtilities.java @@ -84,6 +84,7 @@ import com.sun.tools.javac.tree.JCTree.JCCompilationUnit; import java.io.IOException; +import java.net.URI; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -120,6 +121,7 @@ import javax.lang.model.util.Elements; import javax.swing.text.Document; import javax.swing.text.JTextComponent; +import javax.tools.JavaFileObject; import org.netbeans.api.editor.EditorRegistry; import org.netbeans.api.java.lexer.JavaTokenId; @@ -127,10 +129,13 @@ import org.netbeans.editor.GuardedDocument; import org.netbeans.modules.java.source.builder.CommentHandlerService; import org.netbeans.modules.java.source.builder.CommentSetImpl; +import org.netbeans.modules.java.source.parsing.FileObjects; import org.netbeans.modules.java.source.parsing.SourceFileObject; import org.netbeans.modules.java.source.query.CommentSet.RelativePosition; import org.netbeans.modules.java.source.save.DiffContext; import org.openide.cookies.EditorCookie; +import org.openide.filesystems.FileObject; +import org.openide.filesystems.FileUtil; import org.openide.loaders.DataObject; import org.openide.util.Exceptions; @@ -159,6 +164,32 @@ } /** + * Create a new CompilationUnitTree from a template. + * + * @param sourceRoot a source root under which the new file is created + * @param path a relative path to file separated by '/' + * @param kind the kind of Element to use for the template, can be null or + * CLASS, INTERFACE, ANNOTATION_TYPE, ENUM, PACKAGE + * @return new CompilationUnitTree created from a template + * @throws IOException when an exception occurs while creating the template + * @since 0.100 + */ + public CompilationUnitTree createFromTemplate(FileObject sourceRoot, String path, ElementKind kind) throws IOException { + String[] nameComponent = FileObjects.getFolderAndBaseName(path, '/'); + JavaFileObject sourceFile = FileObjects.templateFileObject(sourceRoot, nameComponent[0], nameComponent[1]); + FileObject template = FileUtil.getConfigFile(copy.template(kind)); + FileObject targetFile = copy.doCreateFromTemplate(template, sourceFile); + CompilationUnitTree templateCUT = copy.impl.getJavacTask().parse(FileObjects.nbFileObject(targetFile, targetFile.getParent())).iterator().next(); + CompilationUnitTree importComments = GeneratorUtilities.get(copy).importComments(templateCUT, templateCUT); + CompilationUnitTree result = copy.getTreeMaker().CompilationUnit(importComments.getPackageAnnotations(), + sourceRoot, + path, + importComments.getImports(), + importComments.getTypeDecls()); + return result; + } + + /** * Inserts a member to a class. Using the rules specified in the {@link CodeStyle} * it finds the proper place for the member and calls {@link TreeMaker.insertClassMember} * --- a/java.source/src/org/netbeans/api/java/source/WorkingCopy.java +++ a/java.source/src/org/netbeans/api/java/source/WorkingCopy.java @@ -61,6 +61,7 @@ import java.io.IOException; import java.io.StringWriter; import java.lang.ref.Reference; +import java.net.URI; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; @@ -74,6 +75,7 @@ import java.util.logging.Level; import java.util.logging.Logger; import javax.lang.model.element.Element; +import javax.lang.model.element.ElementKind; import javax.swing.text.BadLocationException; import javax.tools.JavaFileObject; @@ -550,8 +552,9 @@ try { FileObject targetFile = doCreateFromTemplate(t); CompilationUnitTree templateCUT = impl.getJavacTask().parse(FileObjects.nbFileObject(targetFile, targetFile.getParent())).iterator().next(); + CompilationUnitTree importComments = GeneratorUtilities.get(this).importComments(templateCUT, templateCUT); - changes.put(templateCUT, t); + changes.put(importComments, t); StringWriter target = new StringWriter(); @@ -568,26 +571,54 @@ return result; } - private static String template(CompilationUnitTree cut) { - if ("package-info.java".equals(cut.getSourceFile().getName())) return "Templates/Classes/package-info.java"; - if (cut.getTypeDecls().isEmpty()) return "Templates/Classes/Empty.java"; - - switch (cut.getTypeDecls().get(0).getKind()) { + String template(ElementKind kind) { + if(kind == null) { + return "Templates/Classes/Empty.java"; // NOI18N + } + switch (kind) { case CLASS: return "Templates/Classes/Class.java"; // NOI18N case INTERFACE: return "Templates/Classes/Interface.java"; // NOI18N case ANNOTATION_TYPE: return "Templates/Classes/AnnotationType.java"; // NOI18N case ENUM: return "Templates/Classes/Enum.java"; // NOI18N + case PACKAGE: return "Templates/Classes/package-info.java"; // NOI18N default: - Logger.getLogger(WorkingCopy.class.getName()).log(Level.SEVERE, "Cannot resolve template for {0}", cut.getTypeDecls().get(0).getKind()); - return "Templates/Classes/Empty.java"; + Logger.getLogger(WorkingCopy.class.getName()).log(Level.SEVERE, "Cannot resolve template for {0}", kind); + return "Templates/Classes/Empty.java"; // NOI18N } } - private static FileObject doCreateFromTemplate(CompilationUnitTree t) throws IOException { + FileObject doCreateFromTemplate(CompilationUnitTree cut) throws IOException { + ElementKind kind; + if ("package-info.java".equals(cut.getSourceFile().getName())) { + kind = ElementKind.PACKAGE; + } else if (cut.getTypeDecls().isEmpty()) { + kind = null; + } else { + switch (cut.getTypeDecls().get(0).getKind()) { + case CLASS: + kind = ElementKind.CLASS; + break; + case INTERFACE: + kind = ElementKind.INTERFACE; + break; + case ANNOTATION_TYPE: + kind = ElementKind.ANNOTATION_TYPE; + break; + case ENUM: + kind = ElementKind.ENUM; + break; + default: + Logger.getLogger(WorkingCopy.class.getName()).log(Level.SEVERE, "Cannot resolve template for {0}", cut.getTypeDecls().get(0).getKind()); + kind = null; + } + } + FileObject template = FileUtil.getConfigFile(template(kind)); + return doCreateFromTemplate(template, cut.getSourceFile()); + } + + FileObject doCreateFromTemplate(FileObject template, JavaFileObject sourceFile) throws IOException { FileObject scratchFolder = FileUtil.createMemoryFileSystem().getRoot(); - FileObject template = FileUtil.getConfigFile(template(t)); - if (template == null) { return scratchFolder.createData("out", "java"); } @@ -598,7 +629,7 @@ return scratchFolder.createData("out", "java"); } - File pack = new File(t.getSourceFile().toUri()).getParentFile(); + File pack = new File(sourceFile.toUri()).getParentFile(); while (FileUtil.toFileObject(pack) == null) { pack = pack.getParentFile(); --- a/java.source/test/unit/src/org/netbeans/api/java/source/gen/CompilationUnitTest.java +++ a/java.source/test/unit/src/org/netbeans/api/java/source/gen/CompilationUnitTest.java @@ -39,6 +39,7 @@ import java.util.Collections; import java.util.EnumSet; import java.util.Map; +import javax.lang.model.element.ElementKind; import javax.lang.model.element.Modifier; import javax.lang.model.element.TypeElement; import javax.lang.model.type.TypeKind; @@ -90,6 +91,90 @@ if (templates != null) templates.delete(); } + public void testNewCompilationUnitFromTemplate() throws Exception { + testFile = new File(getWorkDir(), "Test.java"); + + File fakeFile = new File(getWorkDir(), "Fake.java"); + FileObject fakeFO = FileUtil.createData(fakeFile); + + FileObject emptyJava = FileUtil.createData(FileUtil.getConfigRoot(), "Templates/Classes/Empty.java"); + emptyJava.setAttribute("template", Boolean.TRUE); + + FileObject classJava = FileUtil.createData(FileUtil.getConfigRoot(), "Templates/Classes/Class.java"); + classJava.setAttribute("template", Boolean.TRUE); + Writer w = new OutputStreamWriter(classJava.getOutputStream(), "UTF-8"); + w.write("package zoo;\n" + + "/**\n * trida\n */\n" + + "public class Template {\n" + + "}"); + w.close(); + + FileObject testSourceFO = FileUtil.createData(testFile); assertNotNull(testSourceFO); + ClassPath sourcePath = ClassPath.getClassPath(testSourceFO, ClassPath.SOURCE); assertNotNull(sourcePath); + FileObject[] roots = sourcePath.getRoots(); assertEquals(1, roots.length); + final FileObject sourceRoot = roots[0]; assertNotNull(sourceRoot); + ClassPath compilePath = ClassPath.getClassPath(testSourceFO, ClassPath.COMPILE); assertNotNull(compilePath); + ClassPath bootPath = ClassPath.getClassPath(testSourceFO, ClassPath.BOOT); assertNotNull(bootPath); + ClasspathInfo cpInfo = ClasspathInfo.create(bootPath, compilePath, sourcePath); + JavaSource javaSource = JavaSource.create(cpInfo, fakeFO); + + Task task = new Task() { + + public void cancel() { + } + + public void run(WorkingCopy workingCopy) throws Exception { + workingCopy.toPhase(JavaSource.Phase.PARSED); + TreeMaker make = workingCopy.getTreeMaker(); + String path = "zoo/Krtek.java"; + GeneratorUtilities genUtils = GeneratorUtilities.get(workingCopy); + CompilationUnitTree newTree = genUtils.createFromTemplate(sourceRoot, path, ElementKind.CLASS); + MethodTree nju = make.Method( + make.Modifiers(Collections.emptySet()), + "m", + make.PrimitiveType(TypeKind.VOID), // return type - void + Collections.emptyList(), + Collections.emptyList(), + Collections.emptyList(), + make.Block(Collections.emptyList(), false), + null // default value - not applicable + ); + ClassTree clazz = make.Class( + make.Modifiers(Collections.singleton(Modifier.PUBLIC)), + "Krtek", + Collections.emptyList(), + null, + Collections.emptyList(), + Collections.singletonList(nju) + ); + workingCopy.rewrite(null, newTree); + if(newTree.getTypeDecls().isEmpty()) { + make.addCompUnitTypeDecl(newTree, clazz); + } else { + Tree templateClass = newTree.getTypeDecls().get(0); + genUtils.copyComments(templateClass, clazz, true); + genUtils.copyComments(templateClass, clazz, false); + workingCopy.rewrite(templateClass, clazz); + } + } + }; + ModificationResult result = javaSource.runModificationTask(task); + result.commit(); + + String golden = + "package zoo;\n" + + "\n" + + "\n" + // XXX: + "/**\n * trida\n */\n" + + "public class Krtek {\n" + + "\n" + + " void m() {\n" + + " }\n" + + "}\n"; + String res = TestUtilities.copyFileToString(new File(getDataDir().getAbsolutePath() + "/zoo/Krtek.java")); + assertEquals(golden, res); + } + public void testNewCompilationUnit() throws Exception { testFile = new File(getWorkDir(), "Test.java");