You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@netbeans.apache.org by jl...@apache.org on 2017/10/04 19:41:16 UTC
[04/13] incubator-netbeans git commit: Work in progress: an
experiment on using javac from JDK for source code modeling.
http://git-wip-us.apache.org/repos/asf/incubator-netbeans/blob/ffc0de5a/java.source.nbjavac/src/org/netbeans/modules/java/source/nbjavac/parsing/PartialReparserImpl.java
----------------------------------------------------------------------
diff --git a/java.source.nbjavac/src/org/netbeans/modules/java/source/nbjavac/parsing/PartialReparserImpl.java b/java.source.nbjavac/src/org/netbeans/modules/java/source/nbjavac/parsing/PartialReparserImpl.java
new file mode 100644
index 0000000..a3c7975
--- /dev/null
+++ b/java.source.nbjavac/src/org/netbeans/modules/java/source/nbjavac/parsing/PartialReparserImpl.java
@@ -0,0 +1,226 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.netbeans.modules.java.source.nbjavac.parsing;
+
+import com.sun.source.tree.ClassTree;
+import com.sun.source.tree.CompilationUnitTree;
+import com.sun.source.tree.MethodTree;
+import com.sun.source.tree.Tree;
+import com.sun.source.util.TreePath;
+import com.sun.tools.javac.api.JavacTaskImpl;
+import com.sun.tools.javac.api.JavacTrees;
+import com.sun.tools.javac.parser.LazyDocCommentTable;
+import com.sun.tools.javac.tree.EndPosTable;
+import com.sun.tools.javac.tree.JCTree;
+import com.sun.tools.javac.util.Context;
+import com.sun.tools.javac.util.CouplingAbort;
+import com.sun.tools.javac.util.Log;
+import com.sun.tools.javac.util.Options;
+import com.sun.tools.javac.util.Position.LineMapImpl;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import javax.tools.Diagnostic;
+import javax.tools.DiagnosticListener;
+import javax.tools.JavaFileObject;
+import org.netbeans.api.java.source.JavaSource.Phase;
+import org.netbeans.modules.*;
+import org.netbeans.modules.java.source.parsing.CompilationInfoImpl;
+import org.netbeans.modules.java.source.parsing.JavacFlowListener;
+import org.netbeans.modules.java.source.parsing.JavacParser;
+import org.netbeans.modules.java.source.parsing.JavacParser.PartialReparser;
+import org.netbeans.modules.parsing.api.Snapshot;
+import org.openide.filesystems.FileObject;
+import org.openide.util.lookup.ServiceProvider;
+
+/**
+ *
+ * @author lahvac
+ */
+@ServiceProvider(service = PartialReparser.class, position = 100)
+public class PartialReparserImpl implements PartialReparser {
+ private static final Logger LOGGER = Logger.getLogger(PartialReparserImpl.class.getName());
+
+ @Override
+ public boolean reparseMethod (final CompilationInfoImpl ci,
+ final Snapshot snapshot,
+ final MethodTree orig,
+ final String newBody) throws IOException {
+ assert ci != null;
+ final FileObject fo = ci.getFileObject();
+ if (LOGGER.isLoggable(Level.FINER)) {
+ LOGGER.log(Level.FINER, "Reparse method in: {0}", fo); //NOI18N
+ }
+ final Phase currentPhase = ci.getPhase();
+ if (Phase.PARSED.compareTo(currentPhase) > 0) {
+ return false;
+ }
+ try {
+ final CompilationUnitTree cu = ci.getCompilationUnit();
+ if (cu == null || newBody == null) {
+ return false;
+ }
+ final JavacTaskImpl task = ci.getJavacTask();
+ if (Options.instance(task.getContext()).isSet(JavacParser.LOMBOK_DETECTED)) {
+ return false;
+ }
+ PartialReparserService pr = PartialReparserService.instance(task.getContext());
+ if (((JCTree.JCMethodDecl)orig).localEnv == null) {
+ //We are seeing interface method or abstract or native method with body.
+ //Don't do any optimalization of this broken code - has no attr env.
+ return false;
+ }
+ final JavacTrees jt = JavacTrees.instance(task);
+ final int origStartPos = (int) jt.getSourcePositions().getStartPosition(cu, orig.getBody());
+ final int origEndPos = (int) jt.getSourcePositions().getEndPosition(cu, orig.getBody());
+ if (origStartPos < 0) {
+ LOGGER.log(Level.WARNING, "Javac returned startpos: {0} < 0", new Object[]{origStartPos}); //NOI18N
+ return false;
+ }
+ if (origStartPos > origEndPos) {
+ LOGGER.log(Level.WARNING, "Javac returned startpos: {0} > endpos: {1}", new Object[]{origStartPos, origEndPos}); //NOI18N
+ return false;
+ }
+ final FindAnonymousVisitor fav = new FindAnonymousVisitor();
+ fav.scan(orig.getBody(), null);
+ if (fav.hasLocalClass) {
+ if (LOGGER.isLoggable(Level.FINER)) {
+ LOGGER.log(Level.FINER, "Skeep reparse method (old local classes): {0}", fo); //NOI18N
+ }
+ return false;
+ }
+ final int firstInner = fav.firstInner;
+ final int noInner = fav.noInner;
+ final Context ctx = task.getContext();
+// final TreeLoader treeLoader = TreeLoader.instance(ctx);
+// if (treeLoader != null) {
+// treeLoader.startPartialReparse();
+// }
+ try {
+ final Log l = Log.instance(ctx);
+ l.startPartialReparse();
+ final JavaFileObject prevLogged = l.useSource(cu.getSourceFile());
+ JCTree.JCBlock block;
+ try {
+ DiagnosticListener dl = ci.getDiagnosticListener();
+ assert dl instanceof CompilationInfoImpl.DiagnosticListenerImpl;
+ ((CompilationInfoImpl.DiagnosticListenerImpl)dl).startPartialReparse(origStartPos, origEndPos);
+ long start = System.currentTimeMillis();
+ Map<JCTree,LazyDocCommentTable.Entry> docComments = new HashMap<>();
+ block = pr.reparseMethodBody(cu, orig, newBody + " ", firstInner, docComments);
+ LOGGER.log(Level.FINER, "Reparsed method in: {0}", fo); //NOI18N
+ if (block == null) {
+ LOGGER.log(
+ Level.FINER,
+ "Skeep reparse method, invalid position, newBody: ", //NOI18N
+ newBody);
+ return false;
+ }
+ final int newEndPos = (int) jt.getSourcePositions().getEndPosition(cu, block);
+ if (newEndPos != origStartPos + newBody.length()) {
+ return false;
+ }
+ fav.reset();
+ fav.scan(block, null);
+ final int newNoInner = fav.noInner;
+ if (fav.hasLocalClass || noInner != newNoInner) {
+ if (LOGGER.isLoggable(Level.FINER)) {
+ LOGGER.log(Level.FINER, "Skeep reparse method (new local classes): {0}", fo); //NOI18N
+ }
+ return false;
+ }
+ ((LazyDocCommentTable) ((JCTree.JCCompilationUnit)cu).docComments).table.keySet().removeAll(fav.docOwners);
+ ((LazyDocCommentTable) ((JCTree.JCCompilationUnit)cu).docComments).table.putAll(docComments);
+ long end = System.currentTimeMillis();
+ if (fo != null) {
+ JavacParser.logTime (fo,Phase.PARSED,(end-start));
+ }
+ final int delta = newEndPos - origEndPos;
+ final EndPosTable endPos = ((JCTree.JCCompilationUnit)cu).endPositions;
+ final TranslatePositionsVisitor tpv = new TranslatePositionsVisitor(orig, endPos, delta);
+ tpv.scan(cu, null);
+ ((JCTree.JCMethodDecl)orig).body = block;
+ if (Phase.RESOLVED.compareTo(currentPhase)<=0) {
+ start = System.currentTimeMillis();
+ pr.reattrMethodBody(orig, block);
+ if (LOGGER.isLoggable(Level.FINER)) {
+ LOGGER.log(Level.FINER, "Resolved method in: {0}", fo); //NOI18N
+ }
+ if (!((CompilationInfoImpl.DiagnosticListenerImpl)dl).hasPartialReparseErrors()) {
+ final JavacFlowListener fl = JavacFlowListener.instance(ctx);
+ if (fl != null && fl.hasFlowCompleted(fo)) {
+ if (LOGGER.isLoggable(Level.FINER)) {
+ final List<? extends Diagnostic> diag = ci.getDiagnostics();
+ if (!diag.isEmpty()) {
+ LOGGER.log(Level.FINER, "Reflow with errors: {0} {1}", new Object[]{fo, diag}); //NOI18N
+ }
+ }
+ TreePath tp = TreePath.getPath(cu, orig); //todo: store treepath in changed method => improve speed
+ Tree t = tp.getParentPath().getLeaf();
+ pr.reflowMethodBody(cu, (ClassTree) t, orig);
+ if (LOGGER.isLoggable(Level.FINER)) {
+ LOGGER.log(Level.FINER, "Reflowed method in: {0}", fo); //NOI18N
+ }
+ }
+ }
+ end = System.currentTimeMillis();
+ if (fo != null) {
+ JavacParser.logTime (fo, Phase.ELEMENTS_RESOLVED,0L);
+ JavacParser.logTime (fo,Phase.RESOLVED,(end-start));
+ }
+ }
+
+ //fix CompilationUnitTree.getLineMap:
+ long startM = System.currentTimeMillis();
+ char[] chars = snapshot.getText().toString().toCharArray();
+ ((LineMapImpl) cu.getLineMap()).build(chars, chars.length);
+ LOGGER.log(Level.FINER, "Rebuilding LineMap took: {0}", System.currentTimeMillis() - startM);
+
+ ((CompilationInfoImpl.DiagnosticListenerImpl)dl).endPartialReparse (delta);
+ } finally {
+ l.endPartialReparse();
+ l.useSource(prevLogged);
+ }
+ ci.update(snapshot);
+ } finally {
+// if (treeLoader != null) {
+// treeLoader.endPartialReparse();
+// }
+ }
+ } catch (CouplingAbort ca) {
+ //Needs full reparse
+ return false;
+ } catch (Throwable t) {
+ if (t instanceof ThreadDeath) {
+ throw (ThreadDeath) t;
+ }
+ boolean a = false;
+ assert a = true;
+ if (a) {
+ JavacParser.dumpSource(ci, t);
+ }
+ return false;
+ }
+ return true;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-netbeans/blob/ffc0de5a/java.source.nbjavac/src/org/netbeans/modules/java/source/nbjavac/parsing/PartialReparserService.java
----------------------------------------------------------------------
diff --git a/java.source.nbjavac/src/org/netbeans/modules/java/source/nbjavac/parsing/PartialReparserService.java b/java.source.nbjavac/src/org/netbeans/modules/java/source/nbjavac/parsing/PartialReparserService.java
new file mode 100644
index 0000000..7bb407b
--- /dev/null
+++ b/java.source.nbjavac/src/org/netbeans/modules/java/source/nbjavac/parsing/PartialReparserService.java
@@ -0,0 +1,187 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.netbeans.modules.java.source.nbjavac.parsing;
+
+import com.sun.source.tree.BlockTree;
+import com.sun.source.tree.ClassTree;
+import com.sun.source.tree.CompilationUnitTree;
+import com.sun.source.tree.MethodTree;
+import com.sun.source.tree.Tree;
+import com.sun.tools.javac.code.Flags;
+import com.sun.tools.javac.code.Symbol.ClassSymbol;
+import com.sun.tools.javac.code.Symtab;
+import com.sun.tools.javac.code.Type;
+import com.sun.tools.javac.comp.Attr;
+import com.sun.tools.javac.comp.AttrContext;
+import com.sun.tools.javac.comp.Env;
+import com.sun.tools.javac.comp.Flow;
+import com.sun.tools.javac.comp.TypeEnter;
+import com.sun.tools.javac.parser.JavacParser;
+import com.sun.tools.javac.parser.LazyDocCommentTable;
+import com.sun.tools.javac.parser.Scanner;
+import com.sun.tools.javac.parser.ScannerFactory;
+import com.sun.tools.javac.tree.EndPosTable;
+import com.sun.tools.javac.tree.JCTree;
+import com.sun.tools.javac.tree.JCTree.JCBlock;
+import com.sun.tools.javac.tree.JCTree.JCClassDecl;
+import com.sun.tools.javac.tree.JCTree.JCCompilationUnit;
+import com.sun.tools.javac.tree.JCTree.JCMethodDecl;
+import com.sun.tools.javac.tree.JCTree.JCStatement;
+import com.sun.tools.javac.tree.JCTree.JCVariableDecl;
+import com.sun.tools.javac.tree.TreeInfo;
+import com.sun.tools.javac.tree.TreeMaker;
+import com.sun.tools.javac.util.Context;
+import com.sun.tools.javac.util.List;
+import com.sun.tools.javac.util.Log;
+import com.sun.tools.javac.util.Names;
+import java.nio.CharBuffer;
+import java.util.Map;
+import org.netbeans.lib.nbjavac.services.CancelService;
+import org.netbeans.lib.nbjavac.services.NBParserFactory;
+
+/**
+ *
+ * @author Tomas Zezula
+ */
+public class PartialReparserService {
+
+ protected static final Context.Key<PartialReparserService> partialReparserKey = new Context.Key<PartialReparserService>();
+
+ public static PartialReparserService instance(Context ctx) {
+ PartialReparserService res = ctx.get(partialReparserKey);
+
+ if (res == null) {
+ ctx.put(partialReparserKey, res = new PartialReparserService(ctx));
+ }
+
+ return res;
+ }
+
+ private final Context context;
+
+ public PartialReparserService(Context context) {
+ this.context = context;
+ }
+
+ public JCBlock reparseMethodBody(CompilationUnitTree topLevel, MethodTree methodToReparse, String newBodyText, int annonIndex,
+ final Map<? super JCTree,? super LazyDocCommentTable.Entry> docComments) {
+ CharBuffer buf = CharBuffer.wrap((newBodyText+"\u0000").toCharArray(), 0, newBodyText.length());
+ JavacParser parser = newParser(context, buf, ((JCBlock)methodToReparse.getBody()).pos, ((JCCompilationUnit)topLevel).endPositions);
+ final JCStatement statement = parser.parseStatement();
+ NBParserFactory.assignAnonymousClassIndices(Names.instance(context), statement, Names.instance(context).empty, annonIndex);
+ if (statement.getKind() == Tree.Kind.BLOCK) {
+ if (docComments != null) {
+ docComments.putAll(((LazyDocCommentTable) parser.getDocComments()).table);
+ }
+ return (JCBlock) statement;
+ }
+ return null;
+ }
+
+ public JavacParser newParser(Context context, CharSequence input, int startPos, final EndPosTable endPos) {
+ NBParserFactory parserFactory = (NBParserFactory) NBParserFactory.instance(context); //TODO: eliminate the cast
+ ScannerFactory scannerFactory = ScannerFactory.instance(context);
+ CancelService cancelService = CancelService.instance(context);
+ Scanner lexer = scannerFactory.newScanner(input, true);
+ lexer.seek(startPos);
+ ((NBParserFactory.NBJavacParser.EndPosTableImpl)endPos).resetErrorEndPos();
+ return new NBParserFactory.NBJavacParser(parserFactory, lexer, true, false, true, false, cancelService) {
+ @Override protected JavacParser.AbstractEndPosTable newEndPosTable(boolean keepEndPositions) {
+ return new JavacParser.AbstractEndPosTable(this) {
+
+ @Override
+ public void storeEnd(JCTree tree, int endpos) {
+ ((NBParserFactory.NBJavacParser.EndPosTableImpl)endPos).storeEnd(tree, endpos);
+ }
+
+ @Override
+ protected <T extends JCTree> T to(T t) {
+ storeEnd(t, token.endPos);
+ return t;
+ }
+
+ @Override
+ protected <T extends JCTree> T toP(T t) {
+ storeEnd(t, S.prevToken().endPos);
+ return t;
+ }
+
+ @Override
+ public int getEndPos(JCTree tree) {
+ return endPos.getEndPos(tree);
+ }
+
+ @Override
+ public int replaceTree(JCTree oldtree, JCTree newtree) {
+ return endPos.replaceTree(oldtree, newtree);
+ }
+
+ @Override
+ public void setErrorEndPos(int errPos) {
+ super.setErrorEndPos(errPos);
+ ((NBParserFactory.NBJavacParser.EndPosTableImpl)endPos).setErrorEndPos(errPos);
+ }
+ };
+ }
+ };
+ }
+
+ public BlockTree reattrMethodBody(MethodTree methodToReparse, BlockTree block) {
+ Attr attr = Attr.instance(context);
+ assert ((JCMethodDecl)methodToReparse).localEnv != null;
+ JCMethodDecl tree = (JCMethodDecl) methodToReparse;
+ final Names names = Names.instance(context);
+ final Symtab syms = Symtab.instance(context);
+ final TypeEnter typeEnter = TypeEnter.instance(context);
+ final Log log = Log.instance(context);
+ final TreeMaker make = TreeMaker.instance(context);
+ final Env<AttrContext> env = attr.dupLocalEnv(((JCMethodDecl) methodToReparse).localEnv);
+ final ClassSymbol owner = env.enclClass.sym;
+ if (tree.name == names.init && !owner.type.isErroneous() && owner.type != syms.objectType) {
+ JCBlock body = tree.body;
+ if (body.stats.isEmpty() || !TreeInfo.isSelfCall(body.stats.head)) {
+ body.stats = body.stats.
+ prepend(typeEnter.SuperCall(make.at(body.pos),
+ List.<Type>nil(),
+ List.<JCVariableDecl>nil(),
+ false));
+ } else if ((env.enclClass.sym.flags() & Flags.ENUM) != 0 &&
+ (tree.mods.flags & Flags.GENERATEDCONSTR) == 0 &&
+ TreeInfo.isSuperCall(body.stats.head)) {
+ // enum constructors are not allowed to call super
+ // directly, so make sure there aren't any super calls
+ // in enum constructors, except in the compiler
+ // generated one.
+ log.error(tree.body.stats.head.pos(),
+ "call.to.super.not.allowed.in.enum.ctor",
+ env.enclClass.sym);
+ }
+ }
+ attr.attribStat((JCBlock)block, env);
+ return block;
+ }
+
+ public BlockTree reflowMethodBody(CompilationUnitTree topLevel, ClassTree ownerClass, MethodTree methodToReparse) {
+ Flow flow = Flow.instance(context);
+ TreeMaker make = TreeMaker.instance(context);
+ flow.reanalyzeMethod(make.forToplevel((JCCompilationUnit)topLevel),
+ (JCClassDecl)ownerClass);
+ return methodToReparse.getBody();
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-netbeans/blob/ffc0de5a/java.source.nbjavac/src/org/netbeans/modules/java/source/nbjavac/parsing/SequentialParsingImpl.java
----------------------------------------------------------------------
diff --git a/java.source.nbjavac/src/org/netbeans/modules/java/source/nbjavac/parsing/SequentialParsingImpl.java b/java.source.nbjavac/src/org/netbeans/modules/java/source/nbjavac/parsing/SequentialParsingImpl.java
new file mode 100644
index 0000000..a30b6a2
--- /dev/null
+++ b/java.source.nbjavac/src/org/netbeans/modules/java/source/nbjavac/parsing/SequentialParsingImpl.java
@@ -0,0 +1,41 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.netbeans.modules.java.source.nbjavac.parsing;
+
+import com.sun.source.tree.CompilationUnitTree;
+import com.sun.source.util.JavacTask;
+import com.sun.tools.javac.api.JavacTaskImpl;
+import java.io.IOException;
+import javax.tools.JavaFileObject;
+import org.netbeans.modules.java.source.parsing.JavacParser.SequentialParsing;
+import org.openide.util.lookup.ServiceProvider;
+
+/**
+ *
+ * @author lahvac
+ */
+@ServiceProvider(service=SequentialParsing.class)
+public class SequentialParsingImpl implements SequentialParsing {
+
+ @Override
+ public Iterable<? extends CompilationUnitTree> parse(JavacTask task, JavaFileObject file) throws IOException {
+ return ((JavacTaskImpl) task).parse(file);
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-netbeans/blob/ffc0de5a/java.source.nbjavac/src/org/netbeans/modules/java/source/nbjavac/parsing/TranslatePositionsVisitor.java
----------------------------------------------------------------------
diff --git a/java.source.nbjavac/src/org/netbeans/modules/java/source/nbjavac/parsing/TranslatePositionsVisitor.java b/java.source.nbjavac/src/org/netbeans/modules/java/source/nbjavac/parsing/TranslatePositionsVisitor.java
new file mode 100644
index 0000000..fb9c261
--- /dev/null
+++ b/java.source.nbjavac/src/org/netbeans/modules/java/source/nbjavac/parsing/TranslatePositionsVisitor.java
@@ -0,0 +1,121 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.netbeans.modules.java.source.nbjavac.parsing;
+
+import com.sun.source.tree.CompilationUnitTree;
+import com.sun.source.tree.MethodTree;
+import com.sun.source.tree.Tree;
+import com.sun.source.tree.VariableTree;
+import org.netbeans.api.java.source.support.ErrorAwareTreeScanner;
+import com.sun.tools.javac.tree.EndPosTable;
+import com.sun.tools.javac.tree.JCTree;
+import com.sun.tools.javac.tree.JCTree.JCVariableDecl;
+
+/**
+ * Helper visitor for partial reparse.
+ * Updates tree positions by the given delta.
+ * @author Tomas Zezula
+ */
+class TranslatePositionsVisitor extends ErrorAwareTreeScanner<Void,Void> {
+
+ private final MethodTree changedMethod;
+ private final EndPosTable endPos;
+ private final int delta;
+ boolean active;
+ boolean inMethod;
+
+ public TranslatePositionsVisitor (final MethodTree changedMethod, final EndPosTable endPos, final int delta) {
+ assert changedMethod != null;
+ assert endPos != null;
+ this.changedMethod = changedMethod;
+ this.endPos = endPos;
+ this.delta = delta;
+ }
+
+
+ @Override
+ public Void scan(Tree node, Void p) {
+ if (active && node != null) {
+ if (((JCTree)node).pos >= 0) {
+ ((JCTree)node).pos+=delta;
+ }
+ }
+ Void result = super.scan(node, p);
+ if (inMethod && node != null) {
+ endPos.replaceTree((JCTree) node, null);//remove
+ }
+ if (active && node != null) {
+ Integer pos = endPos.replaceTree((JCTree) node, null);//remove
+ if (pos != null) {
+ int newPos;
+ if (pos < 0) {
+ newPos = pos;
+ }
+ else {
+ newPos = pos+delta;
+ }
+ endPos.storeEnd((JCTree)node,newPos);
+ }
+ }
+ return result;
+ }
+
+ @Override
+ public Void visitCompilationUnit(CompilationUnitTree node, Void p) {
+ return scan (node.getTypeDecls(), p);
+ }
+
+
+ @Override
+ public Void visitMethod(MethodTree node, Void p) {
+ if (active || inMethod) {
+ scan(node.getModifiers(), p);
+ scan(node.getReturnType(), p);
+ scan(node.getTypeParameters(), p);
+ scan(node.getParameters(), p);
+ scan(node.getThrows(), p);
+ }
+ if (node == changedMethod) {
+ inMethod = true;
+ }
+ if (active || inMethod) {
+ scan(node.getBody(), p);
+ }
+ if (inMethod) {
+ active = inMethod;
+ inMethod = false;
+ }
+ if (active || inMethod) {
+ scan(node.getDefaultValue(), p);
+ }
+ return null;
+ }
+
+ @Override
+ public Void visitVariable(VariableTree node, Void p) {
+ JCVariableDecl varDecl = (JCVariableDecl) node;
+ if (varDecl.sym != null && active && varDecl.sym.pos >= 0) {
+ varDecl.sym.pos += delta;
+ }
+ return super.visitVariable(node, p);
+ }
+
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-netbeans/blob/ffc0de5a/java.source.nbjavac/src/org/netbeans/modules/java/source/nbjavac/parsing/TreeLoader.java
----------------------------------------------------------------------
diff --git a/java.source.nbjavac/src/org/netbeans/modules/java/source/nbjavac/parsing/TreeLoader.java b/java.source.nbjavac/src/org/netbeans/modules/java/source/nbjavac/parsing/TreeLoader.java
new file mode 100644
index 0000000..bcece76
--- /dev/null
+++ b/java.source.nbjavac/src/org/netbeans/modules/java/source/nbjavac/parsing/TreeLoader.java
@@ -0,0 +1,814 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.netbeans.modules.java.source.nbjavac.parsing;
+
+import com.sun.source.tree.ClassTree;
+import com.sun.source.tree.CompilationUnitTree;
+import com.sun.source.tree.MethodTree;
+import com.sun.source.tree.PackageTree;
+import com.sun.source.tree.Tree;
+import com.sun.source.tree.TypeParameterTree;
+import com.sun.source.tree.VariableTree;
+import com.sun.source.util.JavacTask;
+import com.sun.source.util.TaskEvent;
+import com.sun.source.util.TaskListener;
+import com.sun.tools.javac.api.DuplicateClassChecker;
+import com.sun.tools.javac.api.JavacTaskImpl;
+import com.sun.tools.javac.code.Kinds;
+import com.sun.tools.javac.code.Scope;
+import com.sun.tools.javac.code.Symbol;
+import com.sun.tools.javac.code.Symbol.ClassSymbol;
+import com.sun.tools.javac.code.Symbol.MethodSymbol;
+import com.sun.tools.javac.code.Symbol.VarSymbol;
+import com.sun.tools.javac.code.Symtab;
+import com.sun.tools.javac.code.Type;
+import com.sun.tools.javac.code.Types;
+import com.sun.tools.javac.comp.AttrContext;
+import com.sun.tools.javac.comp.Enter;
+import com.sun.tools.javac.comp.Env;
+import com.sun.tools.javac.main.JavaCompiler;
+import com.sun.tools.javac.model.LazyTreeLoader;
+import com.sun.tools.javac.tree.JCTree;
+import com.sun.tools.javac.tree.JCTree.JCClassDecl;
+import com.sun.tools.javac.tree.JCTree.JCMethodDecl;
+import com.sun.tools.javac.tree.JCTree.JCVariableDecl;
+import com.sun.tools.javac.tree.TreeScanner;
+import com.sun.tools.javac.util.Context;
+import com.sun.tools.javac.util.CouplingAbort;
+import com.sun.tools.javac.util.List;
+import com.sun.tools.javac.util.Log;
+import com.sun.tools.javac.util.Log.DiscardDiagnosticHandler;
+import java.awt.EventQueue;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.InterruptedIOException;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.io.Reader;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import java.util.regex.Pattern;
+import javax.lang.model.element.Name;
+import javax.swing.text.ChangedCharSetException;
+import javax.swing.text.MutableAttributeSet;
+import javax.swing.text.html.HTML;
+import javax.swing.text.html.HTML.Tag;
+import javax.swing.text.html.HTMLEditorKit;
+import javax.swing.text.html.HTMLEditorKit.ParserCallback;
+import javax.swing.text.html.parser.ParserDelegator;
+import javax.tools.JavaFileManager;
+import javax.tools.JavaFileObject;
+import javax.tools.StandardLocation;
+import org.netbeans.api.annotations.common.CheckForNull;
+import org.netbeans.api.annotations.common.NonNull;
+import org.netbeans.api.java.source.ClasspathInfo;
+import org.netbeans.api.java.source.ElementUtilities;
+import org.netbeans.api.java.source.SourceUtils;
+import org.netbeans.modules.java.source.JavaSourceAccessor;
+import org.netbeans.modules.java.source.JavadocHelper;
+import org.netbeans.modules.java.source.indexing.FQN2Files;
+import org.netbeans.modules.java.source.indexing.JavaBinaryIndexer;
+import org.netbeans.modules.java.source.indexing.JavaIndex;
+import org.netbeans.modules.java.source.parsing.FileManagerTransaction;
+import org.netbeans.modules.java.source.parsing.FileObjects;
+import org.netbeans.modules.java.source.parsing.JavacParser;
+import org.netbeans.modules.java.source.parsing.JavacParser.DuplicateClassRegistry;
+import org.netbeans.modules.java.source.parsing.JavacParser.TreeLoaderRegistry;
+import org.netbeans.modules.java.source.parsing.OutputFileManager.InvalidSourcePath;
+import org.netbeans.modules.java.source.usages.ClasspathInfoAccessor;
+import org.netbeans.modules.parsing.impl.indexing.IndexingUtils;
+import org.openide.filesystems.FileObject;
+import org.openide.filesystems.URLMapper;
+import org.openide.util.Exceptions;
+import org.openide.util.lookup.ServiceProvider;
+
+/**
+ *
+ * @author Dusan Balek
+ */
+public class TreeLoader extends LazyTreeLoader {
+
+ private static final String OPTION_OUTPUT_ROOT = "output-root"; //NOI18N
+ private static final Pattern ctor_summary_name = Pattern.compile("constructor[_.]summary"); //NOI18N
+ private static final Pattern method_summary_name = Pattern.compile("method[_.]summary"); //NOI18N
+ private static final Pattern field_detail_name = Pattern.compile("field[_.]detail"); //NOI18N
+ private static final Pattern ctor_detail_name = Pattern.compile("constructor[_.]detail"); //NOI18N
+ private static final Pattern method_detail_name = Pattern.compile("method[_.]detail"); //NOI18N
+ private static final ThreadLocal<Boolean> isTreeLoading = new ThreadLocal<Boolean>();
+
+ public static void preRegister(final Context context, final ClasspathInfo cpInfo, final boolean detached) {
+ context.put(lazyTreeLoaderKey, new TreeLoader(context, cpInfo, detached));
+ }
+
+ public static TreeLoader instance (final Context ctx) {
+ final LazyTreeLoader tl = LazyTreeLoader.instance(ctx);
+ return (tl instanceof TreeLoader) ? (TreeLoader)tl : null;
+ }
+
+ public static boolean isTreeLoading() {
+ return isTreeLoading.get() == Boolean.TRUE;
+ }
+
+ private static final Logger LOGGER = Logger.getLogger(TreeLoader.class.getName());
+ private static final boolean ALWAYS_ALLOW_JDOC_ARG_NAMES = Boolean.getBoolean("java.source.args.from.http.jdoc"); //NOI18N
+ public static boolean DISABLE_CONFINEMENT_TEST = false; //Only for tests!
+ public static boolean DISABLE_ARTIFICAL_PARAMETER_NAMES = false; //Only for tests!
+
+ private Context context;
+ private ClasspathInfo cpInfo;
+ private Map<ClassSymbol, StringBuilder> couplingErrors;
+ private boolean partialReparse;
+ //@GuardedBy("this")
+ private Thread owner;
+ //@GuardedBy("this")
+ private int depth;
+ private final boolean checkContention;
+
+ private TreeLoader(Context context, ClasspathInfo cpInfo, boolean detached) {
+ this.context = context;
+ this.cpInfo = cpInfo;
+ this.checkContention = detached;
+ }
+
+ @Override
+ public boolean loadTreeFor(final ClassSymbol clazz, boolean persist) {
+ boolean contended = !attachTo(Thread.currentThread());
+ try {
+ assert DISABLE_CONFINEMENT_TEST || JavaSourceAccessor.getINSTANCE().isJavaCompilerLocked() || !contended;
+ if (clazz != null) {
+ if (Enter.instance(context).getEnv(clazz) != null) {
+ return true;
+ }
+ try {
+ FileObject fo = SourceUtils.getFile(clazz, cpInfo);
+ final JavacTaskImpl jti = (JavacTaskImpl) context.get(JavacTask.class);
+ JavaCompiler jc = JavaCompiler.instance(context);
+ if (fo != null && jti != null) {
+ final Log log = Log.instance(context);
+ log.nerrors = 0;
+ final JavaFileManager jfm = context.get(JavaFileManager.class);
+ final Symtab syms = Symtab.instance(context);
+ JavaFileObject jfo = FileObjects.sourceFileObject(fo, null);
+ Map<ClassSymbol, StringBuilder> oldCouplingErrors = couplingErrors;
+ boolean oldSkipAPT = jc.skipAnnotationProcessing;
+ try {
+ couplingErrors = new HashMap<ClassSymbol, StringBuilder>();
+ jc.skipAnnotationProcessing = true;
+ Iterable<? extends CompilationUnitTree> cuts = jti.parse(jfo);
+ for (CompilationUnitTree cut : cuts) {
+ ((JCTree.JCCompilationUnit)cut).modle = clazz.packge().modle;
+ }
+ jti.analyze(jti.enter(cuts));
+ if (persist && log.nerrors == 0) {
+ final File classFolder = getClassFolder(jfm, clazz);
+ if (classFolder != null) {
+ jfm.handleOption(OPTION_OUTPUT_ROOT, Collections.singletonList(classFolder.getPath()).iterator()); //NOI18N
+ try {
+ if (jfm.hasLocation(StandardLocation.CLASS_OUTPUT) && canWrite(cpInfo)) {
+ Env<AttrContext> env = Enter.instance(context).getEnv(clazz);
+ HashMap<ClassSymbol, JCClassDecl> syms2trees;
+ if (env != null && pruneTree(env.tree, Symtab.instance(context), syms2trees = new HashMap<>())) {
+ isTreeLoading.set(Boolean.TRUE);
+ try {
+ dumpSymFile(jti, clazz, syms2trees);
+ } finally {
+ isTreeLoading.remove();
+ }
+ }
+ } else {
+ final JavaFileObject cfo = clazz.classfile;
+ final FileObject cFileObject = URLMapper.findFileObject(cfo.toUri().toURL());
+ FileObject root = null;
+ if (cFileObject != null) {
+ root = cFileObject;
+ for (String pathElement : ElementUtilities.getBinaryName(clazz).split("\\.")) {
+ root = root.getParent();
+ }
+ }
+ if (root != null) {
+ final FileObject rootFin = root;
+ IndexingUtils.runAsScanWork(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ JavaBinaryIndexer.preBuildArgs(rootFin,cFileObject);
+ } catch (IOException ioe) {
+ Exceptions.printStackTrace(ioe);
+ }
+ }
+ });
+ }
+ }
+ } finally {
+ jfm.handleOption(OPTION_OUTPUT_ROOT, Collections.singletonList("").iterator()); //NOI18N
+ }
+ }
+ }
+ return true;
+ } finally {
+ jc.skipAnnotationProcessing = oldSkipAPT;
+ log.nerrors = 0;
+ for (Map.Entry<ClassSymbol, StringBuilder> e : couplingErrors.entrySet()) {
+ dumpCouplingAbort(new CouplingAbort(e.getKey(), null), e.getValue().toString());
+ }
+ couplingErrors = oldCouplingErrors;
+ }
+ }
+ } catch (IOException ex) {
+ Exceptions.printStackTrace(ex);
+ }
+ }
+ return false;
+ } finally {
+ dettachFrom(Thread.currentThread());
+ }
+ }
+
+ @Override
+ public boolean loadParamNames(ClassSymbol clazz) {
+ boolean contended = !attachTo(Thread.currentThread());
+ try {
+ assert DISABLE_CONFINEMENT_TEST || JavaSourceAccessor.getINSTANCE().isJavaCompilerLocked() || !contended;
+ if (clazz != null) {
+ JavadocHelper.TextStream page = JavadocHelper.getJavadoc(clazz, ALWAYS_ALLOW_JDOC_ARG_NAMES, null);
+ if (page != null && (!page.isRemote() || !EventQueue.isDispatchThread())) {
+ if (getParamNamesFromJavadocText(page, clazz)) {
+ return true;
+ }
+ }
+ if (!DISABLE_ARTIFICAL_PARAMETER_NAMES) {
+ fillArtificalParamNames(clazz);
+ return true;
+ }
+ }
+
+ return false;
+ } finally {
+ dettachFrom(Thread.currentThread());
+ }
+ }
+
+ @Override
+ public void couplingError(ClassSymbol clazz, Tree t) {
+ if (this.partialReparse) {
+ super.couplingError(clazz, t);
+ }
+ if (clazz != null && couplingErrors != null) {
+ StringBuilder sb = couplingErrors.get(clazz);
+ if (sb != null)
+ sb.append(getTreeInfo(t));
+ else
+ couplingErrors.put(clazz, getTreeInfo(t));
+ } else {
+ dumpCouplingAbort(new CouplingAbort(clazz, t), null);
+ }
+ }
+
+ @Override
+ public void updateContext(Context context) {
+ this.context = context;
+ }
+
+ public final void startPartialReparse () {
+ this.partialReparse = true;
+ }
+
+ public final void endPartialReparse () {
+ this.partialReparse = false;
+ }
+
+ public static boolean pruneTree(final JCTree tree, final Symtab syms, final HashMap<ClassSymbol, JCClassDecl> syms2trees) {
+ final AtomicBoolean ret = new AtomicBoolean(true);
+ new TreeScanner() {
+ @Override
+ public void visitMethodDef(JCMethodDecl tree) {
+ super.visitMethodDef(tree);
+ if (tree.sym == null || tree.type == null || tree.type == syms.unknownType)
+ ret.set(false);
+ tree.body = null;
+ }
+ @Override
+ public void visitVarDef(JCVariableDecl tree) {
+ super.visitVarDef(tree);
+ if (tree.sym == null || tree.type == null || tree.type == syms.unknownType)
+ ret.set(false);
+ tree.init = null;
+ }
+ @Override
+ public void visitClassDef(JCClassDecl tree) {
+ scan(tree.mods);
+ scan(tree.typarams);
+ scan(tree.extending);
+ scan(tree.implementing);
+ if (tree.defs != null) {
+ List<JCTree> prev = null;
+ for (List<JCTree> l = tree.defs; l.nonEmpty(); l = l.tail) {
+ scan(l.head);
+ if (l.head.getTag() == JCTree.Tag.BLOCK) {
+ if (prev != null)
+ prev.tail = l.tail;
+ else
+ tree.defs = l.tail;
+ }
+ prev = l;
+ }
+ }
+ if (tree.sym == null || tree.type == null || tree.type == syms.unknownType) {
+ ret.set(false);
+ } else if (syms2trees != null) {
+ syms2trees.put(tree.sym, tree);
+ }
+ }
+ @Override
+ public void visitModuleDef(JCTree.JCModuleDecl tree) {
+ ret.set(false);
+ super.visitModuleDef(tree);
+ }
+ }.scan(tree);
+ return ret.get();
+ }
+
+ public static void dumpSymFile(
+ @NonNull final JavaFileManager jfm,
+ @NonNull final JavacTaskImpl jti,
+ @NonNull final ClassSymbol clazz,
+ @NonNull final File classFolder,
+ @NonNull final HashMap<ClassSymbol, JCClassDecl> syms2trees) throws IOException {
+ jfm.handleOption(OPTION_OUTPUT_ROOT, Collections.singletonList(classFolder.getPath()).iterator()); //NOI18N
+ try {
+ dumpSymFile(jti, clazz, syms2trees);
+ } finally {
+ jfm.handleOption(OPTION_OUTPUT_ROOT, Collections.singletonList("").iterator()); //NOI18N
+ }
+ }
+
+ @CheckForNull
+ private static File getClassFolder(
+ JavaFileManager jfm,
+ ClassSymbol clazz) throws IOException {
+ String binaryName = null;
+ String surl = null;
+ if (clazz.classfile != null) {
+ binaryName = jfm.inferBinaryName(StandardLocation.PLATFORM_CLASS_PATH, clazz.classfile);
+ if (binaryName == null)
+ binaryName = jfm.inferBinaryName(StandardLocation.CLASS_PATH, clazz.classfile);
+ surl = clazz.classfile.toUri().toURL().toExternalForm();
+ } else if (clazz.sourcefile != null) {
+ binaryName = jfm.inferBinaryName(StandardLocation.SOURCE_PATH, clazz.sourcefile);
+ surl = clazz.sourcefile.toUri().toURL().toExternalForm();
+ }
+ if (binaryName == null || surl == null) {
+ return null;
+ }
+ int index = surl.lastIndexOf(FileObjects.convertPackage2Folder(binaryName));
+ if (index > 0) {
+ return JavaIndex.getClassFolder(new URL(surl.substring(0, index)));
+ } else {
+ LOGGER.log(
+ Level.INFO,
+ "Invalid binary name when writing sym file for class: {0}, source: {1}, binary name {2}", // NOI18N
+ new Object[] {
+ clazz.flatname,
+ surl,
+ binaryName
+ });
+ return null;
+ }
+ }
+
+ private static void dumpSymFile(
+ @NonNull final JavacTaskImpl jti,
+ @NonNull final ClassSymbol clazz,
+ @NonNull final HashMap<ClassSymbol, JCClassDecl> syms2trees) throws IOException {
+ Log log = Log.instance(jti.getContext());
+ JavaCompiler compiler = JavaCompiler.instance(jti.getContext());
+ JavaFileObject prevLogTo = log.useSource(null);
+ DiscardDiagnosticHandler discardDiagnosticHandler = new Log.DiscardDiagnosticHandler(log);
+ final TaskListener listener = new TaskListener() {
+ @Override
+ public void started(TaskEvent e) {
+ if (e != null && e.getKind() == TaskEvent.Kind.GENERATE) {
+ JCClassDecl tree = syms2trees.get((ClassSymbol)e.getTypeElement());
+ if (tree != null)
+ pruneTree(tree, Symtab.instance(jti.getContext()), null);
+ }
+ }
+ @Override
+ public void finished(TaskEvent e) {
+ }
+ };
+ boolean oldSkipAP = compiler.skipAnnotationProcessing;
+ try {
+ compiler.skipAnnotationProcessing = true;
+ jti.addTaskListener(listener);
+ jti.generate(Collections.singletonList(clazz));
+ } catch (InvalidSourcePath isp) {
+ LOGGER.log(Level.INFO, "InvalidSourcePath reported when writing sym file for class: {0}", clazz.flatname); // NOI18N
+ } finally {
+ jti.removeTaskListener(listener);
+ compiler.skipAnnotationProcessing = oldSkipAP;
+ log.popDiagnosticHandler(discardDiagnosticHandler);
+ log.useSource(prevLogTo);
+ }
+ }
+
+ private static final int MAX_DUMPS = Integer.getInteger("org.netbeans.modules.java.source.parsing.JavacParser.maxDumps", 255);
+
+ public static void dumpCouplingAbort(CouplingAbort couplingAbort, String treeInfo) {
+ if (treeInfo == null)
+ treeInfo = getTreeInfo(couplingAbort.getTree()).toString();
+ String dumpDir = System.getProperty("netbeans.user") + "/var/log/"; //NOI18N
+ JavaFileObject classFile = couplingAbort.getClassFile();
+ String cfURI = classFile != null ? classFile.toUri().toASCIIString() : "<unknown>"; //NOI18N
+ JavaFileObject sourceFile = couplingAbort.getSourceFile();
+ String sfURI = sourceFile != null ? sourceFile.toUri().toASCIIString() : "<unknown>"; //NOI18N
+ String origName = classFile != null ? classFile.getName() : "unknown"; //NOI18N
+ File f = new File(dumpDir + origName + ".dump"); // NOI18N
+ boolean dumpSucceeded = false;
+ int i = 1;
+ while (i < MAX_DUMPS) {
+ if (!f.exists())
+ break;
+ f = new File(dumpDir + origName + '_' + i + ".dump"); // NOI18N
+ i++;
+ }
+ if (!f.exists()) {
+ try {
+ f.getParentFile().mkdirs();
+ OutputStream os = new FileOutputStream(f);
+ PrintWriter writer = new PrintWriter(new OutputStreamWriter(os, "UTF-8")); // NOI18N
+ try {
+ writer.println("Coupling error:"); //NOI18N
+ writer.println(String.format("class file %s", cfURI)); //NOI18N
+ writer.println(String.format("source file %s", sfURI)); //NOI18N
+ writer.println("----- Source file content: ----------------------------------------"); // NOI18N
+ if (sourceFile != null) {
+ try {
+ writer.println(sourceFile.getCharContent(true));
+ } catch (UnsupportedOperationException uoe) {
+ writer.println("<unknown>"); //NOI18N
+ }
+ } else {
+ writer.println("<unknown>"); //NOI18N
+ }
+ writer.print("----- Trees: -------------------------------------------------------"); // NOI18N
+ writer.println(treeInfo);
+ writer.println("----- Stack trace: ---------------------------------------------"); // NOI18N
+ couplingAbort.printStackTrace(writer);
+ } finally {
+ writer.close();
+ dumpSucceeded = true;
+ }
+ } catch (IOException ioe) {
+ LOGGER.log(Level.INFO, "Error when writing coupling error dump file!", ioe); // NOI18N
+ }
+ }
+ LOGGER.log(Level.WARNING, "Coupling error:\nclass file: {0}\nsource file: {1}{2}\n", new Object[] {cfURI, sfURI, treeInfo}); //NOI18N
+ if (!dumpSucceeded) {
+ LOGGER.log(Level.WARNING,
+ "Dump could not be written. Either dump file could not " + // NOI18N
+ "be created or all dump files were already used. Please " + // NOI18N
+ "check that you have write permission to '" + dumpDir + "' and " + // NOI18N
+ "clean all *.dump files in that directory."); // NOI18N
+ }
+ }
+
+ private static StringBuilder getTreeInfo(Tree t) {
+ StringBuilder info = new StringBuilder("\n"); //NOI18N
+ if (t != null) {
+ switch (t.getKind()) {
+ case ANNOTATION_TYPE:
+ case CLASS:
+ case ENUM:
+ case INTERFACE:
+ info.append("CLASS: ").append(((ClassTree) t).getSimpleName().toString()); //NOI18N
+ break;
+ case VARIABLE:
+ info.append("VARIABLE: ").append(((VariableTree) t).getName().toString()); //NOI18N
+ break;
+ case METHOD:
+ info.append("METHOD: ").append(((MethodTree) t).getName().toString()); //NOI18N
+ break;
+ case TYPE_PARAMETER:
+ info.append("TYPE_PARAMETER: ").append(((TypeParameterTree) t).getName().toString()); //NOI18N
+ break;
+ default:
+ info.append("TREE: <unknown>"); //NOI18N
+ break;
+ }
+ }
+ return info;
+ }
+
+ private boolean getParamNamesFromJavadocText(final JavadocHelper.TextStream page, final ClassSymbol clazz) {
+ HTMLEditorKit.Parser parser;
+ InputStream is = null;
+ String charset = null;
+ for (;;) {
+ try{
+ is = page.openStream();
+ Reader reader = charset == null ? new InputStreamReader(is): new InputStreamReader(is, charset);
+ parser = new ParserDelegator();
+ parser.parse(reader, new ParserCallback() {
+
+ private int state = 0; //init
+ private String signature = null;
+ private StringBuilder sb = null;
+
+ @Override
+ public void handleStartTag(HTML.Tag t, MutableAttributeSet a, int pos) {
+ if (t == HTML.Tag.A) {
+ String attrName = (String)a.getAttribute(HTML.Attribute.NAME);
+ if (attrName != null && ctor_summary_name.matcher(attrName).matches()) {
+ // we have found desired javadoc constructor info anchor
+ state = 10; //ctos open
+ } else if (attrName != null && method_summary_name.matcher(attrName).matches()) {
+ // we have found desired javadoc method info anchor
+ state = 20; //methods open
+ } else if (attrName != null && field_detail_name.matcher(attrName).matches()) {
+ state = 30; //end
+ } else if (attrName != null && ctor_detail_name.matcher(attrName).matches()) {
+ state = 30; //end
+ } else if (attrName != null && method_detail_name.matcher(attrName).matches()) {
+ state = 30; //end
+ } else if (state == 12 || state == 22) {
+ String attrHref = (String)a.getAttribute(HTML.Attribute.HREF);
+ if (attrHref != null) {
+ int idx = attrHref.indexOf('#');
+ if (idx >= 0) {
+ signature = attrHref.substring(idx + 1);
+ sb = new StringBuilder();
+ }
+ }
+ }
+ } else if (t == HTML.Tag.TABLE) {
+ if (state == 10 || state == 20)
+ state++;
+ } else if (t == HTML.Tag.CODE) {
+ if (state == 11 || state == 21)
+ state++;
+ } else if (t == HTML.Tag.DIV && a.containsAttribute(HTML.Attribute.CLASS, "block")) { //NOI18N
+ if (state == 11 && signature != null && sb != null) {
+ setParamNames(signature, sb.toString().trim(), true);
+ signature = null;
+ sb = null;
+ } else if (state == 21 && signature != null && sb != null) {
+ setParamNames(signature, sb.toString().trim(), false);
+ signature = null;
+ sb = null;
+ }
+ }
+ }
+
+ @Override
+ public void handleEndTag(Tag t, int pos) {
+ if (t == HTML.Tag.CODE) {
+ if (state == 12 || state == 22)
+ state--;
+ } else if (t == HTML.Tag.TABLE) {
+ if (state == 11 || state == 21)
+ state--;
+ }
+ }
+
+ @Override
+ public void handleText(char[] data, int pos) {
+ if (signature != null && sb != null && (state == 12 || state == 22))
+ sb.append(data);
+ }
+
+ @Override
+ public void handleSimpleTag(Tag t, MutableAttributeSet a, int pos) {
+ if (t == HTML.Tag.BR) {
+ if (state == 11 && signature != null && sb != null) {
+ setParamNames(signature, sb.toString().trim(), true);
+ signature = null;
+ sb = null;
+ } else if (state == 21 && signature != null && sb != null) {
+ setParamNames(signature, sb.toString().trim(), false);
+ signature = null;
+ sb = null;
+ }
+ }
+ }
+
+ private void setParamNames(String signature, String names, boolean isCtor) {
+ ArrayList<String> paramTypes = new ArrayList<String>();
+ int idx = -1;
+ for(int i = 0; i < signature.length(); i++) {
+ switch(signature.charAt(i)) {
+ case '-':
+ case '(':
+ case ')':
+ case ',':
+ if (idx > -1 && idx < i - 1) {
+ String typeName = signature.substring(idx + 1, i).trim();
+ if (typeName.endsWith("...")) //NOI18N
+ typeName = typeName.substring(0, typeName.length() - 3) + "[]"; //NOI18N
+ paramTypes.add(typeName);
+ }
+ idx = i;
+ break;
+ }
+ }
+ String methodName = null;
+ ArrayList<String> paramNames = new ArrayList<String>();
+ idx = -1;
+ for(int i = 0; i < names.length(); i++) {
+ switch(names.charAt(i)) {
+ case '(':
+ methodName = names.substring(0, i);
+ break;
+ case ')':
+ case ',':
+ if (idx > -1) {
+ paramNames.add(names.substring(idx + 1, i));
+ idx = -1;
+ }
+ break;
+ case 160: //
+ idx = i;
+ break;
+ }
+ }
+ assert methodName != null : "Null methodName. Signature: [" + signature + "], Names: [" + names + "]";
+ assert paramTypes.size() == paramNames.size() : "Inconsistent param types/names. Signature: [" + signature + "], Names: [" + names + "]";
+ if (paramNames.size() > 0) {
+ for (Symbol s : clazz.members().getSymbolsByName(isCtor
+ ? clazz.name.table.names.init
+ : clazz.name.table.fromString(methodName))) {
+ if (s.kind == Kinds.Kind.MTH && s.owner == clazz) {
+ MethodSymbol sym = (MethodSymbol)s;
+ List<VarSymbol> params = sym.params;
+ if (checkParamTypes(params, paramTypes)) {
+ for (String name : paramNames) {
+ params.head.setName(clazz.name.table.fromString(name));
+ params = params.tail;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ private boolean checkParamTypes(List<VarSymbol> params, ArrayList<String> paramTypes) {
+ Types types = Types.instance(context);
+ for (String typeName : paramTypes) {
+ if (params.isEmpty())
+ return false;
+ Type type = params.head.type;
+ if (type.isParameterized())
+ type = types.erasure(type);
+ if (!typeName.equals(type.toString()))
+ return false;
+ params = params.tail;
+ }
+ return params.isEmpty();
+ }
+ }, charset != null);
+ return true;
+ } catch (ChangedCharSetException e) {
+ if (charset == null) {
+ charset = JavadocHelper.getCharSet(e);
+ //restart with valid charset
+ } else {
+ e.printStackTrace();
+ break;
+ }
+ } catch (InterruptedIOException x) {
+ //Http javadoc timeout
+ break;
+ } catch(IOException ioe){
+ ioe.printStackTrace();
+ break;
+ }finally{
+ parser = null;
+ if (is!=null) {
+ try{
+ is.close();
+ }catch(IOException ioe){
+ ioe.printStackTrace();
+ }
+ }
+ }
+ }
+ return false;
+ }
+
+ private void fillArtificalParamNames(final ClassSymbol clazz) {
+ for (Symbol s : clazz.getEnclosedElements()) {
+ if (s instanceof MethodSymbol) {
+ MethodSymbol ms = (MethodSymbol) s;
+
+ if (ms.getParameters().isEmpty()) {
+ continue;
+ }
+
+ Set<String> usedNames = new HashSet<String>();
+
+ for (VarSymbol vs : ms.getParameters()) {
+ String name = JavaSourceAccessor.getINSTANCE().generateReadableParameterName(vs.asType().toString(), usedNames);
+
+ vs.setName(clazz.name.table.fromString(name));
+ }
+ }
+ }
+ }
+
+ private synchronized boolean attachTo(final Thread thread) {
+ assert thread != null;
+ if (!checkContention) {
+ return true;
+ } else if (this.owner == null) {
+ assert this.depth == 0;
+ this.owner = thread;
+ this.depth++;
+ return true;
+ } else if (this.owner == thread) {
+ assert this.depth > 0;
+ this.depth++;
+ return true;
+ } else {
+ assert this.depth > 0;
+ return false;
+ }
+ }
+
+ private synchronized boolean dettachFrom(final Thread thread) {
+ assert thread != null;
+ if (!checkContention) {
+ return true;
+ } else if (this.owner == thread) {
+ assert depth > 0;
+ this.depth--;
+ if (this.depth == 0) {
+ this.owner = null;
+ }
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ private boolean canWrite(final ClasspathInfo cpInfo) {
+ final FileManagerTransaction fmTx = ClasspathInfoAccessor.getINSTANCE().getFileManagerTransaction(cpInfo);
+ assert fmTx != null;
+ return fmTx.canWrite();
+ }
+
+ @ServiceProvider(service=TreeLoaderRegistry.class)
+ public static final class TreeLoaderRegistryImpl implements TreeLoaderRegistry {
+
+ @Override
+ public void enhance(Context context, ClasspathInfo cpInfo, boolean detached) {
+ TreeLoader.preRegister(context, cpInfo, detached);
+ }
+
+ }
+
+ @ServiceProvider(service=DuplicateClassRegistry.class)
+ public static final class DuplicateClassRegistryImpl implements DuplicateClassRegistry {
+
+ @Override
+ public void enhance(Context context, FQN2Files fqn2Files) {
+ if (fqn2Files == null)
+ return;
+ context.put(DuplicateClassChecker.class, new DuplicateClassChecker() {
+ @Override
+ public boolean check(Name name, JavaFileObject jfo) {
+ return fqn2Files.check(name, jfo);
+ }
+ });
+ }
+
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-netbeans/blob/ffc0de5a/java.source.nbjavac/test/unit/src/org/netbeans/lib/nbjavac/services/CouplingTest.java
----------------------------------------------------------------------
diff --git a/java.source.nbjavac/test/unit/src/org/netbeans/lib/nbjavac/services/CouplingTest.java b/java.source.nbjavac/test/unit/src/org/netbeans/lib/nbjavac/services/CouplingTest.java
new file mode 100644
index 0000000..6658518
--- /dev/null
+++ b/java.source.nbjavac/test/unit/src/org/netbeans/lib/nbjavac/services/CouplingTest.java
@@ -0,0 +1,177 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.netbeans.lib.nbjavac.services;
+
+import com.sun.source.tree.ClassTree;
+import com.sun.source.tree.CompilationUnitTree;
+import org.netbeans.api.java.source.support.ErrorAwareTreePathScanner;
+import com.sun.source.util.Trees;
+import com.sun.tools.javac.api.JavacTaskImpl;
+import com.sun.tools.javac.code.Flags;
+import com.sun.tools.javac.code.Symbol.ClassSymbol;
+import com.sun.tools.javac.model.JavacElements;
+import java.io.File;
+import java.io.IOException;
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.TypeElement;
+import javax.tools.JavaCompiler;
+import javax.tools.JavaFileObject;
+import javax.tools.SimpleJavaFileObject;
+import javax.tools.StandardJavaFileManager;
+import javax.tools.StandardLocation;
+import javax.tools.ToolProvider;
+import junit.framework.TestCase;
+
+/**XXX: why is this passing?????
+ *
+ * @author lahvac
+ */
+public class CouplingTest extends TestCase {
+
+ public void test200122() throws Exception {
+ String code = "package test; public class Test { void t() { new Runnable() { public void run() {} }; } }";
+ List<String> fqns = compile(code);
+
+ assertEquals(testCoupling(code, false, fqns), testCoupling(code, true, fqns));
+ }
+
+ //<editor-fold defaultstate="collapsed" desc=" Test Infrastructure ">
+ private static class MyFileObject extends SimpleJavaFileObject {
+ private String text;
+
+ public MyFileObject(String text) {
+ super(URI.create("myfo:/Test.java"), JavaFileObject.Kind.SOURCE);
+ this.text = text;
+ }
+
+ @Override
+ public CharSequence getCharContent(boolean ignoreEncodingErrors) {
+ return text;
+ }
+ }
+
+ private File workingDir;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+
+ workingDir = File.createTempFile("CouplingTest", "");
+
+ workingDir.delete();
+ workingDir.mkdirs();
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ deleteRecursively(workingDir);
+ super.tearDown();
+ }
+
+ private List<String> compile(String code) throws Exception {
+ final String bootPath = System.getProperty("sun.boot.class.path"); //NOI18N
+ final String version = System.getProperty("java.vm.specification.version"); //NOI18N
+ final JavaCompiler tool = ToolProvider.getSystemJavaCompiler();
+ assert tool != null;
+
+ StandardJavaFileManager std = tool.getStandardFileManager(null, null, null);
+
+ std.setLocation(StandardLocation.CLASS_OUTPUT, Collections.singleton(workingDir));
+
+ final JavacTaskImpl ct = (JavacTaskImpl)tool.getTask(null, std, null, Arrays.asList("-bootclasspath", bootPath, "-source", version, "-Xjcov", "-XDshouldStopPolicy=GENERATE"), null, Arrays.asList(new MyFileObject(code)));
+ Iterable<? extends CompilationUnitTree> cuts = ct.parse();
+
+ ct.analyze();
+
+ final List<String> result = new ArrayList<String>();
+
+ new ErrorAwareTreePathScanner<Void, Void>() {
+ @Override public Void visitClass(ClassTree node, Void p) {
+ Element el = Trees.instance(ct).getElement(getCurrentPath());
+
+ if (el != null && (el.getKind().isClass() || el.getKind().isInterface())) {
+ result.add(ct.getElements().getBinaryName((TypeElement) el).toString());
+ }
+
+ return super.visitClass(node, p);
+ }
+ }.scan(cuts, null);
+
+ ct.generate();
+
+ return result;
+ }
+
+ private Set<String> testCoupling(String code, boolean loadFromClasses, List<String> fqns) throws IOException {
+ final JavaCompiler tool = ToolProvider.getSystemJavaCompiler();
+ assert tool != null;
+
+ StandardJavaFileManager std = tool.getStandardFileManager(null, null, null);
+
+ if (loadFromClasses) {
+ std.setLocation(StandardLocation.CLASS_OUTPUT, Collections.singleton(workingDir));
+ std.setLocation(StandardLocation.CLASS_PATH, Collections.singleton(workingDir));
+ }
+
+ JavacTaskImpl ct = Utilities.createJavac(std);
+ ct.enter();
+
+ if (loadFromClasses) {
+ for (String fqn : fqns) {
+ assertNotNull(fqn, ((JavacElements)ct.getElements()).getTypeElementByBinaryName(fqn));
+ }
+ }
+
+ ct.parse(Utilities.fileObjectFor(code));
+ ct.analyze();
+
+ Set<String> classInfo = new HashSet<String>();
+
+ for (String fqn : fqns) {
+ ClassSymbol clazz = ((JavacElements)ct.getElements()).getTypeElementByBinaryName(fqn);
+ StringBuilder info = new StringBuilder();
+
+ info.append(clazz.flatname.toString()).append(",");
+ info.append(Long.toHexString(clazz.flags() & ~(Flags.FROMCLASS | Flags.APT_CLEANED))).append(",");
+ info.append(clazz.hasOuterInstance());
+
+ classInfo.add(info.toString());
+ }
+
+ return classInfo;
+ }
+
+ private void deleteRecursively(File f) {
+ if (f.isDirectory()) {
+ for (File c : f.listFiles()) {
+ deleteRecursively(c);
+ }
+ }
+
+ f.delete();
+ }
+ //</editor-fold>
+}
http://git-wip-us.apache.org/repos/asf/incubator-netbeans/blob/ffc0de5a/java.source.nbjavac/test/unit/src/org/netbeans/modules/java/source/nbjavac/indexing/ClassNamesForFileOraculumImplTest.java
----------------------------------------------------------------------
diff --git a/java.source.nbjavac/test/unit/src/org/netbeans/modules/java/source/nbjavac/indexing/ClassNamesForFileOraculumImplTest.java b/java.source.nbjavac/test/unit/src/org/netbeans/modules/java/source/nbjavac/indexing/ClassNamesForFileOraculumImplTest.java
new file mode 100644
index 0000000..f5a226a
--- /dev/null
+++ b/java.source.nbjavac/test/unit/src/org/netbeans/modules/java/source/nbjavac/indexing/ClassNamesForFileOraculumImplTest.java
@@ -0,0 +1,73 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.netbeans.modules.java.source.nbjavac.indexing;
+
+import com.sun.tools.javac.api.ClassNamesForFileOraculum;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.Reader;
+import java.io.Writer;
+import java.net.URI;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import javax.lang.model.element.Modifier;
+import javax.lang.model.element.NestingKind;
+import javax.tools.JavaFileObject;
+import javax.tools.SimpleJavaFileObject;
+import org.junit.Test;
+import static org.junit.Assert.*;
+
+/**
+ *
+ * @author lahvac
+ */
+public class ClassNamesForFileOraculumImplTest {
+
+ public ClassNamesForFileOraculumImplTest() {
+ }
+
+ @Test
+ public void testDivineSources() {
+ TestJavaFileObject fo1 = new TestJavaFileObject();
+ TestJavaFileObject fo2 = new TestJavaFileObject();
+ Map<JavaFileObject, List<String>> fo2FQNs = new HashMap<JavaFileObject, List<String>>();
+
+ fo2FQNs.put(fo1, Arrays.asList("a.b.c.Class1"));
+ fo2FQNs.put(fo2, Arrays.asList("e.f.g"));
+
+ ClassNamesForFileOraculum oraculum = new ClassNamesForFileOraculumImpl(fo2FQNs);
+
+ assertArrayEquals(new JavaFileObject[] {fo1}, oraculum.divineSources("a.b.c"));
+ assertNull(oraculum.divineSources("a.b"));
+ assertNull(oraculum.divineSources("e.f.g"));
+ }
+
+ private static final class TestJavaFileObject extends SimpleJavaFileObject {
+
+ public TestJavaFileObject() {
+ super(URI.create("test://test.java"), Kind.SOURCE);
+ }
+
+ }
+
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-netbeans/blob/ffc0de5a/java.source.nbjavac/test/unit/src/org/netbeans/modules/java/source/nbjavac/indexing/MultiPassCompileWorkerTest.java
----------------------------------------------------------------------
diff --git a/java.source.nbjavac/test/unit/src/org/netbeans/modules/java/source/nbjavac/indexing/MultiPassCompileWorkerTest.java b/java.source.nbjavac/test/unit/src/org/netbeans/modules/java/source/nbjavac/indexing/MultiPassCompileWorkerTest.java
new file mode 100644
index 0000000..874079c
--- /dev/null
+++ b/java.source.nbjavac/test/unit/src/org/netbeans/modules/java/source/nbjavac/indexing/MultiPassCompileWorkerTest.java
@@ -0,0 +1,44 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.netbeans.modules.java.source.nbjavac.indexing;
+
+import java.util.Collection;
+import org.netbeans.modules.java.source.indexing.CompileWorker.ParsingOutput;
+import org.netbeans.modules.java.source.indexing.JavaCustomIndexer;
+import org.netbeans.modules.java.source.indexing.JavaCustomIndexer.CompileTuple;
+import org.netbeans.modules.java.source.indexing.JavaParsingContext;
+import org.netbeans.modules.parsing.spi.indexing.Context;
+
+/**
+ *
+ * @author lahvac
+ */
+public class MultiPassCompileWorkerTest extends NBJavacCompileWorkerTestBase {
+
+ public MultiPassCompileWorkerTest(String name) {
+ super(name);
+ }
+
+ @Override
+ protected ParsingOutput runCompileWorker(Context context, JavaParsingContext javaContext, Collection<? extends CompileTuple> files) throws Exception {
+ JavaCustomIndexer.NO_ONE_PASS_COMPILE_WORKER = true;
+ ParsingOutput fromOnePass = new OnePassCompileWorker().compile(null, context, javaContext, files);
+ return new MultiPassCompileWorker().compile(fromOnePass, context, javaContext, files);
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-netbeans/blob/ffc0de5a/java.source.nbjavac/test/unit/src/org/netbeans/modules/java/source/nbjavac/indexing/NBJavacCompileWorkerTestBase.java
----------------------------------------------------------------------
diff --git a/java.source.nbjavac/test/unit/src/org/netbeans/modules/java/source/nbjavac/indexing/NBJavacCompileWorkerTestBase.java b/java.source.nbjavac/test/unit/src/org/netbeans/modules/java/source/nbjavac/indexing/NBJavacCompileWorkerTestBase.java
new file mode 100644
index 0000000..09e57ae
--- /dev/null
+++ b/java.source.nbjavac/test/unit/src/org/netbeans/modules/java/source/nbjavac/indexing/NBJavacCompileWorkerTestBase.java
@@ -0,0 +1,55 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.netbeans.modules.java.source.nbjavac.indexing;
+
+import java.io.File;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+import org.netbeans.modules.java.source.indexing.CompileWorker.ParsingOutput;
+import org.netbeans.modules.java.source.indexing.CompileWorkerTestBase;
+
+/**
+ *
+ * @author lahvac
+ */
+public abstract class NBJavacCompileWorkerTestBase extends CompileWorkerTestBase {
+
+ public NBJavacCompileWorkerTestBase(String name) {
+ super(name);
+ }
+
+ public void test219787() throws Exception {
+ ParsingOutput result = runIndexing(Arrays.asList(compileTuple("test/Test3.java", "package test; public class Test3")),
+ Arrays.asList(virtualCompileTuple("test/Test1.virtual", "package test; public class Test1 {}"),
+ virtualCompileTuple("test/Test2.virtual", "package test; public class Test2 {}")));
+
+ assertFalse(result.lowMemory);
+ assertTrue(result.success);
+
+ Set<String> createdFiles = new HashSet<String>();
+
+ for (File created : result.createdFiles) {
+ createdFiles.add(getWorkDir().toURI().relativize(created.toURI()).getPath());
+ }
+
+ assertEquals(new HashSet<String>(Arrays.asList("cache/s1/java/15/classes/test/Test3.sig")), createdFiles);
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-netbeans/blob/ffc0de5a/java.source.nbjavac/test/unit/src/org/netbeans/modules/java/source/nbjavac/indexing/OnePassCompileWorkerTest.java
----------------------------------------------------------------------
diff --git a/java.source.nbjavac/test/unit/src/org/netbeans/modules/java/source/nbjavac/indexing/OnePassCompileWorkerTest.java b/java.source.nbjavac/test/unit/src/org/netbeans/modules/java/source/nbjavac/indexing/OnePassCompileWorkerTest.java
new file mode 100644
index 0000000..743e114
--- /dev/null
+++ b/java.source.nbjavac/test/unit/src/org/netbeans/modules/java/source/nbjavac/indexing/OnePassCompileWorkerTest.java
@@ -0,0 +1,42 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.netbeans.modules.java.source.nbjavac.indexing;
+
+import java.util.Collection;
+import org.netbeans.modules.java.source.indexing.CompileWorker.ParsingOutput;
+import org.netbeans.modules.java.source.indexing.JavaCustomIndexer.CompileTuple;
+import org.netbeans.modules.java.source.indexing.JavaParsingContext;
+import org.netbeans.modules.parsing.spi.indexing.Context;
+
+/**
+ *
+ * @author lahvac
+ */
+public class OnePassCompileWorkerTest extends NBJavacCompileWorkerTestBase {
+
+ public OnePassCompileWorkerTest(String name) {
+ super(name);
+ }
+
+ @Override
+ protected ParsingOutput runCompileWorker(Context context, JavaParsingContext javaContext, Collection<? extends CompileTuple> files) {
+ return new OnePassCompileWorker().compile(null, context, javaContext, files);
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-netbeans/blob/ffc0de5a/java.source.nbjavac/test/unit/src/org/netbeans/modules/java/source/nbjavac/indexing/SuperOnePassCompileWorkerTest.java
----------------------------------------------------------------------
diff --git a/java.source.nbjavac/test/unit/src/org/netbeans/modules/java/source/nbjavac/indexing/SuperOnePassCompileWorkerTest.java b/java.source.nbjavac/test/unit/src/org/netbeans/modules/java/source/nbjavac/indexing/SuperOnePassCompileWorkerTest.java
new file mode 100644
index 0000000..093b0b0
--- /dev/null
+++ b/java.source.nbjavac/test/unit/src/org/netbeans/modules/java/source/nbjavac/indexing/SuperOnePassCompileWorkerTest.java
@@ -0,0 +1,42 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.netbeans.modules.java.source.nbjavac.indexing;
+
+import java.util.Collection;
+import org.netbeans.modules.java.source.indexing.CompileWorker.ParsingOutput;
+import org.netbeans.modules.java.source.indexing.JavaCustomIndexer.CompileTuple;
+import org.netbeans.modules.java.source.indexing.JavaParsingContext;
+import org.netbeans.modules.parsing.spi.indexing.Context;
+
+/**
+ *
+ * @author lahvac
+ */
+public class SuperOnePassCompileWorkerTest extends NBJavacCompileWorkerTestBase {
+
+ public SuperOnePassCompileWorkerTest(String name) {
+ super(name);
+ }
+
+ @Override
+ protected ParsingOutput runCompileWorker(Context context, JavaParsingContext javaContext, Collection<? extends CompileTuple> files) {
+ return new SuperOnePassCompileWorker().compile(null, context, javaContext, files);
+ }
+
+}