Commits

Jan Lahoda  committed c97eab5 Merge

Merging recent trunk/default changes to marks branch

  • Participants
  • Parent commits a7da9dd, 1f0eb21
  • Branches marks

Comments (0)

Files changed (98)

File api/nbproject/genfiles.properties

 build.xml.stylesheet.CRC32=79c3b980@1.28.0.7
 # This file is used by a NetBeans-based IDE to track changes in generated files such as build-impl.xml.
 # Do not edit this file. You may delete it but then the IDE will never regenerate such files for you.
-nbproject/build-impl.xml.data.CRC32=d6395e19
+nbproject/build-impl.xml.data.CRC32=a7e51eed
 nbproject/build-impl.xml.script.CRC32=c4574e66
 nbproject/build-impl.xml.stylesheet.CRC32=238281d1@1.44

File api/nbproject/project.xml

                     <build-prerequisite/>
                     <compile-dependency/>
                     <run-dependency>
-                        <release-version>1</release-version>
-                        <specification-version>2.6.0.232</specification-version>
+                        <release-version>1-3</release-version>
+                        <specification-version>2.13</specification-version>
                     </run-dependency>
                 </dependency>
                 <dependency>

File api/src/org/netbeans/modules/jackpot30/impl/Utilities.java

         return parseAndAttribute(null, jti, pattern, null, errors);
     }
 
+    public static Tree parseAndAttribute(JavacTaskImpl jti, String pattern, SourcePositions[] sourcePositions, Collection<Diagnostic<? extends JavaFileObject>> errors) {
+        return parseAndAttribute(null, jti, pattern, null, sourcePositions, errors);
+    }
+
     private static Tree parseAndAttribute(CompilationInfo info, JavacTaskImpl jti, String pattern, Scope scope, Collection<Diagnostic<? extends JavaFileObject>> errors) {
         return parseAndAttribute(info, jti, pattern, scope, new SourcePositions[1], errors);
     }
             return super.switchBlockStatementGroup();
         }
 
