You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@netbeans.apache.org by po...@apache.org on 2017/09/03 17:33:33 UTC

[40/51] [partial] incubator-netbeans-jackpot30 git commit: INFRA-15006 Import for http://bits.netbeans.org/download/apache-donation

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-jackpot30/blob/9ed0a377/duplicates/ide/impl/src/org/netbeans/modules/jackpot30/impl/duplicates/indexing/DeferredCustomIndexer.java
----------------------------------------------------------------------
diff --git a/duplicates/ide/impl/src/org/netbeans/modules/jackpot30/impl/duplicates/indexing/DeferredCustomIndexer.java b/duplicates/ide/impl/src/org/netbeans/modules/jackpot30/impl/duplicates/indexing/DeferredCustomIndexer.java
new file mode 100644
index 0000000..40945e6
--- /dev/null
+++ b/duplicates/ide/impl/src/org/netbeans/modules/jackpot30/impl/duplicates/indexing/DeferredCustomIndexer.java
@@ -0,0 +1,500 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2009-2010 Sun Microsystems, Inc. All rights reserved.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ *
+ * Contributor(s):
+ *
+ * Portions Copyrighted 2009-2010 Sun Microsystems, Inc.
+ */
+
+package org.netbeans.modules.jackpot30.impl.duplicates.indexing;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.io.Writer;
+import java.net.URL;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import javax.swing.JButton;
+import javax.swing.SwingUtilities;
+import org.netbeans.api.annotations.common.NonNull;
+import org.netbeans.api.java.classpath.ClassPath;
+import org.netbeans.api.java.source.CancellableTask;
+import org.netbeans.api.java.source.ClasspathInfo;
+import org.netbeans.api.java.source.CompilationController;
+import org.netbeans.api.java.source.CompilationInfo;
+import org.netbeans.api.java.source.JavaSource;
+import org.netbeans.api.java.source.JavaSource.Phase;
+import org.netbeans.api.java.source.JavaSource.Priority;
+import org.netbeans.api.java.source.JavaSourceTaskFactory;
+import org.netbeans.api.java.source.Task;
+import org.netbeans.api.progress.ProgressHandle;
+import org.netbeans.api.progress.ProgressHandleFactory;
+import org.netbeans.modules.parsing.impl.indexing.CacheFolder;
+import org.netbeans.modules.parsing.impl.indexing.SPIAccessor;
+import org.netbeans.modules.parsing.spi.indexing.Context;
+import org.netbeans.modules.parsing.spi.indexing.CustomIndexer;
+import org.netbeans.modules.parsing.spi.indexing.CustomIndexerFactory;
+import org.netbeans.modules.parsing.spi.indexing.Indexable;
+import org.openide.DialogDescriptor;
+import org.openide.DialogDisplayer;
+import org.openide.filesystems.FileAlreadyLockedException;
+import org.openide.filesystems.FileObject;
+import org.openide.filesystems.FileUtil;
+import org.openide.filesystems.URLMapper;
+import org.openide.util.Cancellable;
+import org.openide.util.Exceptions;
+import org.openide.util.Lookup;
+import org.openide.util.lookup.ServiceProvider;
+
+/**
+ *
+ * @author lahvac
+ */
+public abstract class DeferredCustomIndexer extends CustomIndexer {
+
+    private static final Logger LOG = Logger.getLogger(DeferredCustomIndexer.class.getName());
+    
+    private final DeferredCustomIndexerFactory factory;
+
+    protected DeferredCustomIndexer(DeferredCustomIndexerFactory factory) {
+        this.factory = factory;
+    }
+
+    protected abstract void doIndex(DeferredContext ctx, Collection<? extends FileObject> modifiedAndAdded, Collection<? extends String> removed) throws IOException;
+
+    @Override
+    protected final void index(Iterable<? extends Indexable> files, Context context) {
+        update(factory, context.getRootURI(), files, Collections.<Indexable>emptyList());
+    }
+
+    private static void dump(File where, Iterable<? extends String> lines) {
+        Writer out = null;
+
+        try {
+            out = new BufferedWriter(new OutputStreamWriter(FileUtil.createData(where).getOutputStream(), "UTF-8"));
+            
+            for (String line : lines) {
+                out.write(line);
+                out.write("\n");
+            }
+        } catch (FileAlreadyLockedException ex) {
+            Exceptions.printStackTrace(ex);
+        } catch (IOException ex) {
+            Exceptions.printStackTrace(ex);
+        } finally {
+            if (out != null) {
+                try {
+                    out.close();
+                } catch (IOException ex) {
+                    Exceptions.printStackTrace(ex);
+                }
+            }
+        }
+    }
+
+    private static Set<String> gatherRelativePaths(Iterable<? extends Indexable> it) {
+        Set<String> result = new HashSet<String>();
+
+        for (Indexable i : it) {
+            result.add(i.getRelativePath());
+        }
+
+        return result;
+    }
+
+    private static void update(DeferredCustomIndexerFactory factory, URL root, Iterable<? extends Indexable> modified, Iterable<? extends Indexable> deleted) {
+        try {
+            Set<String> mod = gatherRelativePaths(modified);
+            Set<String> del = gatherRelativePaths(deleted);
+
+            File cacheRoot = cacheRoot(root, factory);
+            
+            File modifiedFile = new File(cacheRoot, "modified");
+            FileObject modifiedFileFO = FileUtil.toFileObject(modifiedFile);
+            Set<String> modifiedFiles = modifiedFileFO != null ? new HashSet<String>(modifiedFileFO.asLines("UTF-8")) : new HashSet<String>();
+            boolean modifiedFilesChanged = modifiedFiles.removeAll(del);
+
+            modifiedFilesChanged |= modifiedFiles.addAll(mod);
+
+            if (modifiedFilesChanged) {
+                dump(modifiedFile, modifiedFiles);
+            }
+
+            File deletedFile = new File(cacheRoot, "deleted");
+            FileObject deletedFileFO = FileUtil.toFileObject(deletedFile);
+            Set<String> deletedFiles = deletedFileFO != null ? new HashSet<String>(deletedFileFO.asLines("UTF-8")) : new HashSet<String>();
+
+            boolean deletedFilesChanged = deletedFiles.removeAll(mod);
+
+            deletedFilesChanged |= deletedFiles.addAll(del);
+
+            if (deletedFilesChanged) {
+                dump(deletedFile, deletedFiles);
+            }
+
+            if (!modifiedFiles.isEmpty() || !deletedFiles.isEmpty()) {
+                add2TODO(root, factory);
+            }
+        } catch (IOException ex) {
+            Exceptions.printStackTrace(ex);
+        }
+    }
+    
+    public static abstract class DeferredCustomIndexerFactory extends CustomIndexerFactory {
+
+        public abstract DeferredCustomIndexer createIndexer();
+
+        @Override
+        public final void filesDeleted(Iterable<? extends Indexable> deleted, Context context) {
+            update(this, context.getRootURI(), Collections.<Indexable>emptyList(), deleted);
+        }
+
+        @Override
+        public final void filesDirty(Iterable<? extends Indexable> dirty, Context context) {}
+
+        @Override
+        public final boolean supportsEmbeddedIndexers() {
+            return false;
+        }
+
+        @Override
+        public final void rootsRemoved(Iterable<? extends URL> removedRoots) {
+            super.rootsRemoved(removedRoots);
+        }
+
+        @Override
+        public final void scanFinished(Context context) {
+            super.scanFinished(context);
+        }
+
+        @Override
+        public final boolean scanStarted(Context context) {
+            return super.scanStarted(context);
+        }
+
+        public void updateIndex(final URL root, final AtomicBoolean cancel) throws IOException {
+            final FileObject rootFO = URLMapper.findFileObject(root);
+            final ClasspathInfo cpInfo = ClasspathInfo.create(ClassPath.EMPTY, ClassPath.EMPTY, ClassPath.EMPTY);
+
+            JavaSource.create(cpInfo).runUserActionTask(new Task<CompilationController>() {
+                public void run(CompilationController parameter) throws Exception {
+                    if (cancel.get()) return ;
+                    updateRoot(DeferredCustomIndexerFactory.this, root, rootFO, cancel);
+                }
+            }, true);
+        }
+
+    }
+
+    public static final class DeferredContext {
+        private final @NonNull URL root;
+        private final @NonNull FileObject rootFileObject;
+        private final @NonNull FileObject cacheRoot;
+        private final @NonNull Set<? extends FileObject> modifiedAndAdded;
+        private final @NonNull Set<? extends String> removed;
+        private final @NonNull AtomicBoolean cancel;
+
+        public DeferredContext(URL root, FileObject rootFileObject, FileObject cacheRoot, Set<? extends FileObject> modifiedAndAdded, Set<? extends String> removed, AtomicBoolean cancel) {
+            this.root = root;
+            this.rootFileObject = rootFileObject;
+            this.cacheRoot = cacheRoot;
+            this.modifiedAndAdded = modifiedAndAdded;
+            this.removed = removed;
+            this.cancel = cancel;
+        }
+
+        public @NonNull URL getRoot() {
+            return root;
+        }
+
+        public @NonNull FileObject getRootFileObject() {
+            return rootFileObject;
+        }
+
+        public FileObject getCacheRoot() {
+            return cacheRoot;
+        }
+
+        public boolean isCancelled() {
+            return cancel.get();
+        }
+
+        public void handledModifiedFile(FileObject file) {
+            modifiedAndAdded.remove(file);
+        }
+
+        public void handledRemovedFile(String relative) {
+            removed.remove(relative);
+        }
+    }
+
+    /*return: true == done*/
+    private static boolean updateRoot(DeferredCustomIndexerFactory factory, URL root, FileObject rootFO, AtomicBoolean cancel) throws IOException {
+        LOG.log(Level.FINE, "updating: {0}, for indexer: {1}", new Object[] {root.toExternalForm(), factory.getIndexerName()});
+         File cacheRoot = cacheRoot(root, factory);
+         FileObject deletedFile = FileUtil.toFileObject(new File(cacheRoot, "deleted"));
+         Set<String> deletedFiles = deletedFile != null ? new HashSet<String>(deletedFile.asLines("UTF-8")) : Collections.<String>emptySet();
+
+         FileObject modifiedFile = FileUtil.toFileObject(new File(cacheRoot, "modified"));
+         Set<String> modifiedFiles = modifiedFile != null ? new HashSet<String>(modifiedFile.asLines("UTF-8")) : Collections.<String>emptySet();
+
+         Set<FileObject> toIndex = new HashSet<FileObject>();
+
+         for (String r : modifiedFiles) {
+             FileObject f = rootFO.getFileObject(r);
+
+             if (f != null) {
+                 toIndex.add(f);
+             }
+         }
+
+         if (!toIndex.isEmpty() || !modifiedFiles.isEmpty()) {
+             factory.createIndexer().doIndex(new DeferredContext(root, rootFO, FileUtil.toFileObject(cacheRoot), toIndex, deletedFiles, cancel), new HashSet<FileObject>(toIndex), new HashSet<String>(deletedFiles));
+         }
+
+         boolean done = true;
+
+         if (deletedFile != null) {
+             if (deletedFiles.isEmpty()) {
+                 deletedFile.delete();
+             } else {
+                 dump(new File(cacheRoot, "deleted"), deletedFiles);
+                 done = false;
+             }
+         }
+         if (modifiedFile != null) {
+             if (toIndex.isEmpty()) {
+                 modifiedFile.delete();
+             }  else {
+                 modifiedFiles.clear();
+
+                 for (FileObject f : toIndex) {
+                     modifiedFiles.add(FileUtil.getRelativePath(rootFO, f));
+                 }
+
+                 dump(new File(cacheRoot, "modified"), modifiedFiles);
+                 done = false;
+             }
+         }
+
+         return done;
+    }
+
+    private static final Map<String, TODO> todo = new HashMap<String, TODO>(); //XXX: synchronization!!!
+
+    private static void add2TODO(URL root, DeferredCustomIndexerFactory factory) {
+        if (DISABLED_INDEXERS.contains(factory.getIndexerName())) return;
+        
+        boolean wasEmpty = todo.isEmpty();
+        TODO roots = todo.get(factory.getIndexerName());
+
+        if (roots == null) {
+            todo.put(factory.getIndexerName(), roots = new TODO(factory));
+        }
+
+        roots.roots.add(root);
+
+        LOG.log(Level.FINE, "add2TODO, root: {0}, for factory: {1}, wasEmpty: {2}, todo: {3}", new Object[] {root.toExternalForm(), factory.getIndexerName(), wasEmpty, todo.toString()});
+        
+        if (wasEmpty) RunAsNeededFactory.fileChanged();
+        else RunAsNeededFactory.refresh();
+    }
+    
+    private static File cacheRoot(URL root, CustomIndexerFactory factory) throws IOException {
+        FileObject indexBaseFolder = CacheFolder.getDataFolder(root);
+        String path = SPIAccessor.getInstance().getIndexerPath(factory.getIndexerName(), factory.getIndexVersion());
+        FileObject indexFolder = FileUtil.createFolder(indexBaseFolder, path);
+        return FileUtil.toFile(indexFolder);
+    }
+
+    private static final Set<String> DISABLED_INDEXERS = Collections.synchronizedSet(new HashSet<String>());
+
+    private static class UpdateWorker implements CancellableTask<CompilationInfo> {
+
+        private static ProgressHandle progressForCurrentFactory;
+        private static DeferredCustomIndexerFactory currentFactory;
+        
+        private final AtomicBoolean cancel = new AtomicBoolean();
+
+        public void run(CompilationInfo parameter) throws Exception {
+            cancel.set(false);
+
+            for (Iterator<Entry<String, TODO>> it = todo.entrySet().iterator(); it.hasNext();) {
+                if (cancel.get()) return;
+
+                final Entry<String, TODO> e = it.next();
+
+                if (DISABLED_INDEXERS.contains(e.getKey())) {
+                    it.remove();
+                    continue;
+                }
+                
+                if (currentFactory != e.getValue().factory) {
+                    if (progressForCurrentFactory != null) {
+                        progressForCurrentFactory.finish();
+                    }
+
+                    currentFactory = e.getValue().factory;
+                    progressForCurrentFactory = ProgressHandleFactory.createSystemHandle("Background indexing for: " + currentFactory.getIndexerName(), new Cancellable() {
+                        public boolean cancel() {
+                            assert SwingUtilities.isEventDispatchThread();
+
+                            JButton disableInThisSession = new JButton("Disable in This Session");
+                            JButton disablePermanently = new JButton("Disable Permanently");
+
+                            disablePermanently.setEnabled(false);
+
+                            Object[] buttons = new Object[]{disableInThisSession, disablePermanently, DialogDescriptor.CANCEL_OPTION};
+                            DialogDescriptor dd = new DialogDescriptor("Disable background indexing for: " + e.getValue().factory.getIndexerName(), "Disable Background Indexing", true, buttons, disableInThisSession, DialogDescriptor.DEFAULT_ALIGN, null, null);
+
+                            dd.setClosingOptions(buttons);
+
+                            Object result = DialogDisplayer.getDefault().notify(dd);
+
+                            if (result == disableInThisSession) {
+                                DISABLED_INDEXERS.add(e.getKey());
+                                return true;
+                            } else if (result == disablePermanently) {
+                                throw new UnsupportedOperationException();
+                            } else {
+                                return false;
+                            }
+                        }
+                    });
+
+                    progressForCurrentFactory.start();
+                }
+
+                for (Iterator<URL> factIt = e.getValue().roots.iterator(); factIt.hasNext();) {
+                    if (cancel.get()) return;
+
+                    URL root = factIt.next();
+                    FileObject rootFO = URLMapper.findFileObject(root);
+
+                    if (rootFO == null) {
+                        //already deleted
+                        it.remove();
+                        continue;
+                    }
+
+                    if (updateRoot(e.getValue().factory, root, rootFO, cancel)) {
+                        factIt.remove();
+                    } else {
+                        if (!cancel.get()) {
+                            LOG.log(Level.WARNING, "indexer: {0} did not update all files even if the process was not cancelled", currentFactory.getIndexerName());
+                        }
+                    }
+                }
+
+                if (e.getValue().roots.isEmpty())
+                    it.remove();
+
+                progressForCurrentFactory.finish();
+                progressForCurrentFactory = null;
+                currentFactory = null;
+            }
+
+            if (todo.isEmpty()) RunAsNeededFactory.fileChanged();
+        }
+
+        public void cancel() {
+            cancel.set(true);
+        }
+    }
+
+    private static final class TODO {
+        final DeferredCustomIndexerFactory factory;
+        final Collection<URL> roots = new HashSet<URL>();
+        TODO(DeferredCustomIndexerFactory factory) {
+            this.factory = factory;
+        }
+    }
+
+    private static final boolean DEFERRED_INDEXER_ENABLED = Boolean.getBoolean(DeferredCustomIndexerFactory.class.getName() + ".enable");
+    
+    private static final FileObject EMPTY_FILE;
+
+    static {
+        try {
+            EMPTY_FILE = FileUtil.createMemoryFileSystem().getRoot().createData("empty.java");
+        } catch (IOException ex) {
+            throw new IllegalStateException(ex);
+        }
+    }
+
+    @ServiceProvider(service=JavaSourceTaskFactory.class)
+    public static final class RunAsNeededFactory extends JavaSourceTaskFactory {
+
+        public RunAsNeededFactory() {
+            super(Phase.PARSED, Priority.MIN);
+        }
+
+        @Override
+        protected CancellableTask<CompilationInfo> createTask(FileObject file) {
+            return new UpdateWorker();
+        }
+
+        @Override
+        protected Collection<FileObject> getFileObjects() {
+            return DEFERRED_INDEXER_ENABLED && !todo.isEmpty() ? Collections.singletonList(EMPTY_FILE) : Collections.<FileObject>emptyList();
+        }
+
+        public static void fileChanged() {
+            for (JavaSourceTaskFactory f : Lookup.getDefault().lookupAll(JavaSourceTaskFactory.class)) {
+                if (f instanceof RunAsNeededFactory) {
+                    ((RunAsNeededFactory) f).fileObjectsChanged();
+                }
+            }
+        }
+
+        public static void refresh() {
+            for (JavaSourceTaskFactory f : Lookup.getDefault().lookupAll(JavaSourceTaskFactory.class)) {
+                if (f instanceof RunAsNeededFactory) {
+                    ((RunAsNeededFactory) f).reschedule(EMPTY_FILE);
+                }
+            }
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-jackpot30/blob/9ed0a377/duplicates/ide/impl/src/org/netbeans/modules/jackpot30/impl/duplicates/indexing/DuplicatesCustomIndexerImpl.java
----------------------------------------------------------------------
diff --git a/duplicates/ide/impl/src/org/netbeans/modules/jackpot30/impl/duplicates/indexing/DuplicatesCustomIndexerImpl.java b/duplicates/ide/impl/src/org/netbeans/modules/jackpot30/impl/duplicates/indexing/DuplicatesCustomIndexerImpl.java
new file mode 100644
index 0000000..434ea46
--- /dev/null
+++ b/duplicates/ide/impl/src/org/netbeans/modules/jackpot30/impl/duplicates/indexing/DuplicatesCustomIndexerImpl.java
@@ -0,0 +1,131 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2009-2010 Sun Microsystems, Inc. All rights reserved.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ *
+ * Contributor(s):
+ *
+ * Portions Copyrighted 2009-2010 Sun Microsystems, Inc.
+ */
+
+package org.netbeans.modules.jackpot30.impl.duplicates.indexing;
+
+import java.io.IOException;
+import java.net.URL;
+import java.util.Collection;
+import org.netbeans.api.java.source.ClasspathInfo;
+import org.netbeans.api.java.source.CompilationController;
+import org.netbeans.api.java.source.JavaSource;
+import org.netbeans.api.java.source.JavaSource.Phase;
+import org.netbeans.api.java.source.Task;
+import org.netbeans.modules.parsing.impl.indexing.IndexableImpl;
+import org.netbeans.modules.parsing.impl.indexing.SPIAccessor;
+import org.openide.filesystems.FileObject;
+import org.openide.util.Exceptions;
+
+/**
+ *
+ * @author lahvac
+ */
+public class DuplicatesCustomIndexerImpl extends DeferredCustomIndexer {
+
+    public DuplicatesCustomIndexerImpl(DeferredCustomIndexerFactory factory) {
+        super(factory);
+    }
+
+    protected void doIndex(final DeferredContext ctx, Collection<? extends FileObject> modifiedAndAdded, Collection<? extends String> removed) throws IOException {
+        final DuplicatesIndex[] w = new DuplicatesIndex[1];
+
+        try {
+            w[0] = new DuplicatesIndex(ctx.getRoot(), ctx.getCacheRoot());
+
+            for (String r : removed) {
+                w[0].remove(r);
+                ctx.handledRemovedFile(r);
+            }
+
+            final ClasspathInfo cpInfo = ClasspathInfo.create(ctx.getRootFileObject());
+
+            JavaSource.create(cpInfo, modifiedAndAdded).runUserActionTask(new Task<CompilationController>() {
+                public void run(final CompilationController cc) throws Exception {
+                    if (cc.toPhase(Phase.RESOLVED).compareTo(Phase.RESOLVED) < 0)
+                        return ;
+
+                    w[0].record(cc, SPIAccessor.getInstance().create(new IndexableImpl() {
+                        @Override public String getRelativePath() {
+                            throw new UnsupportedOperationException("Not supported yet.");
+                        }
+                        @Override public URL getURL() {
+                            return cc.getFileObject().toURL();
+                        }
+                        @Override public String getMimeType() {
+                            throw new UnsupportedOperationException("Not supported yet.");
+                        }
+                        @Override public boolean isTypeOf(String mimeType) {
+                            throw new UnsupportedOperationException("Not supported yet.");
+                        }
+                    }), cc.getCompilationUnit());
+
+                    ctx.handledModifiedFile(cc.getFileObject());
+                }
+            }, true);
+        } finally {
+            if (w[0] != null) {
+                try {
+                    w[0].close();
+                } catch (IOException ex) {
+                    Exceptions.printStackTrace(ex);
+                }
+            }
+        }
+    }
+    
+    public static final class FactoryImpl extends DeferredCustomIndexerFactory {
+
+        @Override
+        public DeferredCustomIndexer createIndexer() {
+            return new DuplicatesCustomIndexerImpl(this);
+        }
+
+        @Override
+        public String getIndexerName() {
+            return DuplicatesIndex.NAME;
+        }
+
+        @Override
+        public int getIndexVersion() {
+            return DuplicatesIndex.VERSION;
+        }
+
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-jackpot30/blob/9ed0a377/duplicates/ide/impl/src/org/netbeans/modules/jackpot30/impl/duplicates/indexing/DuplicatesIndex.java
----------------------------------------------------------------------
diff --git a/duplicates/ide/impl/src/org/netbeans/modules/jackpot30/impl/duplicates/indexing/DuplicatesIndex.java b/duplicates/ide/impl/src/org/netbeans/modules/jackpot30/impl/duplicates/indexing/DuplicatesIndex.java
new file mode 100644
index 0000000..7d94830
--- /dev/null
+++ b/duplicates/ide/impl/src/org/netbeans/modules/jackpot30/impl/duplicates/indexing/DuplicatesIndex.java
@@ -0,0 +1,119 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2009-2010 Sun Microsystems, Inc. All rights reserved.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ *
+ * Contributor(s):
+ *
+ * Portions Copyrighted 2009-2010 Sun Microsystems, Inc.
+ */
+
+package org.netbeans.modules.jackpot30.impl.duplicates.indexing;
+
+import com.sun.source.tree.CompilationUnitTree;
+import com.sun.source.util.Trees;
+import java.io.IOException;
+import java.net.URL;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import org.apache.lucene.document.Document;
+import org.apache.lucene.document.Field;
+import org.apache.lucene.document.Field.Index;
+import org.apache.lucene.document.Field.Store;
+import org.apache.lucene.index.Term;
+import org.netbeans.api.java.source.CompilationInfo;
+import org.netbeans.modules.jackpot30.common.api.IndexAccess;
+import org.netbeans.modules.jackpot30.impl.duplicates.ComputeDuplicates;
+import org.netbeans.modules.parsing.spi.indexing.Indexable;
+import org.openide.filesystems.FileObject;
+import org.openide.util.Lookup;
+
+/**
+ *
+ * @author lahvac
+ */
+public class DuplicatesIndex {
+
+    private final org.apache.lucene.index.IndexWriter luceneWriter;
+
+    public DuplicatesIndex(URL sourceRoot, FileObject cacheRoot) throws IOException {
+        luceneWriter = Lookup.getDefault().lookup(IndexAccess.class).getIndexWriter(sourceRoot, cacheRoot, NAME);
+    }
+
+    public void record(final CompilationInfo info, Indexable idx, final CompilationUnitTree cut) throws IOException {
+        record(info.getTrees(), idx, cut);
+    }
+
+    public void record(final Trees trees, Indexable idx, final CompilationUnitTree cut) throws IOException {
+        String relative = Lookup.getDefault().lookup(IndexAccess.class).getRelativePath(idx);
+
+        try {
+            final Document doc = new Document();
+
+            doc.add(new Field("duplicatesPath", relative, Field.Store.YES, Field.Index.NOT_ANALYZED));
+
+            final Map<String, long[]> positions = ComputeDuplicates.encodeGeneralized(trees, cut);
+
+            for (Entry<String, long[]> e : positions.entrySet()) {
+                doc.add(new Field("duplicatesGeneralized", e.getKey(), Store.YES, Index.NOT_ANALYZED));
+
+                StringBuilder positionsSpec = new StringBuilder();
+
+                for (int i = 0; i < e.getValue().length; i += 2) {
+                    if (positionsSpec.length() > 0) positionsSpec.append(';');
+                    positionsSpec.append(e.getValue()[i]).append(':').append(e.getValue()[i + 1] - e.getValue()[i]);
+                }
+
+                doc.add(new Field("duplicatesPositions", positionsSpec.toString(), Store.YES, Index.NO));
+            }
+
+            luceneWriter.addDocument(doc);
+        } catch (ThreadDeath td) {
+            throw td;
+        } catch (Throwable t) {
+            Logger.getLogger(DuplicatesIndex.class.getName()).log(Level.WARNING, null, t);
+        }
+    }
+
+    public void remove(String relativePath) throws IOException {
+        luceneWriter.deleteDocuments(new Term("duplicatesPath", relativePath));
+    }
+
+    public void close() throws IOException {
+        Lookup.getDefault().lookup(IndexAccess.class).finish();
+    }
+
+    public static final String NAME = "duplicates"; //NOI18N
+    public static final int    VERSION = 1; //NOI18N
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-jackpot30/blob/9ed0a377/duplicates/ide/impl/src/org/netbeans/modules/jackpot30/impl/duplicates/indexing/RemoteDuplicatesIndex.java
----------------------------------------------------------------------
diff --git a/duplicates/ide/impl/src/org/netbeans/modules/jackpot30/impl/duplicates/indexing/RemoteDuplicatesIndex.java b/duplicates/ide/impl/src/org/netbeans/modules/jackpot30/impl/duplicates/indexing/RemoteDuplicatesIndex.java
new file mode 100644
index 0000000..197154e
--- /dev/null
+++ b/duplicates/ide/impl/src/org/netbeans/modules/jackpot30/impl/duplicates/indexing/RemoteDuplicatesIndex.java
@@ -0,0 +1,360 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2010 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ *
+ * Contributor(s):
+ *
+ * Portions Copyrighted 2010 Sun Microsystems, Inc.
+ */
+
+package org.netbeans.modules.jackpot30.impl.duplicates.indexing;
+
+import java.io.IOException;
+import java.net.URI;
+import java.net.URISyntaxException;
+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.Iterator;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.TreeMap;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import org.apache.lucene.document.Document;
+import org.apache.lucene.document.Field;
+import org.apache.lucene.document.Field.Index;
+import org.apache.lucene.document.Field.Store;
+import org.apache.lucene.index.IndexReader;
+import org.apache.lucene.index.IndexWriter;
+import org.apache.lucene.index.Term;
+import org.apache.lucene.search.Collector;
+import org.apache.lucene.search.IndexSearcher;
+import org.apache.lucene.search.Query;
+import org.apache.lucene.search.Searcher;
+import org.apache.lucene.search.TermQuery;
+import org.codeviation.pojson.Pojson;
+import org.netbeans.modules.jackpot30.common.api.LuceneHelpers.BitSetCollector;
+import org.netbeans.modules.jackpot30.impl.duplicates.ComputeDuplicates.DuplicateDescription;
+import org.netbeans.modules.jackpot30.impl.duplicates.ComputeDuplicates.Span;
+import org.netbeans.modules.jackpot30.remoting.api.LocalCache;
+import org.netbeans.modules.jackpot30.remoting.api.LocalCache.Task;
+import org.netbeans.modules.jackpot30.remoting.api.RemoteIndex;
+import org.netbeans.modules.jackpot30.remoting.api.WebUtilities;
+import org.openide.filesystems.FileObject;
+import org.openide.filesystems.URLMapper;
+import org.openide.util.Exceptions;
+
+/**
+ *
+ * @author lahvac
+ */
+@SuppressWarnings("ClassWithMultipleLoggers")
+public class RemoteDuplicatesIndex {
+
+    private static final Logger TIMER = Logger.getLogger("TIMER");
+
+    public static List<DuplicateDescription> findDuplicates(Map<String, long[]> hashes, FileObject currentFile, AtomicBoolean cancel) throws IOException, URISyntaxException {
+        return translate(hashes, findHashOccurrences(hashes.keySet(), currentFile, cancel), currentFile);
+    }
+
+    private static Map<String, Map<RemoteIndex, Collection<String>>> findHashOccurrences(Collection<? extends String> hashes, FileObject currentFile, AtomicBoolean cancel) throws IOException, URISyntaxException {
+        Map<URI, Collection<RemoteIndex>> indices = new LinkedHashMap<URI, Collection<RemoteIndex>>();
+
+        for (RemoteIndex ri : RemoteIndex.loadIndices()) {
+            try {
+                URI uri = ri.remote.toURI();
+                Collection<RemoteIndex> list = indices.get(uri);
+
+                if (list == null) {
+                    indices.put(uri, list = new ArrayList<RemoteIndex>());
+                }
+
+                list.add(ri);
+            } catch (URISyntaxException ex) {
+                Exceptions.printStackTrace(ex);
+            }
+        }
+
+        Map<String, Map<RemoteIndex, Collection<String>>> result = new LinkedHashMap<String, Map<RemoteIndex, Collection<String>>>();
+        long localTime = 0;
+        long remoteTime = 0;
+
+        for (RemoteIndex ri : RemoteIndex.loadIndices()) {
+            if (cancel.get()) return Collections.emptyMap();
+            
+            Set<String> toProcess = new LinkedHashSet<String>(hashes);
+            Map<String, Map<String, Collection<? extends String>>> indexResult = new LinkedHashMap<String, Map<String, Collection<? extends String>>>();
+
+            long locS = System.currentTimeMillis();
+            indexResult.putAll(findHashOccurrencesInLocalCache(ri, toProcess, cancel));
+            localTime += System.currentTimeMillis() - locS;
+
+            toProcess.removeAll(indexResult.keySet());
+
+            if (!toProcess.isEmpty()) {
+                long remS = System.currentTimeMillis();
+                Map<String, Map<String, Collection<? extends String>>> remoteResults = findHashOccurrencesRemote(ri.remote.toURI(), toProcess, cancel);
+                remoteTime += System.currentTimeMillis() - remS;
+
+                Map<String, Map<String, Collection<? extends String>>> toSave = new LinkedHashMap<String, Map<String, Collection<? extends String>>>(remoteResults);
+
+                for (String hash : toProcess) {
+                    if (!toSave.containsKey(hash)) {
+                        toSave.put(hash, Collections.<String, Collection<? extends String>>emptyMap());
+                    }
+                }
+
+                if (cancel.get()) return Collections.emptyMap();
+                
+                saveToLocalCache(ri, toSave);
+
+                indexResult.putAll(remoteResults);
+            }
+
+            for (Entry<String, Map<String, Collection<? extends String>>> e : indexResult.entrySet()) {
+                Map<RemoteIndex, Collection<String>> hashResult = result.get(e.getKey());
+
+                if (hashResult == null) {
+                    result.put(e.getKey(), hashResult = new LinkedHashMap<RemoteIndex, Collection<String>>());
+                }
+
+                for (Entry<String, Collection<? extends String>> insideHash : e.getValue().entrySet()) {
+                    if (cancel.get()) return Collections.emptyMap();
+
+                    Collection<String> dupes = hashResult.get(ri);
+
+                    if (dupes == null) {
+                        hashResult.put(ri, dupes = new LinkedHashSet<String>());
+                    }
+
+                    dupes.addAll(insideHash.getValue());
+                }
+            }
+        }
+
+        TIMER.log(Level.FINE, "local hash duplicates", new Object[] {currentFile, localTime});
+        TIMER.log(Level.FINE, "remote hash duplicates", new Object[] {currentFile, remoteTime});
+        return result;
+    }
+
+    private static Map<String, Map<String, Collection<? extends String>>> findHashOccurrencesRemote(URI remoteIndex, Iterable<? extends String> hashes, AtomicBoolean cancel) {
+        try {
+            String indexURL = remoteIndex.toASCIIString();
+            URI u = new URI(indexURL + "/duplicates/findDuplicates?hashes=" + WebUtilities.escapeForQuery(Pojson.save(hashes)));
+            String hashesMap = WebUtilities.requestStringResponse(u, cancel);
+
+            if (hashesMap == null || cancel.get()) {
+                //some kind of error while getting the duplicates (cannot access remote server)?
+                //ignore:
+                return Collections.emptyMap();
+            }
+            return Pojson.load(LinkedHashMap.class, hashesMap);
+        } catch (URISyntaxException ex) {
+            //XXX: better handling?
+            Exceptions.printStackTrace(ex);
+            return Collections.emptyMap();
+        }
+    }
+
+    private static Map<String, Map<String, Collection<? extends String>>> findHashOccurrencesInLocalCache(RemoteIndex ri, final Iterable<? extends String> hashes, AtomicBoolean cancel) throws IOException, URISyntaxException {
+        return LocalCache.runOverLocalCache(ri, new Task<IndexReader, Map<String, Map<String, Collection<? extends String>>>>() {
+            @Override public Map<String, Map<String, Collection<? extends String>>> run(IndexReader reader, AtomicBoolean cancel) throws IOException {
+                Map<String, Map<String, Collection<String>>> result = new LinkedHashMap<String, Map<String, Collection<String>>>();
+
+                for (Entry<String, Collection<? extends String>> e : containsHash(reader, hashes, cancel).entrySet()) {
+                    if (cancel.get()) return Collections.emptyMap();
+
+                    Map<String, Collection<String>> forHash = result.get(e.getKey());
+
+                    if (forHash == null) {
+                        result.put(e.getKey(), forHash = new LinkedHashMap<String, Collection<String>>());
+                    }
+
+                    for (String path : e.getValue()) {
+                        String segment = path.substring(0, path.indexOf('/'));
+
+                        path = path.substring(path.indexOf('/') + 1);
+
+                        Collection<String> list = forHash.get(segment);
+
+                        if (list == null) {
+                            forHash.put(segment, list = new LinkedList<String>());
+                        }
+
+                        list.add(path);
+                    }
+                }
+
+                return (Map)result; //XXX
+            }
+        }, Collections.<String, Map<String, Collection<? extends String>>>emptyMap(), cancel);
+    }
+
+    private static synchronized void saveToLocalCache(RemoteIndex ri, final Map<String, Map<String, Collection<? extends String>>> what) throws IOException, URISyntaxException {
+        LocalCache.saveToLocalCache(ri, new Task<IndexWriter, Void>() {
+            @Override public Void run(IndexWriter w, AtomicBoolean cancel) throws IOException {
+                for (Entry<String, Map<String, Collection<? extends String>>> e : what.entrySet()) {
+                    Document doc = new Document();
+
+                    doc.add(new Field("hash", e.getKey(), Store.YES, Index.NOT_ANALYZED));
+
+                    for (Entry<String, Collection<? extends String>> pe : e.getValue().entrySet()) {
+                        for (String path : pe.getValue()) {
+                            doc.add(new Field("path", pe.getKey() + "/" + path, Store.YES, Index.NO));
+                        }
+                    }
+
+                    w.addDocument(doc);
+                }
+
+                return null;
+            }
+        });
+    }
+    
+    private static List<DuplicateDescription> translate(Map<String, long[]> hashes, Map<String, Map<RemoteIndex, Collection<String>>> occ, FileObject currentFile) {
+        Map<String, Map<RemoteIndex, Collection<String>>> sorted = hashMap();
+        Map<long[], DuplicateDescription> result = new LinkedHashMap<long[], DuplicateDescription>();
+        List<long[]> seen = new LinkedList<long[]>();
+
+        sorted.putAll(occ);
+
+        OUTER: for (Entry<String, Map<RemoteIndex, Collection<String>>> e : occ.entrySet()) {
+            long[] currentSpan = hashes.get(e.getKey());
+
+            for (Iterator<Entry<long[], DuplicateDescription>> it = result.entrySet().iterator(); it.hasNext();) {
+                Entry<long[], DuplicateDescription> span = it.next();
+
+                if (span.getKey()[0] <= currentSpan[0] && span.getKey()[1] >= currentSpan[1]) {
+                    continue OUTER;
+                }
+
+                if (currentSpan[0] <= span.getKey()[0] && currentSpan[1] >= span.getKey()[1]) {
+                    it.remove();
+                }
+            }
+
+            if (currentSpan[0] == (-1) || currentSpan[1] == (-1)) continue;
+            
+            seen.add(currentSpan);
+            
+            String longest = e.getKey();
+            List<Span> foundDuplicates = new LinkedList<Span>();
+
+            for (Entry<RemoteIndex, Collection<String>> root2Occurrences : e.getValue().entrySet()) {
+                FileObject localRoot = URLMapper.findFileObject(root2Occurrences.getKey().getLocalFolder());
+
+                for (String cand : root2Occurrences.getValue()) {
+                    FileObject o = localRoot.getFileObject(cand);
+
+                    if (o == null) continue; //XXX log!
+                    if (areEquivalent(currentFile, o)) continue;
+                    
+                    foundDuplicates.add(new Span(o, -1, -1));
+                }
+            }
+
+            if (foundDuplicates.isEmpty()) continue;
+            
+            DuplicateDescription current = DuplicateDescription.of(foundDuplicates, getValue(longest), longest);
+
+            result.put(currentSpan, current);
+        }
+
+        return new LinkedList<DuplicateDescription>(result.values());
+    }
+
+    private static boolean areEquivalent(FileObject f1, FileObject f2) {
+        return f1.equals(f2);
+    }
+
+    private static long getValue(String encoded) {
+        return Long.parseLong(encoded.substring(encoded.lastIndexOf(":") + 1));
+    }
+
+    private static <T> TreeMap<String, T> hashMap() {
+        return new TreeMap<String, T>(new Comparator<String>() {
+            public int compare(String arg0, String arg1) {
+                return (int) Math.signum(getValue(arg1) - getValue(arg0));
+            }
+        });
+    }
+
+    private static Map<String, Collection<? extends String>> containsHash(IndexReader reader, Iterable<? extends String> hashes, AtomicBoolean cancel) throws IOException {
+        Map<String, Collection<? extends String>> result = new LinkedHashMap<String, Collection<? extends String>>();
+
+        for (String hash : hashes) {
+            if (cancel.get()) return Collections.emptyMap();
+
+            Collection<String> found = new LinkedList<String>();
+            Query query = new TermQuery(new Term("hash", hash));
+            Searcher s = new IndexSearcher(reader);
+            BitSet matchingDocuments = new BitSet(reader.maxDoc());
+            Collector c = new BitSetCollector(matchingDocuments);
+
+            s.search(query, c);
+
+            boolean wasFound = false;
+
+            for (int docNum = matchingDocuments.nextSetBit(0); docNum >= 0; docNum = matchingDocuments.nextSetBit(docNum + 1)) {
+                if (cancel.get()) return Collections.emptyMap();
+
+                final Document doc = reader.document(docNum);
+
+                found.addAll(Arrays.asList(doc.getValues("path")));
+                wasFound = true;
+            }
+
+            if (wasFound) {
+                result.put(hash, found);
+            }
+        }
+
+        return result;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-jackpot30/blob/9ed0a377/duplicates/ide/impl/test/unit/src/org/netbeans/modules/jackpot30/impl/duplicates/ComputeDuplicatesTest.java
----------------------------------------------------------------------
diff --git a/duplicates/ide/impl/test/unit/src/org/netbeans/modules/jackpot30/impl/duplicates/ComputeDuplicatesTest.java b/duplicates/ide/impl/test/unit/src/org/netbeans/modules/jackpot30/impl/duplicates/ComputeDuplicatesTest.java
new file mode 100644
index 0000000..1ae259b
--- /dev/null
+++ b/duplicates/ide/impl/test/unit/src/org/netbeans/modules/jackpot30/impl/duplicates/ComputeDuplicatesTest.java
@@ -0,0 +1,134 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2009-2010 Sun Microsystems, Inc. All rights reserved.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ *
+ * Contributor(s):
+ *
+ * Portions Copyrighted 2009-2010 Sun Microsystems, Inc.
+ */
+
+package org.netbeans.modules.jackpot30.impl.duplicates;
+
+import org.netbeans.api.progress.ProgressHandle;
+import org.openide.filesystems.FileObject;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicBoolean;
+import org.netbeans.api.java.source.TestUtilities;
+import org.netbeans.api.progress.ProgressHandleFactory;
+import org.netbeans.modules.jackpot30.impl.duplicates.ComputeDuplicates.DuplicateDescription;
+import org.netbeans.modules.jackpot30.impl.duplicates.ComputeDuplicates.Span;
+import org.netbeans.modules.jackpot30.common.test.IndexTestBase;
+import org.openide.filesystems.FileUtil;
+import org.openide.util.NbCollections;
+import static org.junit.Assert.*;
+import org.netbeans.api.editor.mimelookup.MimePath;
+import org.netbeans.modules.jackpot30.impl.duplicates.indexing.DuplicatesCustomIndexerImpl;
+import org.netbeans.spi.editor.mimelookup.MimeDataProvider;
+import org.openide.util.Lookup;
+import org.openide.util.lookup.Lookups;
+import org.openide.util.lookup.ServiceProvider;
+
+/**
+ *
+ * @author lahvac
+ */
+public class ComputeDuplicatesTest extends IndexTestBase {
+
+    public ComputeDuplicatesTest(String name) {
+        super(name);
+    }
+
+    public void testDuplicateDuplicates() throws Exception {
+        writeFilesAndWaitForScan(src,
+                                 new File("test/Test1.java", "package test; public class Test1 { private void test() { java.io.File f = null; f.isDirectory(); } }"),
+                                 new File("test/Test2.java", "package test; public class Test2 { private int a; private void test() { java.io.File f = null; f.isDirectory(); } }"),
+                                 new File("test/Test3.java", "package test; public class Test3 { private void test() { for (int i = 0; i < 10; i++) { System.err.println(3 * i); System.err.println(4 * i); } }"));
+
+        verifyDuplicates("test/Test1.java",
+                         "private void test() { java.io.File f = null; f.isDirectory(); }",
+                         "test/Test2.java",
+                         "private void test() { java.io.File f = null; f.isDirectory(); }");
+    }
+
+    public void testCrossIndex() throws Exception {
+        writeFilesAndWaitForScan(src,
+                                 new File("test/Test1.java", "package test; public class Test1 { private void test() { java.io.File f = null; f.isDirectory(); } }"));
+
+        writeFilesAndWaitForScan(src2,
+                                 new File("test/Test2.java", "package test; public class Test2 { private int a; private void test() { java.io.File f = null; f.isDirectory(); } }"));
+
+        verifyDuplicates("test/Test1.java",
+                         "private void test() { java.io.File f = null; f.isDirectory(); }",
+                         "test/Test2.java",
+                         "private void test() { java.io.File f = null; f.isDirectory(); }");
+    }
+
+    private void verifyDuplicates(String... fileAndDuplicateCode) throws Exception {
+        Map<String, String> duplicatesGolden = new HashMap<String, String>();
+
+        for (int cntr = 0; cntr < fileAndDuplicateCode.length; cntr += 2) {
+            duplicatesGolden.put(fileAndDuplicateCode[cntr], fileAndDuplicateCode[cntr + 1]);
+        }
+
+        Map<String, String> duplicatesReal = new HashMap<String, String>();
+        ProgressHandle handle = ProgressHandleFactory.createHandle("test");
+
+        handle.start();
+        
+        for (DuplicateDescription dd : NbCollections.iterable(new ComputeDuplicates().computeDuplicatesForAllOpenedProjects(handle, new AtomicBoolean()))) {
+            for (Span s : dd.dupes) {
+                duplicatesReal.put(relativePath(s.file), TestUtilities.copyFileToString(FileUtil.toFile(s.file)).substring(s.startOff, s.endOff));
+            }
+        }
+
+        assertEquals(duplicatesGolden, duplicatesReal);
+    }
+
+    private String relativePath(FileObject file) {
+        return FileUtil.isParentOf(src, file) ? FileUtil.getRelativePath(src, file) : FileUtil.getRelativePath(src2, file);
+    }
+    
+    @ServiceProvider(service=MimeDataProvider.class)
+    public static final class MimeDataProviderImpl implements MimeDataProvider {
+
+        private final Lookup L = Lookups.fixed(new DuplicatesCustomIndexerImpl.FactoryImpl());
+        
+        @Override
+        public Lookup getLookup(MimePath mimePath) {
+            if ("text/x-java".equals(mimePath.getPath())) return L;
+            return Lookup.EMPTY;
+        }
+        
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-jackpot30/blob/9ed0a377/duplicates/ide/impl/test/unit/src/org/netbeans/modules/jackpot30/impl/duplicates/indexing/RemoteDuplicatesIndexTest.java
----------------------------------------------------------------------
diff --git a/duplicates/ide/impl/test/unit/src/org/netbeans/modules/jackpot30/impl/duplicates/indexing/RemoteDuplicatesIndexTest.java b/duplicates/ide/impl/test/unit/src/org/netbeans/modules/jackpot30/impl/duplicates/indexing/RemoteDuplicatesIndexTest.java
new file mode 100644
index 0000000..f51503a
--- /dev/null
+++ b/duplicates/ide/impl/test/unit/src/org/netbeans/modules/jackpot30/impl/duplicates/indexing/RemoteDuplicatesIndexTest.java
@@ -0,0 +1,136 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2011 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ *
+ * Contributor(s):
+ *
+ * Portions Copyrighted 2011 Sun Microsystems, Inc.
+ */
+package org.netbeans.modules.jackpot30.impl.duplicates.indexing;
+
+import java.io.File;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicBoolean;
+import org.netbeans.api.java.source.TestUtilities;
+import static org.junit.Assert.*;
+import org.netbeans.api.java.source.SourceUtilsTestUtil;
+import org.netbeans.core.startup.Main;
+import org.netbeans.junit.NbTestCase;
+import org.netbeans.modules.jackpot30.impl.TestUtils;
+import org.netbeans.modules.jackpot30.impl.duplicates.ComputeDuplicates.DuplicateDescription;
+import org.netbeans.modules.jackpot30.impl.duplicates.ComputeDuplicates.Span;
+import org.netbeans.modules.jackpot30.remoting.api.RemoteIndex;
+import org.netbeans.modules.parsing.impl.indexing.CacheFolder;
+import org.openide.filesystems.FileObject;
+import org.openide.filesystems.FileUtil;
+
+/**
+ *
+ * @author lahvac
+ */
+public class RemoteDuplicatesIndexTest extends NbTestCase {
+
+    public RemoteDuplicatesIndexTest(String name) {
+        super(name);
+    }
+
+    public void setUp() throws Exception {
+        SourceUtilsTestUtil.prepareTest(new String[0], new Object[0]);
+        Main.initializeURLFactory();
+        TestUtils.clearRemoteContent();
+        super.setUp();
+    }
+
+    public void testFindHashOccurrences() throws Exception {
+        clearWorkDir();
+        File cacheDir = new File(getWorkDir(), "cache");
+        File dataDir = new File(getWorkDir(), "data");
+
+        CacheFolder.setCacheFolder(FileUtil.createFolder(cacheDir));
+
+        FileObject data1 = FileUtil.createFolder(new File(dataDir, "data1"));
+        FileObject data2 = FileUtil.createFolder(new File(dataDir, "data2"));
+        FileObject source = FileUtil.createData(new File(dataDir, "src/Test.java"));
+
+        TestUtilities.copyStringToFile(data1.createData("T1.java"), "0123456789");
+        TestUtilities.copyStringToFile(data1.createData("T2.java"), "0123456789");
+        TestUtilities.copyStringToFile(data2.createData("T3.java"), "0123456789");
+        TestUtilities.copyStringToFile(data2.createData("T4.java"), "0123456789");
+        
+        TestUtils.addRemoteContent(new URL("test://test/index/duplicates/findDuplicates?hashes=[%0A%20%20%20%20%2200%22,%0A%20%20%20%20%2201%22,%0A%20%20%20%20%2202%22%0A]"), "{ \"00\": { \"foo1\": [ \"T1.java\", \"T2.java\" ], \"foo2\": [ \"T3.java\", \"T4.java\" ] } }");
+        TestUtils.addRemoteContent(new URL("test://test/index/info?path=foo1"), "{ }");
+        TestUtils.addRemoteContent(new URL("test://test/index/info?path=foo2"), "{ }");
+        RemoteIndex.saveIndices(Arrays.asList(RemoteIndex.create(FileUtil.toFile(data1).toURI().toURL(), new URL("test://test/index"), "foo1"),
+                                              RemoteIndex.create(FileUtil.toFile(data2).toURI().toURL(), new URL("test://test/index"), "foo2")));
+
+        TestUtilities.copyStringToFile(source, "01234567890123456789");
+        
+        Map<String, long[]> hashes = new HashMap<String, long[]>();
+
+        hashes.put("00", new long[] {1, 2, 5, 6});
+        hashes.put("01", new long[] {8, 9});
+        hashes.put("02", new long[] {3, 4});
+        
+        List<DuplicateDescription> duplicates = RemoteDuplicatesIndex.findDuplicates(hashes, source, new AtomicBoolean());
+        List<String> duplicatesReal = new ArrayList<String>();
+
+        for (DuplicateDescription dd : duplicates) {
+            for (Span s : dd.dupes) {
+                duplicatesReal.add(s.file.getName());
+            }
+        }
+
+        assertEquals(Arrays.asList("T1", "T2", "T3", "T4"), duplicatesReal);
+
+        //check local cache:
+        duplicates = RemoteDuplicatesIndex.findDuplicates(hashes, source, new AtomicBoolean());
+        duplicatesReal = new ArrayList<String>();
+
+        for (DuplicateDescription dd : duplicates) {
+            for (Span s : dd.dupes) {
+                duplicatesReal.add(s.file.getName());
+            }
+        }
+
+        assertEquals(Arrays.asList("T1", "T2", "T3", "T4"), duplicatesReal);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-jackpot30/blob/9ed0a377/duplicates/ide/nbproject/build-impl.xml
----------------------------------------------------------------------
diff --git a/duplicates/ide/nbproject/build-impl.xml b/duplicates/ide/nbproject/build-impl.xml
new file mode 100644
index 0000000..822d89e
--- /dev/null
+++ b/duplicates/ide/nbproject/build-impl.xml
@@ -0,0 +1,93 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+
+Copyright 2009-2017 Oracle and/or its affiliates. All rights reserved.
+
+Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+Other names may be trademarks of their respective owners.
+
+The contents of this file are subject to the terms of either the GNU
+General Public License Version 2 only ("GPL") or the Common
+Development and Distribution License("CDDL") (collectively, the
+"License"). You may not use this file except in compliance with the
+License. You can obtain a copy of the License at
+http://www.netbeans.org/cddl-gplv2.html
+or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+specific language governing permissions and limitations under the
+License.  When distributing the software, include this License Header
+Notice in each file and include the License file at
+nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+particular file as subject to the "Classpath" exception as provided
+by Oracle in the GPL Version 2 section of the License file that
+accompanied this code. If applicable, add the following below the
+License Header, with the fields enclosed by brackets [] replaced by
+your own identifying information:
+"Portions Copyrighted [year] [name of copyright owner]"
+
+Contributor(s):
+
+The Original Software is NetBeans. The Initial Developer of the Original
+Software is Sun Microsystems, Inc. Portions Copyright 2009-2010 Sun
+Microsystems, Inc. All Rights Reserved.
+
+If you wish your version of this file to be governed by only the CDDL
+or only the GPL Version 2, indicate your decision by adding
+"[Contributor] elects to include this software in this distribution
+under the [CDDL or GPL Version 2] license." If you do not indicate a
+single choice of license, a recipient has the option to distribute
+your version of this file under either the CDDL, the GPL Version 2 or
+to extend the choice of license to its licensees as provided above.
+However, if you add GPL Version 2 code and therefore, elected the GPL
+Version 2 license, then the option applies only if the new code is
+made subject to such option by the copyright holder.
+-->
+<!--
+*** GENERATED FROM project.xml - DO NOT EDIT  ***
+***         EDIT ../build.xml INSTEAD         ***
+-->
+<project name="duplicates-ide-impl" basedir=".." xmlns:sproject="http://www.netbeans.org/ns/nb-module-suite-project/1">
+    <fail message="Please build using Ant 1.7.1 or higher.">
+        <condition>
+            <not>
+                <antversion atleast="1.7.1"/>
+            </not>
+        </condition>
+    </fail>
+    <property file="nbproject/private/platform-private.properties"/>
+    <property file="nbproject/platform.properties"/>
+    <macrodef name="property" uri="http://www.netbeans.org/ns/nb-module-suite-project/1">
+        <attribute name="name"/>
+        <attribute name="value"/>
+        <sequential>
+            <property name="@{name}" value="${@{value}}"/>
+        </sequential>
+    </macrodef>
+    <macrodef name="evalprops" uri="http://www.netbeans.org/ns/nb-module-suite-project/1">
+        <attribute name="property"/>
+        <attribute name="value"/>
+        <sequential>
+            <property name="@{property}" value="@{value}"/>
+        </sequential>
+    </macrodef>
+    <property file="${user.properties.file}"/>
+    <sproject:property name="harness.dir" value="nbplatform.${nbplatform.active}.harness.dir"/>
+    <sproject:property name="nbplatform.active.dir" value="nbplatform.${nbplatform.active}.netbeans.dest.dir"/>
+    <sproject:evalprops property="cluster.path.evaluated" value="${cluster.path}"/>
+    <fail message="Path to 'platform' cluster missing in $${cluster.path} property or using corrupt Netbeans Platform (missing harness).">
+        <condition>
+            <not>
+                <contains string="${cluster.path.evaluated}" substring="platform"/>
+            </not>
+        </condition>
+    </fail>
+    <ant antfile="nbproject/platform.xml"/>
+    <fail message="Cannot find NetBeans build harness. ${line.separator}Check that nbplatform.${nbplatform.active}.netbeans.dest.dir and nbplatform.${nbplatform.active}.harness.dir are defined. ${line.separator}On a developer machine these are normally defined in ${user.properties.file}=${netbeans.user}/build.properties ${line.separator}but for automated builds you should pass these properties to Ant explicitly. ${line.separator}You may instead download the harness and platform: -Dbootstrap.url=.../tasks.jar -Dautoupdate.catalog.url=.../updates.xml">
+        <condition>
+            <not>
+                <available file="${harness.dir}/suite.xml"/>
+            </not>
+        </condition>
+    </fail>
+    <import file="${harness.dir}/suite.xml"/>
+</project>

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-jackpot30/blob/9ed0a377/duplicates/ide/nbproject/genfiles.properties
----------------------------------------------------------------------
diff --git a/duplicates/ide/nbproject/genfiles.properties b/duplicates/ide/nbproject/genfiles.properties
new file mode 100644
index 0000000..5207440
--- /dev/null
+++ b/duplicates/ide/nbproject/genfiles.properties
@@ -0,0 +1,52 @@
+# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+#
+# Copyright 2009-2017 Oracle and/or its affiliates. All rights reserved.
+#
+# Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+# Other names may be trademarks of their respective owners.
+#
+# The contents of this file are subject to the terms of either the GNU
+# General Public License Version 2 only ("GPL") or the Common
+# Development and Distribution License("CDDL") (collectively, the
+# "License"). You may not use this file except in compliance with the
+# License. You can obtain a copy of the License at
+# http://www.netbeans.org/cddl-gplv2.html
+# or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+# specific language governing permissions and limitations under the
+# License.  When distributing the software, include this License Header
+# Notice in each file and include the License file at
+# nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+# particular file as subject to the "Classpath" exception as provided
+# by Oracle in the GPL Version 2 section of the License file that
+# accompanied this code. If applicable, add the following below the
+# License Header, with the fields enclosed by brackets [] replaced by
+# your own identifying information:
+# "Portions Copyrighted [year] [name of copyright owner]"
+#
+# Contributor(s):
+#
+# The Original Software is NetBeans. The Initial Developer of the Original
+# Software is Sun Microsystems, Inc. Portions Copyright 2009-2010 Sun
+# Microsystems, Inc. All Rights Reserved.
+#
+# If you wish your version of this file to be governed by only the CDDL
+# or only the GPL Version 2, indicate your decision by adding
+# "[Contributor] elects to include this software in this distribution
+# under the [CDDL or GPL Version 2] license." If you do not indicate a
+# single choice of license, a recipient has the option to distribute
+# your version of this file under either the CDDL, the GPL Version 2 or
+# to extend the choice of license to its licensees as provided above.
+# However, if you add GPL Version 2 code and therefore, elected the GPL
+# Version 2 license, then the option applies only if the new code is
+# made subject to such option by the copyright holder.
+build.xml.data.CRC32=891734f5
+build.xml.script.CRC32=82b74f72
+build.xml.stylesheet.CRC32=eaf9f76a@2.49
+# 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=44b3f6ba
+nbproject/build-impl.xml.script.CRC32=d800a24f
+nbproject/build-impl.xml.stylesheet.CRC32=0f381476@2.49
+nbproject/platform.xml.data.CRC32=44b3f6ba
+nbproject/platform.xml.script.CRC32=6dcbd131
+nbproject/platform.xml.stylesheet.CRC32=4e1f53d4@2.70

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-jackpot30/blob/9ed0a377/duplicates/ide/nbproject/platform.properties
----------------------------------------------------------------------
diff --git a/duplicates/ide/nbproject/platform.properties b/duplicates/ide/nbproject/platform.properties
new file mode 100644
index 0000000..f8d652e
--- /dev/null
+++ b/duplicates/ide/nbproject/platform.properties
@@ -0,0 +1,61 @@
+# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+#
+# Copyright 2009-2017 Oracle and/or its affiliates. All rights reserved.
+#
+# Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+# Other names may be trademarks of their respective owners.
+#
+# The contents of this file are subject to the terms of either the GNU
+# General Public License Version 2 only ("GPL") or the Common
+# Development and Distribution License("CDDL") (collectively, the
+# "License"). You may not use this file except in compliance with the
+# License. You can obtain a copy of the License at
+# http://www.netbeans.org/cddl-gplv2.html
+# or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+# specific language governing permissions and limitations under the
+# License.  When distributing the software, include this License Header
+# Notice in each file and include the License file at
+# nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+# particular file as subject to the "Classpath" exception as provided
+# by Oracle in the GPL Version 2 section of the License file that
+# accompanied this code. If applicable, add the following below the
+# License Header, with the fields enclosed by brackets [] replaced by
+# your own identifying information:
+# "Portions Copyrighted [year] [name of copyright owner]"
+#
+# Contributor(s):
+#
+# The Original Software is NetBeans. The Initial Developer of the Original
+# Software is Sun Microsystems, Inc. Portions Copyright 2009-2010 Sun
+# Microsystems, Inc. All Rights Reserved.
+#
+# If you wish your version of this file to be governed by only the CDDL
+# or only the GPL Version 2, indicate your decision by adding
+# "[Contributor] elects to include this software in this distribution
+# under the [CDDL or GPL Version 2] license." If you do not indicate a
+# single choice of license, a recipient has the option to distribute
+# your version of this file under either the CDDL, the GPL Version 2 or
+# to extend the choice of license to its licensees as provided above.
+# However, if you add GPL Version 2 code and therefore, elected the GPL
+# Version 2 license, then the option applies only if the new code is
+# made subject to such option by the copyright holder.
+cluster.path=\
+    ${nbplatform.active.dir}/apisupport:\
+    ${nbplatform.active.dir}/cnd:\
+    ${nbplatform.active.dir}/dlight:\
+    ${nbplatform.active.dir}/enterprise:\
+    ${nbplatform.active.dir}/harness:\
+    ${nbplatform.active.dir}/ide:\
+    ${nbplatform.active.dir}/extide:\
+    ${nbplatform.active.dir}/java:\
+    ${nbplatform.active.dir}/nb:\
+    ${nbplatform.active.dir}/platform:\
+    ${nbplatform.active.dir}/profiler:\
+    ${nbplatform.active.dir}/websvccommon:\
+    ../../remoting/common/build/cluster:\
+    ../../remoting/ide/build/cluster
+extcluster.../../remoting/common/build/cluster.javadoc=
+extcluster.../../remoting/common/build/cluster.sources=../../remoting/common
+extcluster.../../remoting/ide/build/cluster.javadoc=
+extcluster.../../remoting/ide/build/cluster.sources=../../remoting/ide
+nbplatform.active=default

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-jackpot30/blob/9ed0a377/duplicates/ide/nbproject/platform.xml
----------------------------------------------------------------------
diff --git a/duplicates/ide/nbproject/platform.xml b/duplicates/ide/nbproject/platform.xml
new file mode 100644
index 0000000..1917e28
--- /dev/null
+++ b/duplicates/ide/nbproject/platform.xml
@@ -0,0 +1,77 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+
+Copyright 2009-2017 Oracle and/or its affiliates. All rights reserved.
+
+Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+Other names may be trademarks of their respective owners.
+
+The contents of this file are subject to the terms of either the GNU
+General Public License Version 2 only ("GPL") or the Common
+Development and Distribution License("CDDL") (collectively, the
+"License"). You may not use this file except in compliance with the
+License. You can obtain a copy of the License at
+http://www.netbeans.org/cddl-gplv2.html
+or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+specific language governing permissions and limitations under the
+License.  When distributing the software, include this License Header
+Notice in each file and include the License file at
+nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+particular file as subject to the "Classpath" exception as provided
+by Oracle in the GPL Version 2 section of the License file that
+accompanied this code. If applicable, add the following below the
+License Header, with the fields enclosed by brackets [] replaced by
+your own identifying information:
+"Portions Copyrighted [year] [name of copyright owner]"
+
+Contributor(s):
+
+The Original Software is NetBeans. The Initial Developer of the Original
+Software is Sun Microsystems, Inc. Portions Copyright 2009-2010 Sun
+Microsystems, Inc. All Rights Reserved.
+
+If you wish your version of this file to be governed by only the CDDL
+or only the GPL Version 2, indicate your decision by adding
+"[Contributor] elects to include this software in this distribution
+under the [CDDL or GPL Version 2] license." If you do not indicate a
+single choice of license, a recipient has the option to distribute
+your version of this file under either the CDDL, the GPL Version 2 or
+to extend the choice of license to its licensees as provided above.
+However, if you add GPL Version 2 code and therefore, elected the GPL
+Version 2 license, then the option applies only if the new code is
+made subject to such option by the copyright holder.
+-->
+<project name="platform" default="download" basedir="..">
+    <condition property="download.required">
+        <and>
+            <not>
+                <available file="${harness.dir}/suite.xml"/>
+            </not>
+            <isset property="bootstrap.url"/>
+            <isset property="autoupdate.catalog.url"/>
+        </and>
+    </condition>
+    <target name="download" if="download.required">
+        <mkdir dir="${harness.dir}"/>
+        <pathconvert pathsep="|" property="download.clusters">
+            <mapper type="flatten"/>
+            <path path="${cluster.path}"/>
+        </pathconvert>
+        <property name="disabled.modules" value=""/>
+        <pathconvert property="module.includes" pathsep="">
+            <mapper type="glob" from="${basedir}${file.separator}*" to="(?!^\Q*\E$)"/>
+            <path>
+                <filelist files="${disabled.modules}" dir="."/>
+            </path>
+        </pathconvert>
+        <echo message="Downloading clusters ${download.clusters}"/>
+        <property name="tasks.jar" location="${java.io.tmpdir}/tasks.jar"/>
+        <get src="${bootstrap.url}" dest="${tasks.jar}" usetimestamp="true" verbose="true"/>
+        <taskdef name="autoupdate" classname="org.netbeans.nbbuild.AutoUpdate" classpath="${tasks.jar}"/>
+        <autoupdate installdir="${nbplatform.active.dir}" updatecenter="${autoupdate.catalog.url}">
+            <modules includes="${module.includes}.*" clusters="${download.clusters}"/>
+            <modules includes="org[.]netbeans[.]modules[.]apisupport[.]harness" clusters="harness"/>
+        </autoupdate>
+    </target>
+</project>

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-jackpot30/blob/9ed0a377/duplicates/ide/nbproject/project.properties
----------------------------------------------------------------------
diff --git a/duplicates/ide/nbproject/project.properties b/duplicates/ide/nbproject/project.properties
new file mode 100644
index 0000000..8877ce6
--- /dev/null
+++ b/duplicates/ide/nbproject/project.properties
@@ -0,0 +1,44 @@
+# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+#
+# Copyright 2009-2017 Oracle and/or its affiliates. All rights reserved.
+#
+# Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+# Other names may be trademarks of their respective owners.
+#
+# The contents of this file are subject to the terms of either the GNU
+# General Public License Version 2 only ("GPL") or the Common
+# Development and Distribution License("CDDL") (collectively, the
+# "License"). You may not use this file except in compliance with the
+# License. You can obtain a copy of the License at
+# http://www.netbeans.org/cddl-gplv2.html
+# or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+# specific language governing permissions and limitations under the
+# License.  When distributing the software, include this License Header
+# Notice in each file and include the License file at
+# nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+# particular file as subject to the "Classpath" exception as provided
+# by Oracle in the GPL Version 2 section of the License file that
+# accompanied this code. If applicable, add the following below the
+# License Header, with the fields enclosed by brackets [] replaced by
+# your own identifying information:
+# "Portions Copyrighted [year] [name of copyright owner]"
+#
+# Contributor(s):
+#
+# The Original Software is NetBeans. The Initial Developer of the Original
+# Software is Sun Microsystems, Inc. Portions Copyright 2009-2010 Sun
+# Microsystems, Inc. All Rights Reserved.
+#
+# If you wish your version of this file to be governed by only the CDDL
+# or only the GPL Version 2, indicate your decision by adding
+# "[Contributor] elects to include this software in this distribution
+# under the [CDDL or GPL Version 2] license." If you do not indicate a
+# single choice of license, a recipient has the option to distribute
+# your version of this file under either the CDDL, the GPL Version 2 or
+# to extend the choice of license to its licensees as provided above.
+# However, if you add GPL Version 2 code and therefore, elected the GPL
+# Version 2 license, then the option applies only if the new code is
+# made subject to such option by the copyright holder.
+modules=\
+    ${project.org.netbeans.modules.jackpot30.duplicates.impl}
+project.org.netbeans.modules.jackpot30.duplicates.impl=impl

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-jackpot30/blob/9ed0a377/duplicates/ide/nbproject/project.xml
----------------------------------------------------------------------
diff --git a/duplicates/ide/nbproject/project.xml b/duplicates/ide/nbproject/project.xml
new file mode 100644
index 0000000..9052409
--- /dev/null
+++ b/duplicates/ide/nbproject/project.xml
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+
+Copyright 2009-2017 Oracle and/or its affiliates. All rights reserved.
+
+Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+Other names may be trademarks of their respective owners.
+
+The contents of this file are subject to the terms of either the GNU
+General Public License Version 2 only ("GPL") or the Common
+Development and Distribution License("CDDL") (collectively, the
+"License"). You may not use this file except in compliance with the
+License. You can obtain a copy of the License at
+http://www.netbeans.org/cddl-gplv2.html
+or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+specific language governing permissions and limitations under the
+License.  When distributing the software, include this License Header
+Notice in each file and include the License file at
+nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+particular file as subject to the "Classpath" exception as provided
+by Oracle in the GPL Version 2 section of the License file that
+accompanied this code. If applicable, add the following below the
+License Header, with the fields enclosed by brackets [] replaced by
+your own identifying information:
+"Portions Copyrighted [year] [name of copyright owner]"
+
+Contributor(s):
+
+The Original Software is NetBeans. The Initial Developer of the Original
+Software is Sun Microsystems, Inc. Portions Copyright 2009-2010 Sun
+Microsystems, Inc. All Rights Reserved.
+
+If you wish your version of this file to be governed by only the CDDL
+or only the GPL Version 2, indicate your decision by adding
+"[Contributor] elects to include this software in this distribution
+under the [CDDL or GPL Version 2] license." If you do not indicate a
+single choice of license, a recipient has the option to distribute
+your version of this file under either the CDDL, the GPL Version 2 or
+to extend the choice of license to its licensees as provided above.
+However, if you add GPL Version 2 code and therefore, elected the GPL
+Version 2 license, then the option applies only if the new code is
+made subject to such option by the copyright holder.
+-->
+<project xmlns="http://www.netbeans.org/ns/project/1">
+    <type>org.netbeans.modules.apisupport.project.suite</type>
+    <configuration>
+        <data xmlns="http://www.netbeans.org/ns/nb-module-suite-project/1">
+            <name>duplicates-ide</name>
+        </data>
+    </configuration>
+</project>

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-jackpot30/blob/9ed0a377/duplicates/server/indexer/build.xml
----------------------------------------------------------------------
diff --git a/duplicates/server/indexer/build.xml b/duplicates/server/indexer/build.xml
new file mode 100644
index 0000000..bb9edf2
--- /dev/null
+++ b/duplicates/server/indexer/build.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+
+Copyright 2009-2017 Oracle and/or its affiliates. All rights reserved.
+
+Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+Other names may be trademarks of their respective owners.
+
+The contents of this file are subject to the terms of either the GNU
+General Public License Version 2 only ("GPL") or the Common
+Development and Distribution License("CDDL") (collectively, the
+"License"). You may not use this file except in compliance with the
+License. You can obtain a copy of the License at
+http://www.netbeans.org/cddl-gplv2.html
+or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+specific language governing permissions and limitations under the
+License.  When distributing the software, include this License Header
+Notice in each file and include the License file at
+nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+particular file as subject to the "Classpath" exception as provided
+by Oracle in the GPL Version 2 section of the License file that
+accompanied this code. If applicable, add the following below the
+License Header, with the fields enclosed by brackets [] replaced by
+your own identifying information:
+"Portions Copyrighted [year] [name of copyright owner]"
+
+Contributor(s):
+
+The Original Software is NetBeans. The Initial Developer of the Original
+Software is Sun Microsystems, Inc. Portions Copyright 2009-2010 Sun
+Microsystems, Inc. All Rights Reserved.
+
+If you wish your version of this file to be governed by only the CDDL
+or only the GPL Version 2, indicate your decision by adding
+"[Contributor] elects to include this software in this distribution
+under the [CDDL or GPL Version 2] license." If you do not indicate a
+single choice of license, a recipient has the option to distribute
+your version of this file under either the CDDL, the GPL Version 2 or
+to extend the choice of license to its licensees as provided above.
+However, if you add GPL Version 2 code and therefore, elected the GPL
+Version 2 license, then the option applies only if the new code is
+made subject to such option by the copyright holder.
+-->
+<!-- You may freely edit this file. See harness/README in the NetBeans platform -->
+<!-- for some information on what you could do (e.g. targets to override). -->
+<!-- If you delete this file and reopen the project it will be recreated. -->
+<project name="duplicates-indexer" basedir=".">
+    <description>Builds the module suite indexer.</description>
+    <import file="nbproject/build-impl.xml"/>
+</project>