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: //&nbsp;
+                                    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);
+    }
+    
+}