+
+        @Override
+        protected JCTree resource() {
+            if (S.token() == Token.IDENTIFIER && S.stringVal().startsWith("$")) {
+                //XXX: should inspect the next token, not next character:
+                char[] maybeSemicolon = S.getRawCharacters(S.endPos(), S.endPos() + 1);
+
+                if (maybeSemicolon[0] == ';' || maybeSemicolon[0] == ')') {
+                    int pos = S.pos();
+                    com.sun.tools.javac.util.Name name = S.name();
+
+                    S.nextToken();
+
+                    return F.at(pos).Ident(name);
+                }
+            }
+            return super.resource();
+        }
+
     }
 
     private static final class PushbackLexer implements Lexer {

File api/src/org/netbeans/modules/jackpot30/impl/batch/BatchSearch.java

             private Set<FileObject> KNOWN_SOURCE_ROOTS = new HashSet<FileObject>(GlobalPathRegistry.getDefault().getSourceRoots());
             public Index findIndex(FileObject root, ProgressHandleWrapper progress) {
                 progress.startNextPart(1);
-                if (KNOWN_SOURCE_ROOTS.contains(root)) {
+                if (KNOWN_SOURCE_ROOTS.contains(root) || scope.forceIndicesUpToDate) {
                     try {
                         return FileBasedIndex.get(root.getURL());
                     } catch (IOException ex) {
     }
     
     public static void getVerifiedSpans(BatchResult candidates, @NonNull ProgressHandleWrapper progress, final VerifiedSpansCallBack callback, final Collection<? super MessageImpl> problems) {
+        getVerifiedSpans(candidates, progress, callback, false, problems);
+    }
+
+    public static void getVerifiedSpans(BatchResult candidates, @NonNull ProgressHandleWrapper progress, final VerifiedSpansCallBack callback, boolean doNotRegisterClassPath, final Collection<? super MessageImpl> problems) {
         int[] parts = new int[candidates.projectId2Resources.size()];
         int   index = 0;
 
         for (Collection<? extends Resource> it :candidates.projectId2Resources.values()) {
             inner.startNextPart(it.size());
 
-            getVerifiedSpans(it, inner, callback, problems);
+            getVerifiedSpans(it, inner, callback, doNotRegisterClassPath, problems);
         }
     }
 
-    private static void getVerifiedSpans(Collection<? extends Resource> resources, @NonNull final ProgressHandleWrapper progress, final VerifiedSpansCallBack callback, final Collection<? super MessageImpl> problems) {
+    private static void getVerifiedSpans(Collection<? extends Resource> resources, @NonNull final ProgressHandleWrapper progress, final VerifiedSpansCallBack callback, boolean doNotRegisterClassPath, final Collection<? super MessageImpl> problems) {
         Collection<FileObject> files = new LinkedList<FileObject>();
         final Map<FileObject, Resource> file2Resource = new HashMap<FileObject, Resource>();
 
         }
 
         Map<ClasspathInfo, Collection<FileObject>> cp2Files = BatchUtilities.sortFiles(files);
-        Set<ClassPath> toRegisterSet = new HashSet<ClassPath>();
+        ClassPath[] toRegister = null;
 
-        for (ClasspathInfo cpInfo : cp2Files.keySet()) {
-            toRegisterSet.add(cpInfo.getClassPath(PathKind.SOURCE));
-        }
+        if (!doNotRegisterClassPath) {
+            Set<ClassPath> toRegisterSet = new HashSet<ClassPath>();
 
-        ClassPath[] toRegister = !toRegisterSet.isEmpty() ? toRegisterSet.toArray(new ClassPath[0]) : null;
+            for (ClasspathInfo cpInfo : cp2Files.keySet()) {
+                toRegisterSet.add(cpInfo.getClassPath(PathKind.SOURCE));
+            }
 
-        if (toRegister != null) {
-            GlobalPathRegistry.getDefault().register(ClassPath.SOURCE, toRegister);
-            try {
-                Utilities.waitScanFinished();
-            } catch (InterruptedException ex) {
-                Exceptions.printStackTrace(ex);
+            toRegister = !toRegisterSet.isEmpty() ? toRegisterSet.toArray(new ClassPath[0]) : null;
+
+            if (toRegister != null) {
+                GlobalPathRegistry.getDefault().register(ClassPath.SOURCE, toRegister);
+                try {
+                    Utilities.waitScanFinished();
+                } catch (InterruptedException ex) {
+                    Exceptions.printStackTrace(ex);
+                }
             }
         }
 
                                 if (parameter.toPhase(Phase.PARSED).compareTo(Phase.PARSED) < 0)
                                     return ;
 
-                                Context ctx = JavaSourceAccessor.getINSTANCE().getJavacTask(parameter).getContext();
-                                ClassReader reader = ClassReader.instance(ctx);
-                                Field attributeReaders = ClassReader.class.getDeclaredField("attributeReaders");
+                                boolean cont = true;
 
-                                attributeReaders.setAccessible(true);
-                                ((Map) attributeReaders.get(reader)).remove(Names.instance(ctx)._org_netbeans_ParameterNames);
-                                //workaround for #192481 end
+                                try {
+                                    Context ctx = JavaSourceAccessor.getINSTANCE().getJavacTask(parameter).getContext();
+                                    ClassReader reader = ClassReader.instance(ctx);
+                                    Field attributeReaders = ClassReader.class.getDeclaredField("attributeReaders");
 
-                                if (parameter.toPhase(Phase.RESOLVED).compareTo(Phase.RESOLVED) < 0)
-                                    return ;
+                                    attributeReaders.setAccessible(true);
+                                    ((Map) attributeReaders.get(reader)).remove(Names.instance(ctx)._org_netbeans_ParameterNames);
+                                    //workaround for #192481 end
 
-                                progress.setMessage("processing: " + FileUtil.getFileDisplayName(parameter.getFileObject()));
-                                Resource r = file2Resource.get(parameter.getFileObject());
-                                Map<PatternDescription, List<HintDescription>> sortedHintsPatterns = new HashMap<PatternDescription, List<HintDescription>>();
-                                Map<Kind, List<HintDescription>> sortedHintsKinds = new HashMap<Kind, List<HintDescription>>();
+                                    if (parameter.toPhase(Phase.RESOLVED).compareTo(Phase.RESOLVED) < 0)
+                                        return ;
 
-                                RulesManager.sortOut(r.hints, sortedHintsKinds, sortedHintsPatterns);
+                                    progress.setMessage("processing: " + FileUtil.getFileDisplayName(parameter.getFileObject()));
+                                    Resource r = file2Resource.get(parameter.getFileObject());
+                                    Map<PatternDescription, List<HintDescription>> sortedHintsPatterns = new HashMap<PatternDescription, List<HintDescription>>();
+                                    Map<Kind, List<HintDescription>> sortedHintsKinds = new HashMap<Kind, List<HintDescription>>();
 
-                                List<ErrorDescription> hints = new HintsInvoker(parameter, new AtomicBoolean()).computeHints(parameter, sortedHintsKinds, sortedHintsPatterns, problems);
+                                    RulesManager.sortOut(r.hints, sortedHintsKinds, sortedHintsPatterns);
 
-                                if (callback.spansVerified(parameter, r, hints)) {
+                                    List<ErrorDescription> hints = new HintsInvoker(parameter, new AtomicBoolean()).computeHints(parameter, sortedHintsKinds, sortedHintsPatterns, problems);
+
+                                    cont = callback.spansVerified(parameter, r, hints);
+                                } catch (ThreadDeath td) {
+                                    throw td;
+                                } catch (Throwable t) {
+                                    LOG.log(Level.INFO, "Exception while performing batch processing in " + FileUtil.getFileDisplayName(parameter.getFileObject()), t);
+                                    problems.add(new MessageImpl(MessageKind.WARNING, "An exception occurred while processing file: " + FileUtil.getFileDisplayName(parameter.getFileObject()) + " (" + t.getLocalizedMessage() + ")."));
+                                }
+                                
+                                if (cont) {
                                     progress.tick();
                                     currentPointer.incrementAndGet();
                                 } else {
         public  final String subIndex; //public only for AddScopePanel
         public  final boolean update; //public only for AddScopePanel
         private final Collection<? extends FileObject> sourceRoots;
+        private final boolean forceIndicesUpToDate;
 
         private Scope() {
-            this(null, null, null, null, true, null);
+            this(null, null, null, null, true, null, false);
         }
 
-        private Scope(ScopeType scopeType, String folder, String indexURL, String subIndex, boolean update, Collection<? extends FileObject> sourceRoots) {
+        private Scope(ScopeType scopeType, String folder, String indexURL, String subIndex, boolean update, Collection<? extends FileObject> sourceRoots, boolean forceIndicesUpToDate) {
             this.scopeType = scopeType;
             this.folder = folder;
             this.indexURL = indexURL;
             this.subIndex = subIndex;
             this.update = update;
             this.sourceRoots = sourceRoots;
+            this.forceIndicesUpToDate = forceIndicesUpToDate;
         }
 
         public String serialize() {
         }
 
         public static Scope createAllOpenedProjectsScope() {
-            return new Scope(ScopeType.ALL_OPENED_PROJECTS, null, null, null, false, null);
+            return new Scope(ScopeType.ALL_OPENED_PROJECTS, null, null, null, false, null, false);
         }
 
         public static Scope createAllDependentOpenedSourceRoots(FileObject from) {
-            return new Scope(ScopeType.ALL_DEPENDENT_OPENED_SOURCE_ROOTS, null, null, null, false, Collections.singletonList(from));
+            return new Scope(ScopeType.ALL_DEPENDENT_OPENED_SOURCE_ROOTS, null, null, null, false, Collections.singletonList(from), false);
         }
 
         public static Scope createGivenFolderNoIndex(String folder) {
-            return new Scope(ScopeType.GIVEN_FOLDER, folder, null, null, false, null);
+            return new Scope(ScopeType.GIVEN_FOLDER, folder, null, null, false, null, false);
         }
 
         public static Scope createGivenFolderLocalIndex(String folder, File indexFolder, boolean update) {
-            return new Scope(ScopeType.GIVEN_FOLDER, folder, indexFolder.getAbsolutePath(), null, update, null);
+            return new Scope(ScopeType.GIVEN_FOLDER, folder, indexFolder.getAbsolutePath(), null, update, null, false);
         }
 
         public static Scope createGivenFolderRemoteIndex(String folder, String urlIndex, String subIndex) {
-            return new Scope(ScopeType.GIVEN_FOLDER, folder, urlIndex, subIndex, false, null);
+            return new Scope(ScopeType.GIVEN_FOLDER, folder, urlIndex, subIndex, false, null, false);
         }
 
         public static Scope createGivenSourceRoots(FileObject... sourceRoots) {
-            return new Scope(ScopeType.GIVEN_SOURCE_ROOTS, null, null, null, false, Arrays.asList(sourceRoots));
+            return createGivenSourceRoots(false, sourceRoots);
+        }
+
+        public static Scope createGivenSourceRoots(boolean forceIndicesUpToDate, FileObject... sourceRoots) {
+            return new Scope(ScopeType.GIVEN_SOURCE_ROOTS, null, null, null, false, Arrays.asList(sourceRoots), forceIndicesUpToDate);
         }
 
         public String getDisplayName() {

File api/src/org/netbeans/modules/jackpot30/impl/batch/BatchUtilities.java

 import com.sun.source.util.TreePath;
 import com.sun.tools.javac.api.JavacTaskImpl;
 import com.sun.tools.javac.util.Log;
-import java.io.ByteArrayInputStream;
-import java.io.File;
 import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
 import java.io.Reader;
 import java.io.StringReader;
+import java.io.Writer;
 import java.lang.reflect.Constructor;
 import java.lang.reflect.Method;
 import java.nio.charset.Charset;
 import java.util.logging.Logger;
 import javax.swing.text.Document;
 import org.netbeans.api.annotations.common.NonNull;
+import org.netbeans.api.annotations.common.NullAllowed;
 import org.netbeans.api.java.classpath.ClassPath;
 import org.netbeans.api.java.source.ClasspathInfo;
 import org.netbeans.api.java.source.CompilationController;
 //        }
     }
 
-    public static void exportDiff(ModificationResult result, OutputStream out) throws IOException {
+    public static void exportDiff(ModificationResult result, @NullAllowed FileObject relativeTo, Writer out) throws IOException {
         for (FileObject f : result.getModifiedFileObjects()) {
             Charset c = FileEncodingQuery.getEncoding(f);
             String orig = new String(f.asBytes(), c);
             if (orig.equals(nue)) {
                 continue;
             }
+
+            String name = relativeTo != null ? FileUtil.getRelativePath(relativeTo, f) : FileUtil.toFile(f).getAbsolutePath();
             
-            File jiFile = FileUtil.toFile(f);
-            
-            doExportDiff(jiFile.getAbsolutePath(), orig, nue, out);
+            doExportDiff(name, orig, nue, out);
         }
     }
 
     //copied from the diff module:
-    private static void doExportDiff(String name, String original, String modified, OutputStream out) throws IOException {
+    private static void doExportDiff(String name, String original, String modified, Writer out) throws IOException {
         DiffProvider diff = new BuiltInDiffProvider();//(DiffProvider) Lookup.getDefault().lookup(DiffProvider.class);
 
         Reader r1 = null;
         }
 
         try {
-            InputStream is;
             r1 = new StringReader(original);
             r2 = new StringReader(modified);
             TextDiffVisualizer.TextDiffInfo info = new TextDiffVisualizer.TextDiffInfo(
 //            } else {
 //                diffText = TextDiffVisualizer.differenceToNormalDiffText(info);
 //            }
-            is = new ByteArrayInputStream(diffText.getBytes("utf8"));  // NOI18N
-            while(true) {
-                int i = is.read();
-                if (i == -1) break;
-                out.write(i);
-            }
+            out.write(diffText);
         } finally {
             if (r1 != null) try { r1.close(); } catch (Exception e) {}
             if (r2 != null) try { r2.close(); } catch (Exception e) {}

File api/src/org/netbeans/modules/jackpot30/impl/duplicates/Bundle.properties

 CTL_GlobalFindDuplicates=Global Find Duplicates
+DuplicatesListPanel.findMore.text=<html><body><a href="">Look for More</a>
+DuplicatesListPanel.progressLabel.text=

File api/src/org/netbeans/modules/jackpot30/impl/duplicates/ComputeDuplicates.java

 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.BitSet;
-import java.util.Collection;
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
+import java.util.NoSuchElementException;
 import java.util.Set;
 import java.util.concurrent.atomic.AtomicBoolean;
 import org.apache.lucene.document.Document;
  */
 public class ComputeDuplicates {
 
-    public Collection<? extends DuplicateDescription> computeDuplicatesForAllOpenedProjects(ProgressHandle progress, AtomicBoolean cancel) throws IOException {
+    public Iterator<? extends DuplicateDescription> computeDuplicatesForAllOpenedProjects(ProgressHandle progress, AtomicBoolean cancel) throws IOException {
         Set<URL> urls = new HashSet<URL>();
 
         for (ClassPath cp : GlobalPathRegistry.getDefault().getPaths(ClassPath.SOURCE)) {
         }
     }
 
-    public Collection<? extends DuplicateDescription> computeDuplicates(Set<URL> forURLs, ProgressHandle progress, AtomicBoolean cancel) throws IOException {
+    public Iterator<? extends DuplicateDescription> computeDuplicates(Set<URL> forURLs, ProgressHandle progress, AtomicBoolean cancel) throws IOException {
         Map<IndexReader, FileObject> readers2Roots = new LinkedHashMap<IndexReader, FileObject>();
 
         progress.progress("Updating indices");
         //TODO: only show valuable duplicates?:
 //        dd = dd.subList(0, dd.size() / 10 + 1);
 
-        progress.switchToDeterminate(dd.size());
-        
-        List<DuplicateDescription> result = new LinkedList<DuplicateDescription>();
-        int done = 0;
+        return new DuplicatesIterator(readers2Roots, dd);
+    }
 
-        for (String longest : dd) {
+    private static final class DuplicatesIterator implements Iterator<DuplicateDescription> {
+        private final Map<IndexReader, FileObject> readers2Roots;
+        private final Iterator<String> duplicateCandidates;
+        private final List<DuplicateDescription> result = new LinkedList<DuplicateDescription>();
+
+        public DuplicatesIterator(Map<IndexReader, FileObject> readers2Roots, Iterable<String> duplicateCandidates) {
+            this.readers2Roots = readers2Roots;
+            this.duplicateCandidates = duplicateCandidates.iterator();
+        }
+
+        private DuplicateDescription nextDescription() throws IOException {
+        while (duplicateCandidates.hasNext()) {
+            String longest = duplicateCandidates.next();
             List<Span> foundDuplicates = new LinkedList<Span>();
 
             Query query = new TermQuery(new Term("generalized", longest));
 
             for (Entry<IndexReader, FileObject> e : readers2Roots.entrySet()) {
-                if (cancel.get()) return Collections.emptyList();
-
                 Searcher s = new IndexSearcher(e.getKey());
                 BitSet matchingDocuments = new BitSet(e.getKey().maxDoc());
                 Collector c = new BitSetCollector(matchingDocuments);
                     }
                 }
 
-                if (add)
+                if (add) {
                     result.add(current);
+                    return current;
+                }
             }
 
-            progress.progress(++done);
+        }
+        return null;
         }
 
-        progress.finish();
+        private DuplicateDescription next;
 
-        return result;
+        public boolean hasNext() {
+            if (next == null) {
+                try {
+                    next = nextDescription();
+                } catch (IOException ex) {
+                    Exceptions.printStackTrace(ex);
+                }
+            }
+
+            return next != null;
+        }
+
+        public DuplicateDescription next() {
+            if (!hasNext()) {
+                throw new NoSuchElementException();
+            }
+
+            DuplicateDescription r = next;
+
+            next = null;
+            return r;
+        }
+
+        public void remove() {
+            throw new UnsupportedOperationException("Not supported.");
+        }
+
     }
 
     private static List<String> getDuplicatedValues(IndexReader ir, String field, AtomicBoolean cancel) throws IOException {

File api/src/org/netbeans/modules/jackpot30/impl/duplicates/DuplicatesListPanel.form

-<?xml version="1.0" encoding="UTF-8" ?>
+<?xml version="1.1" encoding="UTF-8" ?>
 
 <Form version="1.5" maxVersion="1.7" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
-  <SyntheticProperties>
-    <SyntheticProperty name="designerSize" type="java.awt.Dimension" value="-84,-19,0,5,115,114,0,18,106,97,118,97,46,97,119,116,46,68,105,109,101,110,115,105,111,110,65,-114,-39,-41,-84,95,68,20,2,0,2,73,0,6,104,101,105,103,104,116,73,0,5,119,105,100,116,104,120,112,0,0,1,56,0,0,1,-98"/>
-  </SyntheticProperties>
   <AuxValues>
     <AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="1"/>
     <AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
   <Layout>
     <DimensionLayout dim="0">
       <Group type="103" groupAlignment="0" attributes="0">
-          <Group type="102" alignment="0" attributes="0">
+          <Group type="102" alignment="1" attributes="0">
               <EmptySpace max="-2" attributes="0"/>
-              <Group type="103" groupAlignment="0" attributes="0">
-                  <Component id="jSplitPane1" alignment="1" pref="390" max="32767" attributes="0"/>
-                  <Component id="jScrollPane4" alignment="1" pref="390" max="32767" attributes="0"/>
+              <Group type="103" groupAlignment="1" attributes="0">
+                  <Component id="mainSplit2" alignment="0" pref="906" max="32767" attributes="0"/>
+                  <Component id="jScrollPane1" alignment="0" pref="906" max="32767" attributes="0"/>
+                  <Component id="jPanel1" alignment="1" min="-2" max="-2" attributes="0"/>
               </Group>
               <EmptySpace max="-2" attributes="0"/>
           </Group>
     <DimensionLayout dim="1">
       <Group type="103" groupAlignment="0" attributes="0">
           <Group type="102" attributes="0">
-              <EmptySpace max="-2" attributes="0"/>
-              <Component id="jScrollPane4" min="-2" pref="76" max="-2" attributes="0"/>
-              <EmptySpace max="-2" attributes="0"/>
-              <Component id="jSplitPane1" pref="206" max="32767" attributes="0"/>
-              <EmptySpace max="-2" attributes="0"/>
+              <EmptySpace min="-2" max="-2" attributes="0"/>
+              <Component id="jScrollPane1" min="-2" pref="67" max="-2" attributes="0"/>
+              <EmptySpace min="-2" max="-2" attributes="0"/>
+              <Component id="mainSplit2" pref="467" max="32767" attributes="0"/>
+              <EmptySpace type="unrelated" min="-2" max="-2" attributes="0"/>
+              <Component id="jPanel1" min="-2" max="-2" attributes="0"/>
+              <EmptySpace min="-2" max="-2" attributes="0"/>
           </Group>
       </Group>
     </DimensionLayout>
   </Layout>
   <SubComponents>
-    <Container class="javax.swing.JSplitPane" name="jSplitPane1">
+    <Container class="javax.swing.JScrollPane" name="jScrollPane1">
+
+      <Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
+      <SubComponents>
+        <Component class="javax.swing.JList" name="duplicatesList">
+          <Properties>
+            <Property name="model" type="javax.swing.ListModel" editor="org.netbeans.modules.form.editors2.ListModelEditor">
+              <StringArray count="0"/>
+            </Property>
+            <Property name="prototypeCellValue" type="java.lang.Object" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
+              <Connection code="&quot;9999999999999999999999999999999999999999999999999999999999999999999999&quot;" type="code"/>
+            </Property>
+            <Property name="visibleRowCount" type="int" value="4"/>
+          </Properties>
+        </Component>
+      </SubComponents>
+    </Container>
+    <Container class="javax.swing.JSplitPane" name="mainSplit2">
+      <Properties>
+        <Property name="dividerLocation" type="int" value="400"/>
+      </Properties>
       <AuxValues>
         <AuxValue name="JavaCodeGenerator_CreateCodeCustom" type="java.lang.String" value="new BalancedSplitPane()"/>
       </AuxValues>
 
       <Layout class="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout"/>
       <SubComponents>
-        <Container class="javax.swing.JScrollPane" name="jScrollPane2">
-          <AuxValues>
-            <AuxValue name="autoScrollPane" type="java.lang.Boolean" value="true"/>
-          </AuxValues>
+        <Container class="javax.swing.JPanel" name="rightPanel">
+          <Constraints>
+            <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout" value="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout$JSplitPaneConstraintsDescription">
+              <JSplitPaneConstraints position="right"/>
+            </Constraint>
+          </Constraints>
+
+          <Layout class="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout"/>
+          <SubComponents>
+            <Component class="javax.swing.JComboBox" name="rightFileList">
+              <Properties>
+                <Property name="model" type="javax.swing.ComboBoxModel" editor="org.netbeans.modules.form.editors2.ComboBoxModelEditor">
+                  <StringArray count="0"/>
+                </Property>
+              </Properties>
+              <Constraints>
+                <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
+                  <GridBagConstraints gridX="0" gridY="0" gridWidth="1" gridHeight="1" fill="2" ipadX="324" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="18" weightX="1.0" weightY="0.0"/>
+                </Constraint>
+              </Constraints>
+            </Component>
+            <Container class="javax.swing.JScrollPane" name="jScrollPane3">
+              <AuxValues>
+                <AuxValue name="autoScrollPane" type="java.lang.Boolean" value="true"/>
+              </AuxValues>
+              <Constraints>
+                <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
+                  <GridBagConstraints gridX="0" gridY="1" gridWidth="1" gridHeight="1" fill="1" ipadX="0" ipadY="0" insetsTop="6" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="18" weightX="1.0" weightY="1.0"/>
+                </Constraint>
+              </Constraints>
+
+              <Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
+              <SubComponents>
+                <Component class="javax.swing.JEditorPane" name="right">
+                </Component>
+              </SubComponents>
+            </Container>
+          </SubComponents>
+        </Container>
+        <Container class="javax.swing.JPanel" name="leftPanel">
           <Constraints>
             <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout" value="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout$JSplitPaneConstraintsDescription">
               <JSplitPaneConstraints position="left"/>
             </Constraint>
           </Constraints>
 
-          <Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
+          <Layout class="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout"/>
           <SubComponents>
-            <Component class="javax.swing.JEditorPane" name="left">
-            </Component>
-          </SubComponents>
-        </Container>
-        <Container class="javax.swing.JScrollPane" name="jScrollPane3">
-          <AuxValues>
-            <AuxValue name="autoScrollPane" type="java.lang.Boolean" value="true"/>
-          </AuxValues>
-          <Constraints>
-            <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout" value="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout$JSplitPaneConstraintsDescription">
-              <JSplitPaneConstraints position="right"/>
-            </Constraint>
-          </Constraints>
+            <Container class="javax.swing.JScrollPane" name="jScrollPane2">
+              <AuxValues>
+                <AuxValue name="autoScrollPane" type="java.lang.Boolean" value="true"/>
+              </AuxValues>
+              <Constraints>
+                <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
+                  <GridBagConstraints gridX="0" gridY="1" gridWidth="1" gridHeight="1" fill="1" ipadX="0" ipadY="0" insetsTop="6" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="18" weightX="1.0" weightY="1.0"/>
+                </Constraint>
+              </Constraints>
 
-          <Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
-          <SubComponents>
-            <Component class="javax.swing.JEditorPane" name="right">
-            </Component>
-          </SubComponents>
-        </Container>
-      </SubComponents>
-    </Container>
-    <Container class="javax.swing.JScrollPane" name="jScrollPane4">
-
-      <Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
-      <SubComponents>
-        <Container class="javax.swing.JSplitPane" name="jSplitPane2">
-          <Properties>
-            <Property name="dividerLocation" type="int" value="250"/>
-            <Property name="dividerSize" type="int" value="1"/>
-          </Properties>
-          <AuxValues>
-            <AuxValue name="JavaCodeGenerator_CreateCodeCustom" type="java.lang.String" value="new BalancedSplitPane()"/>
-          </AuxValues>
-
-          <Layout class="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout"/>
-          <SubComponents>
-            <Component class="javax.swing.JList" name="rightList">
+              <Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
+              <SubComponents>
+                <Component class="javax.swing.JEditorPane" name="left">
+                </Component>
+              </SubComponents>
+            </Container>
+            <Component class="javax.swing.JComboBox" name="leftFileList">
               <Properties>
-                <Property name="model" type="javax.swing.ListModel" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
-                  <Connection code="createListModel()" type="code"/>
-                </Property>
-                <Property name="cellRenderer" type="javax.swing.ListCellRenderer" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
-                  <Connection code="new RendererImpl(false)" type="code"/>
+                <Property name="model" type="javax.swing.ComboBoxModel" editor="org.netbeans.modules.form.editors2.ComboBoxModelEditor">
+                  <StringArray count="0"/>
                 </Property>
               </Properties>
               <Constraints>
-                <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout" value="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout$JSplitPaneConstraintsDescription">
-                  <JSplitPaneConstraints position="right"/>
-                </Constraint>
-              </Constraints>
-            </Component>
-            <Component class="javax.swing.JList" name="leftList">
-              <Properties>
-                <Property name="model" type="javax.swing.ListModel" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
-                  <Connection code="createListModel()" type="code"/>
-                </Property>
-                <Property name="cellRenderer" type="javax.swing.ListCellRenderer" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
-                  <Connection code="new RendererImpl(true)" type="code"/>
-                </Property>
-              </Properties>
-              <Constraints>
-                <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout" value="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout$JSplitPaneConstraintsDescription">
-                  <JSplitPaneConstraints position="left"/>
+                <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
+                  <GridBagConstraints gridX="0" gridY="0" gridWidth="1" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="18" weightX="1.0" weightY="0.0"/>
                 </Constraint>
               </Constraints>
             </Component>
         </Container>
       </SubComponents>
     </Container>
+    <Container class="javax.swing.JPanel" name="jPanel1">
+
+      <Layout class="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout"/>
+      <SubComponents>
+        <Component class="javax.swing.JLabel" name="progressLabel">
+          <Properties>
+            <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
+              <ResourceString bundle="org/netbeans/modules/jackpot30/impl/duplicates/Bundle.properties" key="DuplicatesListPanel.progressLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
+            </Property>
+          </Properties>
+          <Constraints>
+            <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
+              <GridBagConstraints gridX="0" gridY="0" gridWidth="1" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="18" weightX="1.0" weightY="0.0"/>
+            </Constraint>
+          </Constraints>
+        </Component>
+        <Component class="javax.swing.JLabel" name="findMore">
+          <Properties>
+            <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
+              <ResourceString bundle="org/netbeans/modules/jackpot30/impl/duplicates/Bundle.properties" key="DuplicatesListPanel.findMore.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
+            </Property>
+            <Property name="cursor" type="java.awt.Cursor" editor="org.netbeans.modules.form.editors2.CursorEditor">
+              <Color id="Hand Cursor"/>
+            </Property>
+          </Properties>
+          <Events>
+            <EventHandler event="mouseClicked" listener="java.awt.event.MouseListener" parameters="java.awt.event.MouseEvent" handler="findMoreMouseClicked"/>
+          </Events>
+          <Constraints>
+            <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
+              <GridBagConstraints gridX="1" gridY="0" gridWidth="1" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="6" insetsBottom="0" insetsRight="0" anchor="18" weightX="0.0" weightY="0.0"/>
+            </Constraint>
+          </Constraints>
+        </Component>
+      </SubComponents>
+    </Container>
   </SubComponents>
 </Form>

File api/src/org/netbeans/modules/jackpot30/impl/duplicates/DuplicatesListPanel.java

 import java.awt.Color;
 import java.awt.Component;
 import java.awt.Rectangle;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
 import java.io.IOException;
 import java.util.Collection;
+import java.util.Iterator;
+import java.util.LinkedHashSet;
+import java.util.Set;
+import javax.swing.DefaultComboBoxModel;
 import javax.swing.DefaultListCellRenderer;
 import javax.swing.DefaultListModel;
 import javax.swing.JEditorPane;
 import javax.swing.JList;
 import javax.swing.JSplitPane;
-import javax.swing.ListModel;
 import javax.swing.SwingUtilities;
 import javax.swing.event.ListSelectionEvent;
 import javax.swing.event.ListSelectionListener;
 import org.openide.filesystems.FileUtil;
 import org.openide.util.Exceptions;
 import org.openide.util.Lookup;
+import org.openide.util.RequestProcessor;
+import org.openide.util.RequestProcessor.Task;
 import org.openide.util.lookup.Lookups;
 import org.openide.util.lookup.ServiceProvider;
 
  * @author lahvac
  */
 public class DuplicatesListPanel extends javax.swing.JPanel {
-    private final Collection<? extends DuplicateDescription> dupes;
-    private final int commonPrefixLength;
+    private final Collection<String> sourceRoots;
+    private final Iterator<? extends DuplicateDescription> dupes;
 
-    public DuplicatesListPanel(Collection<? extends DuplicateDescription> dupes) {
+    private int targetCount;
+
+    public DuplicatesListPanel(Collection<String> sourceRoots, final Iterator<? extends DuplicateDescription> dupes) {
+        this.sourceRoots = sourceRoots;
         this.dupes = dupes;
-        this.commonPrefixLength = computeCommonPrefixLen(dupes);
         
         initComponents();
 
         right.setContentType("text/x-java");
         right.putClientProperty(DuplicatesListPanel.class, new OffsetsBag(right.getDocument()));
 
-        leftList.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
+        duplicatesList.setModel(new DefaultListModel());
+        duplicatesList.setCellRenderer(new DuplicatesRendererImpl());
+        duplicatesList.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
             public void valueChanged(ListSelectionEvent arg0) {
-                setDiff((DuplicateDescription) leftList.getSelectedValue());
-                rightList.setSelectedValue(leftList.getSelectedValue(), false);
+                DuplicateDescription dd = (DuplicateDescription) duplicatesList.getSelectedValue();
+                DefaultComboBoxModel l = new DefaultComboBoxModel();
+                DefaultComboBoxModel r = new DefaultComboBoxModel();
+
+                for (Span s : dd.dupes) {
+                    l.addElement(s);
+                    r.addElement(s);
+                }
+
+                leftFileList.setModel(l);
+                rightFileList.setModel(r);
+
+                leftFileList.setSelectedIndex(0);
+                rightFileList.setSelectedIndex(1);
             }
         });
 
-        rightList.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
-            public void valueChanged(ListSelectionEvent arg0) {
-                leftList.setSelectedValue(rightList.getSelectedValue(), false);
+        leftFileList.setRenderer(new SpanRendererImpl());
+        leftFileList.addActionListener(new ActionListener() {
+
+            public void actionPerformed(ActionEvent e) {
+                setSpan(left, (Span) leftFileList.getSelectedItem());
+            }
+        });
+        rightFileList.setRenderer(new SpanRendererImpl());
+        rightFileList.addActionListener(new ActionListener() {
+            public void actionPerformed(ActionEvent e) {
+                setSpan(right, (Span) rightFileList.getSelectedItem());
             }
         });
 
-        leftList.setSelectedIndex(0);
+        progressLabel.setText("Looking for duplicates...");
+
+        findMore();
     }
 
     /** This method is called from within the constructor to
     @SuppressWarnings("unchecked")
     // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
     private void initComponents() {
+        java.awt.GridBagConstraints gridBagConstraints;
 
-        jSplitPane1 = new BalancedSplitPane();
+        jScrollPane1 = new javax.swing.JScrollPane();
+        duplicatesList = new javax.swing.JList();
+        mainSplit2 = new BalancedSplitPane();
+        rightPanel = new javax.swing.JPanel();
+        rightFileList = new javax.swing.JComboBox();
+        jScrollPane3 = new javax.swing.JScrollPane();
+        right = new javax.swing.JEditorPane();
+        leftPanel = new javax.swing.JPanel();
         jScrollPane2 = new javax.swing.JScrollPane();
         left = new javax.swing.JEditorPane();
-        jScrollPane3 = new javax.swing.JScrollPane();
-        right = new javax.swing.JEditorPane();
-        jScrollPane4 = new javax.swing.JScrollPane();
-        jSplitPane2 = new BalancedSplitPane();
-        rightList = new javax.swing.JList();
-        leftList = new javax.swing.JList();
+        leftFileList = new javax.swing.JComboBox();
+        jPanel1 = new javax.swing.JPanel();
+        progressLabel = new javax.swing.JLabel();
+        findMore = new javax.swing.JLabel();
+
+        duplicatesList.setPrototypeCellValue("9999999999999999999999999999999999999999999999999999999999999999999999");
+        duplicatesList.setVisibleRowCount(4);
+        jScrollPane1.setViewportView(duplicatesList);
+
+        mainSplit2.setDividerLocation(400);
+
+        rightPanel.setLayout(new java.awt.GridBagLayout());
+
+        gridBagConstraints = new java.awt.GridBagConstraints();
+        gridBagConstraints.gridx = 0;
+        gridBagConstraints.gridy = 0;
+        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
+        gridBagConstraints.ipadx = 324;
+        gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
+        gridBagConstraints.weightx = 1.0;
+        rightPanel.add(rightFileList, gridBagConstraints);
+
+        jScrollPane3.setViewportView(right);
+
+        gridBagConstraints = new java.awt.GridBagConstraints();
+        gridBagConstraints.gridx = 0;
+        gridBagConstraints.gridy = 1;
+        gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
+        gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
+        gridBagConstraints.weightx = 1.0;
+        gridBagConstraints.weighty = 1.0;
+        gridBagConstraints.insets = new java.awt.Insets(6, 0, 0, 0);
+        rightPanel.add(jScrollPane3, gridBagConstraints);
+
+        mainSplit2.setRightComponent(rightPanel);
+
+        leftPanel.setLayout(new java.awt.GridBagLayout());
 
         jScrollPane2.setViewportView(left);
 
-        jSplitPane1.setLeftComponent(jScrollPane2);
+        gridBagConstraints = new java.awt.GridBagConstraints();
+        gridBagConstraints.gridx = 0;
+        gridBagConstraints.gridy = 1;
+        gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
+        gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
+        gridBagConstraints.weightx = 1.0;
+        gridBagConstraints.weighty = 1.0;
+        gridBagConstraints.insets = new java.awt.Insets(6, 0, 0, 0);
+        leftPanel.add(jScrollPane2, gridBagConstraints);
 
-        jScrollPane3.setViewportView(right);
+        gridBagConstraints = new java.awt.GridBagConstraints();
+        gridBagConstraints.gridx = 0;
+        gridBagConstraints.gridy = 0;
+        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
+        gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
+        gridBagConstraints.weightx = 1.0;
+        leftPanel.add(leftFileList, gridBagConstraints);
 
-        jSplitPane1.setRightComponent(jScrollPane3);
+        mainSplit2.setLeftComponent(leftPanel);
 
-        jSplitPane2.setDividerLocation(250);
-        jSplitPane2.setDividerSize(1);
+        jPanel1.setLayout(new java.awt.GridBagLayout());
 
-        rightList.setModel(createListModel());
-        rightList.setCellRenderer(new RendererImpl(false));
-        jSplitPane2.setRightComponent(rightList);
+        progressLabel.setText(org.openide.util.NbBundle.getMessage(DuplicatesListPanel.class, "DuplicatesListPanel.progressLabel.text")); // NOI18N
+        gridBagConstraints = new java.awt.GridBagConstraints();
+        gridBagConstraints.gridx = 0;
+        gridBagConstraints.gridy = 0;
+        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
+        gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
+        gridBagConstraints.weightx = 1.0;
+        jPanel1.add(progressLabel, gridBagConstraints);
 
-        leftList.setModel(createListModel());
-        leftList.setCellRenderer(new RendererImpl(true));
-        jSplitPane2.setLeftComponent(leftList);
-
-        jScrollPane4.setViewportView(jSplitPane2);
+        findMore.setText(org.openide.util.NbBundle.getMessage(DuplicatesListPanel.class, "DuplicatesListPanel.findMore.text")); // NOI18N
+        findMore.setCursor(new java.awt.Cursor(java.awt.Cursor.HAND_CURSOR));
+        findMore.addMouseListener(new java.awt.event.MouseAdapter() {
+            public void mouseClicked(java.awt.event.MouseEvent evt) {
+                findMoreMouseClicked(evt);
+            }
+        });
+        gridBagConstraints = new java.awt.GridBagConstraints();
+        gridBagConstraints.gridx = 1;
+        gridBagConstraints.gridy = 0;
+        gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
+        gridBagConstraints.insets = new java.awt.Insets(0, 6, 0, 0);
+        jPanel1.add(findMore, gridBagConstraints);
 
         javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
         this.setLayout(layout);
         layout.setHorizontalGroup(
             layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
-            .addGroup(layout.createSequentialGroup()
+            .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
                 .addContainerGap()
-                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
-                    .addComponent(jSplitPane1, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, 390, Short.MAX_VALUE)
-                    .addComponent(jScrollPane4, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, 390, Short.MAX_VALUE))
+                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
+                    .addComponent(mainSplit2, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, 906, Short.MAX_VALUE)
+                    .addComponent(jScrollPane1, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, 906, Short.MAX_VALUE)
+                    .addComponent(jPanel1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
                 .addContainerGap())
         );
         layout.setVerticalGroup(
             layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
             .addGroup(layout.createSequentialGroup()
                 .addContainerGap()
-                .addComponent(jScrollPane4, javax.swing.GroupLayout.PREFERRED_SIZE, 76, javax.swing.GroupLayout.PREFERRED_SIZE)
+                .addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 67, javax.swing.GroupLayout.PREFERRED_SIZE)
                 .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
-                .addComponent(jSplitPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 206, Short.MAX_VALUE)
+                .addComponent(mainSplit2, javax.swing.GroupLayout.DEFAULT_SIZE, 467, Short.MAX_VALUE)
+                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
+                .addComponent(jPanel1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                 .addContainerGap())
         );
     }// </editor-fold>//GEN-END:initComponents
 
+    private void findMoreMouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_findMoreMouseClicked
+        findMore();
+    }//GEN-LAST:event_findMoreMouseClicked
 
-    private ListModel createListModel() {
-        DefaultListModel dlm = new DefaultListModel();
-
-        for (DuplicateDescription dd : dupes) {
-            dlm.addElement(dd);
-        }
-
-        return dlm;
-    }
-
-    private static int computeCommonPrefixLen(Collection<? extends DuplicateDescription> dupes) {
-        String commonPrefix = null;
-
-        for (DuplicateDescription dd : dupes) {
-            for (Span s : dd.dupes) {
-                commonPrefix = computeCommonPrefix(commonPrefix, s.file);
-            }
-        }
-
-        return commonPrefix != null ? commonPrefix.length() : 0;
+    private void findMore() {
+        targetCount = duplicatesList.getModel().getSize() + 100;
+        findMore.setVisible(false);
+        WORKER.schedule(0);
     }
 
     private static String computeCommonPrefix(String origCommonPrefix, FileObject file) {
         return origCommonPrefix;
     }
     
-    private void setDiff(DuplicateDescription dd) {
-        setSpan(left, dd.dupes.get(0));
-        setSpan(right, dd.dupes.get(1));
-    }
-
     private static void setSpan(JEditorPane pane, Span s) {
         try {
             pane.setText(s.file.asText());
 
     private static final AttributeSet HIGHLIGHT = AttributesUtilities.createImmutable(StyleConstants.Background, new Color(0xDF, 0xDF, 0xDF, 0xff));
 
-    private final class RendererImpl extends DefaultListCellRenderer {
-        private final boolean left;
-        public RendererImpl(boolean left) {
-            this.left = left;
-        }
+    private final class DuplicatesRendererImpl extends DefaultListCellRenderer {
         @Override
         public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
+            if (!(value instanceof DuplicateDescription)) return super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
             DuplicateDescription dd = (DuplicateDescription) value;
-            String name = left ? FileUtil.getFileDisplayName(dd.dupes.get(0).file).substring(commonPrefixLength) : FileUtil.getFileDisplayName(dd.dupes.get(1).file).substring(commonPrefixLength);
+            Set<FileObject> files = new LinkedHashSet<FileObject>();
+            String commonPrefix = null;
 
-            return super.getListCellRendererComponent(list, name, index, isSelected, cellHasFocus);
+            for (Span s : dd.dupes) {
+                commonPrefix = computeCommonPrefix(commonPrefix, s.file);
+                files.add(s.file);
+            }
+
+            StringBuilder cap = new StringBuilder();
+
+            OUTER: for (FileObject file : files) {
+                String name = FileUtil.getFileDisplayName(file);
+
+                if (cap.length() > 0) {
+                    cap.append("    ");
+                }
+                
+                for (String sr : sourceRoots) {
+                    if (name.startsWith(sr)) {
+                        cap.append(name.substring(Math.max(0, sr.lastIndexOf('/') + 1)));
+                        continue OUTER;
+                    }
+                }
+            }
+
+            return super.getListCellRendererComponent(list, cap.toString(), index, isSelected, cellHasFocus);
+        }
+    }
+
+    private final class SpanRendererImpl extends DefaultListCellRenderer {
+        @Override
+        public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
+            if (!(value instanceof Span)) {
+                return super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
+            }
+            Span span = (Span) value;
+
+            return super.getListCellRendererComponent(list, FileUtil.getFileDisplayName(span.file), index, isSelected, cellHasFocus);
         }
     }
 
     private static final class BalancedSplitPane extends JSplitPane {
 
         @Override
+        @SuppressWarnings("deprecation")
         public void reshape(int x, int y, int w, int h) {
             super.reshape(x, y, w, h);
             SwingUtilities.invokeLater(new Runnable() {
     }
 
     // Variables declaration - do not modify//GEN-BEGIN:variables
+    private javax.swing.JList duplicatesList;
+    private javax.swing.JLabel findMore;
+    private javax.swing.JPanel jPanel1;
+    private javax.swing.JScrollPane jScrollPane1;
     private javax.swing.JScrollPane jScrollPane2;
     private javax.swing.JScrollPane jScrollPane3;
-    private javax.swing.JScrollPane jScrollPane4;
-    private javax.swing.JSplitPane jSplitPane1;
-    private javax.swing.JSplitPane jSplitPane2;
     private javax.swing.JEditorPane left;
-    private javax.swing.JList leftList;
+    private javax.swing.JComboBox leftFileList;
+    private javax.swing.JPanel leftPanel;
+    private javax.swing.JSplitPane mainSplit2;
+    private javax.swing.JLabel progressLabel;
     private javax.swing.JEditorPane right;
-    private javax.swing.JList rightList;
+    private javax.swing.JComboBox rightFileList;
+    private javax.swing.JPanel rightPanel;
     // End of variables declaration//GEN-END:variables
 
+    private static final RequestProcessor DEFAULT_WORKER = new RequestProcessor(DuplicatesListPanel.class.getName(), 1, false, false);
+    private final Task WORKER = DEFAULT_WORKER.create(new Runnable() {
+        public void run() {
+            if (dupes.hasNext()) {
+                final DuplicateDescription dd = dupes.next();
+
+                SwingUtilities.invokeLater(new Runnable() {
+
+                    public void run() {
+                        ((DefaultListModel)duplicatesList.getModel()).addElement(dd);
+
+                        int size = duplicatesList.getModel().getSize();
+
+                        if (size == 1) {
+                            duplicatesList.setSelectedIndex(0);
+                        }
+                        
+                        if (size >= targetCount) {
+                            findMore.setVisible(true);
+                            progressLabel.setText("Found " + size + " duplicated snippets.");
+                        } else {
+                            progressLabel.setText("Found " + size + " duplicated snippets and searching...");
+                            WORKER.schedule(0);
+                        }
+                    }
+                });
+            }
+        }
+    });
+
 }

File api/src/org/netbeans/modules/jackpot30/impl/duplicates/GlobalFindDuplicates.java

 import java.awt.event.ActionListener;
 import java.io.IOException;
 import java.util.Collection;
-import java.util.Collections;
+import java.util.Iterator;
 import java.util.LinkedList;
 import java.util.concurrent.atomic.AtomicBoolean;
 import javax.swing.JLabel;
 import javax.swing.JPanel;
 import javax.swing.SwingUtilities;
+import org.netbeans.api.java.classpath.ClassPath;
+import org.netbeans.api.java.classpath.GlobalPathRegistry;
 import org.netbeans.api.progress.ProgressHandle;
 import org.netbeans.api.progress.ProgressHandleFactory;
 import org.netbeans.modules.jackpot30.impl.duplicates.ComputeDuplicates.DuplicateDescription;
 import org.openide.DialogDescriptor;
 import org.openide.DialogDisplayer;
 import org.openide.NotifyDescriptor;
+import org.openide.filesystems.FileObject;
+import org.openide.filesystems.FileUtil;
 import org.openide.util.Exceptions;
 import org.openide.util.HelpCtx;
 import org.openide.util.RequestProcessor;
 public final class GlobalFindDuplicates implements ActionListener {
 
     public void actionPerformed(ActionEvent e) {
-        final Collection<DuplicateDescription> dupes = Collections.synchronizedList(new LinkedList<DuplicateDescription>());
+        final Iterator<? extends DuplicateDescription>[] dupes = new Iterator[1];
         final ProgressHandle handle = ProgressHandleFactory.createHandle("Compute Duplicates");
         JPanel panel = createPanel(handle);
         final AtomicBoolean cancel = new AtomicBoolean();
 
         final Dialog d = DialogDisplayer.getDefault().createDialog(w);
         final AtomicBoolean done = new AtomicBoolean();
+        final Collection<String> sourceRoots = new LinkedList<String>();
 
         WORKER.post(new Runnable() {
             public void run() {
                 try {
-                    dupes.addAll(new ComputeDuplicates().computeDuplicatesForAllOpenedProjects(handle, cancel));
+                    for (ClassPath cp : GlobalPathRegistry.getDefault().getPaths(ClassPath.SOURCE)) {
+                        for (ClassPath.Entry e : cp.entries()) {
+                            FileObject root = e.getRoot();
+
+                            if (root == null) continue;
+
+                            sourceRoots.add(FileUtil.getFileDisplayName(root));
+                        }
+                    }
+
+                    dupes[0] = new ComputeDuplicates().computeDuplicatesForAllOpenedProjects(handle, cancel);
                     done.set(true);
                 } catch (IOException ex) {
                     Exceptions.printStackTrace(ex);
         
         if (cancel.get()) return;
 
-        NotifyDescriptor nd = new NotifyDescriptor.Message(new DuplicatesListPanel(dupes));
+        NotifyDescriptor nd = new NotifyDescriptor.Message(new DuplicatesListPanel(sourceRoots, dupes[0]));
 
         DialogDisplayer.getDefault().notifyLater(nd);
     }

File api/src/org/netbeans/modules/jackpot30/impl/indexing/AbstractLuceneIndex.java

 import java.util.BitSet;
 import java.util.Collection;
 import java.util.Collections;
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
+import java.util.Map;
 import java.util.Set;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 import javax.lang.model.type.ArrayType;
 import javax.lang.model.type.TypeKind;
 import javax.lang.model.type.TypeMirror;
-import org.apache.lucene.analysis.Token;
 import org.apache.lucene.analysis.TokenStream;
+import org.apache.lucene.analysis.tokenattributes.TermAttribute;
 import org.apache.lucene.document.CompressionTools;
 import org.apache.lucene.document.Document;
 import org.apache.lucene.document.Field;
 import org.apache.lucene.search.TermQuery;
 import org.apache.lucene.search.TopDocs;
 import org.netbeans.api.annotations.common.NonNull;
+import org.netbeans.api.annotations.common.NullAllowed;
 import org.netbeans.modules.jackpot30.impl.pm.BulkSearch;
 import org.netbeans.modules.jackpot30.impl.pm.BulkSearch.BulkPattern;
 import org.netbeans.modules.jackpot30.impl.pm.BulkSearch.EncodingContext;
 
     @Override
     public Collection<? extends String> findCandidates(BulkPattern pattern) throws IOException {
+        return findCandidates(pattern, false).keySet();
+    }
+
+    @Override
+    public Map<String, Map<String, Integer>> findCandidatesWithFrequencies(BulkPattern pattern) throws IOException {
+        return findCandidates(pattern, true);
+    }
+
+    private Map<String, Map<String, Integer>> findCandidates(BulkPattern pattern, boolean withFrequencies) throws IOException {
         IndexReader reader = createReader();
 
         if (reader == null) {
-             return Collections.emptyList();
+             return Collections.emptyMap();
          }
 
         Searcher s = new IndexSearcher(reader);
             throw new IOException(ex);
         }
 
-        Collection<String> result = new HashSet<String>();
+        Map<String, Map<String, Integer>> result = new HashMap<String, Map<String, Integer>>();
 
         for (int docNum = matchingDocuments.nextSetBit(0); docNum >= 0; docNum = matchingDocuments.nextSetBit(docNum+1)) {
             try {
                 ByteArrayInputStream in = new ByteArrayInputStream(CompressionTools.decompress(doc.getField("encoded").getBinaryValue()));
 
                 try {
-                    if (!BulkSearch.getDefault().matches(in, pattern)) {
+                    Map<String, Integer> freqs;
+                    boolean matches;
+
+                    if (withFrequencies) {
+                        freqs = BulkSearch.getDefault().matchesWithFrequencies(in, pattern);
+                        matches = !freqs.isEmpty();
+                    } else {
+                        freqs = null;
+                        matches = BulkSearch.getDefault().matches(in, pattern);
+                    }
+
+                    if (matches) {
+                        result.put(doc.getField("path").stringValue(), freqs);
                         continue;
                     }
                 } finally {
                     in.close();
                 }
-
-                result.add(doc.getField("path").stringValue());
             } catch (DataFormatException ex) {
                 throw new IOException(ex);
             }
                 }
             });
             
-            return CompressionTools.decompressString(doc.getField("sourceCode").getBinaryValue());
+            return CompressionTools.decompressString(doc.getField("sourceCode").getBinaryValue()).replaceAll("\r\n", "\n")/*XXX*/;
         } catch (DataFormatException ex) {
             Exceptions.printStackTrace(ex);
             return "";
         }
 
         @Override
-        public void record(URL source, final CompilationUnitTree cut, final AttributionWrapper attributed) throws IOException {
+        public void record(URL source, final CompilationUnitTree cut, final @NullAllowed AttributionWrapper attributed) throws IOException {
             String relative = source.getPath().substring(stripLength);
             ByteArrayOutputStream out = null;
             EncodingContext ec = null;
     public static final class TokenStreamImpl extends TokenStream {
 
         private final Iterator<? extends String> tokens;
+        private final TermAttribute termAtt;
 
         public TokenStreamImpl(Iterable<? extends String> tokens) {
             this.tokens = tokens != null ? tokens.iterator() : /*???*/Collections.<String>emptyList().iterator();
+            this.termAtt = addAttribute(TermAttribute.class);
         }
 
         @Override
-        public Token next() throws IOException {
+        public boolean incrementToken() throws IOException {
             if (!tokens.hasNext())
-                return null;
+                return false;
 
             String t = tokens.next();
 
-            return new Token(t, 0, t.length());
+            termAtt.setTermBuffer(t);
+            
+            return true;
         }
     }
 

File api/src/org/netbeans/modules/jackpot30/impl/indexing/CustomIndexerImpl.java

                 w[0].remove(path);
             }
 
+            final boolean attributed = Boolean.getBoolean(CustomIndexerImpl.class.getName() + "-attributed");
+
             if (!toIndex.isEmpty()) {
                 JavaSource.create(cpInfo, toIndex).runUserActionTask(new Task<CompilationController>() {
                     public void run(final CompilationController cc) throws Exception {
-                        if (cc.toPhase(Phase.RESOLVED).compareTo(Phase.RESOLVED) < 0)
+                        if (attributed) {
+                            if (cc.toPhase(Phase.RESOLVED).compareTo(Phase.RESOLVED) < 0)
+                                return ;
+                        } else {
+                        if (cc.toPhase(Phase.PARSED).compareTo(Phase.PARSED) < 0)
                             return ;
+                        }
 
-                        w[0].record(cc.getFileObject().getURL(), cc.getCompilationUnit(), new AttributionWrapper(cc));
+                        w[0].record(cc.getFileObject().getURL(), cc.getCompilationUnit(), attributed ? new AttributionWrapper(cc) : null);
                     }
                 }, true);
             }

File api/src/org/netbeans/modules/jackpot30/impl/indexing/Index.java

 
 package org.netbeans.modules.jackpot30.impl.indexing;
 
-import org.netbeans.modules.jackpot30.spi.HintDescription.AdditionalQueryConstraints;
+import java.util.Map;
 import com.sun.source.util.Trees;
 import javax.lang.model.util.Types;
 import org.netbeans.modules.jackpot30.impl.WebUtilities;
 import java.util.Collections;
 import org.codeviation.pojson.Pojson;
 import org.netbeans.api.annotations.common.NonNull;
+import org.netbeans.api.annotations.common.NullAllowed;
 import org.netbeans.api.java.source.CompilationInfo;
 import org.netbeans.modules.jackpot30.impl.pm.BulkSearch.BulkPattern;
 import org.openide.util.Exceptions;
                 }
             }
             @Override
+            public Map<String, Map<String, Integer>> findCandidatesWithFrequencies(BulkPattern pattern) throws IOException {
+                throw new UnsupportedOperationException("Not supported yet.");
+            }
+            @Override
             public @NonNull IndexInfo getIndexInfo() {
                 IndexInfo result = IndexInfo.empty();
 
             @Override
             public CharSequence getSourceCode(String relativePath) {
                 try {
-                    URI u = new URI(indexURL + "?path=" + escapeForQuery(subIndex) + "&relative=" + escapeForQuery(relativePath));
+                    URI u = new URI(indexURL + "/cat?path=" + escapeForQuery(subIndex) + "&relative=" + escapeForQuery(relativePath));
 
                     return WebUtilities.requestStringResponse(u);
                 } catch (URISyntaxException ex) {
     public abstract IndexWriter openForWriting() throws IOException;
 
     public abstract Collection<? extends String> findCandidates(BulkPattern pattern) throws IOException;
+    public abstract Map<String, Map<String, Integer>> findCandidatesWithFrequencies(BulkPattern pattern) throws IOException;
 
     public abstract CharSequence getSourceCode(String relativePath);
 
 
         protected IndexWriter() throws IOException {}
 
-        public abstract void record(URL source, final CompilationUnitTree cut, AttributionWrapper attributed) throws IOException;
+        public abstract void record(URL source, final CompilationUnitTree cut, @NullAllowed AttributionWrapper attributed) throws IOException;
 
         public abstract void remove(String relativePath) throws IOException;
 

File api/src/org/netbeans/modules/jackpot30/impl/pm/BulkSearch.java

     public abstract Map<String, Collection<TreePath>> match(CompilationInfo info, TreePath toSearch, BulkPattern pattern, Map<String, Long> timeLog);
 
     public abstract boolean matches(InputStream encoded, BulkPattern pattern);
+    public abstract Map<String, Integer> matchesWithFrequencies(InputStream encoded, BulkPattern pattern);
     
     public abstract boolean matches(CompilationInfo info, TreePath toSearch, BulkPattern pattern);
 

File api/src/org/netbeans/modules/jackpot30/impl/pm/CopyFinder.java

             return false;
         }
 
-        if (node == null)
-            return p == null;
+        if (node == null) {
+            if (p == null) return true;
+            if (Utilities.isMultistatementWildcardTree(p.getLeaf())) {
+                return true;
+            }
+            return false;
+        }
 
         if (p != null && p.getLeaf().getKind() == Kind.IDENTIFIER) {
             String ident = ((IdentifierTree) p.getLeaf()).getName().toString();
                         bind = info.getTypes().isAssignable(real, designed);
                     else
                         bind = false;
+                } else {
+                    bind = designed == null;
                 }
 
                 if (bind) {
     }
 
     private Boolean scan(Tree node, Tree p, TreePath pOrigin) {
-        if (node == null || p == null)
-            return node == p;
+        if (node == null && p == null)
+            return true;
 
+        if (node != null && p == null)
+            return false;
+        
         return scan(node, new TreePath(pOrigin, p));
     }
 

File api/src/org/netbeans/modules/jackpot30/impl/pm/CopyFinderBasedBulkSearch.java

         throw new UnsupportedOperationException("Not supported yet.");
     }
 
+    @Override
+    public Map<String, Integer> matchesWithFrequencies(InputStream encoded, BulkPattern pattern) {
+        throw new UnsupportedOperationException("Not supported yet.");
+    }
+
     private static final class BulkPatternImpl extends BulkPattern {
 
         private final Map<Tree, String> pattern2Code;

File api/src/org/netbeans/modules/jackpot30/impl/pm/NFABasedBulkSearch.java

 import java.io.UnsupportedEncodingException;
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.Collections;
 import java.util.EnumMap;
 import java.util.EnumSet;
 import java.util.HashMap;
     @Override
     public boolean matches(InputStream encoded, BulkPattern patternIn) {
         try {
-            return matchesImpl(encoded, patternIn);
+            return !matchesImpl(encoded, patternIn, false).isEmpty();
         } catch (IOException ex) {
             Exceptions.printStackTrace(ex);
             return false;
         }
     }
 
-    private boolean matchesImpl(InputStream encoded, BulkPattern patternIn) throws IOException {
+    public Map<String, Integer> matchesWithFrequencies(InputStream encoded, BulkPattern patternIn) {
+        try {
+            return matchesImpl(encoded, patternIn, true);
+        } catch (IOException ex) {
+            Exceptions.printStackTrace(ex);
+            return Collections.emptyMap();
+        }
+    }
+
+    public Map<String, Integer> matchesImpl(InputStream encoded, BulkPattern patternIn, boolean withFrequencies) throws IOException {
         BulkPatternImpl pattern = (BulkPatternImpl) patternIn;
         final NFA<Input, Res> nfa = pattern.toNFA();
         Stack<NFA.State> skips = new Stack<NFA.State>();
             identifiers.add(new String(baos.toByteArray(), "UTF-8"));
         }
 
+        Map<String, Integer> patternsAndFrequencies = new HashMap<String, Integer>();
         int read = encoded.read();
         
         while (read != (-1)) {
                 
                 for (Res res : nfa.getResults(active)) {
                     if (identifiers.containsAll(pattern.getIdentifiers().get(res.patternIndex))) {
-                        return true;
+                        if (!withFrequencies) {
+                            patternsAndFrequencies.put(res.pattern, 1);
+                            return patternsAndFrequencies;
+                        }
+                        
+                        Integer freqs = patternsAndFrequencies.get(res.pattern);
+
+                        if (freqs == null) freqs = 0;
+
+                        patternsAndFrequencies.put(res.pattern, freqs + 1);
                     }
                 }
 
             }
         }
 
-        return false;
+        return patternsAndFrequencies;
     }
 
     private static final Map<Kind, byte[]> kind2Encoded;

File api/src/org/netbeans/modules/jackpot30/impl/refactoring/Bundle.properties

 FindDuplicatesRefactoringPanel.knowPatterns.text=&Known patterns
 FindDuplicatesRefactoringPanel.examplesButton.text=
 BTN_Examples=Examples
-ExamplesList.jLabel1.text=Examples:
+FindDuplicatesRefactoringPanel.recentButton.toolTipText=Recent Rules
+FindDuplicatesRefactoringPanel.recentButton.text=
+ExamplesList.pattern.contentType=text/x-javahints