diff --git a/parsing.api/src/org/netbeans/modules/parsing/impl/indexing/FileEventLog.java b/parsing.api/src/org/netbeans/modules/parsing/impl/indexing/FileEventLog.java new file mode 100644 --- /dev/null +++ b/parsing.api/src/org/netbeans/modules/parsing/impl/indexing/FileEventLog.java @@ -0,0 +1,140 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2009 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 Sun Microsystems, Inc. + */ + +package org.netbeans.modules.parsing.impl.indexing; + +import java.net.URL; +import java.util.HashMap; +import java.util.IdentityHashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.logging.Logger; +import org.netbeans.modules.parsing.impl.indexing.RepositoryUpdater.Work; +import org.openide.filesystems.FileEvent; +import org.openide.filesystems.FileStateInvalidException; + +/** + * + * @author Tomas Zezula + */ +class FileEventLog implements Runnable { + + private static final Logger LOG = Logger.getLogger(FileEventLog.class.getName()); + + public static enum FileOp { + DELETE, + CREATE + }; + + private final Map>> changes; + + public FileEventLog() { + this.changes = new HashMap>>(); + } + + + public void record (final FileOp operation, final URL root, final FileEvent event, final Work work) { + assert operation != null; + assert root != null; + assert event != null; + assert root != null; + try { + record(operation, root, event.getFile().getURL(), event, work); + } catch (FileStateInvalidException e) { + e.printStackTrace(); + } + } + + public void record (final FileOp operation, final URL root, final URL fileURL, FileEvent event, final Work work) { + assert operation != null; + assert root != null; + assert fileURL != null; + assert root != null; + Map> rootSlot = changes.get(root); + if (rootSlot == null) { + rootSlot = new HashMap>(); + changes.put(root, rootSlot); + } + rootSlot.put(fileURL, Pair.of(operation,work)); + event.runWhenDeliveryOver(this); + } + + public void run () { + try { + commit(); + } finally { + cleanUp(); + } + } + + private void commit () { + final List first = new LinkedList(); + final List rest = new LinkedList(); + final IdentityHashMap seenDelete = new IdentityHashMap(); + for (Map> changesInRoot : changes.values()) { + for (Pair desc : changesInRoot.values()) { + if (desc.first == FileOp.DELETE) { + if (!seenDelete.containsKey(desc.second)) { + first.add(desc.second); + seenDelete.put(desc.second, desc.second); + } + } + else { + rest.add(desc.second); + } + } + } + final RepositoryUpdater ru = RepositoryUpdater.getDefault(); + for (Work wrk : first) { + LOG.finer("SCHEDULING: " + wrk); //NOI18N + ru.scheduleWork(wrk, false); + } + for (Work wrk : rest) { + LOG.finer("SCHEDULING: " + wrk); //NOI18N + ru.scheduleWork(wrk, false); + } + } + + private void cleanUp() { + this.changes.clear(); + } + + +} diff --git a/parsing.api/src/org/netbeans/modules/parsing/impl/indexing/Pair.java b/parsing.api/src/org/netbeans/modules/parsing/impl/indexing/Pair.java new file mode 100644 --- /dev/null +++ b/parsing.api/src/org/netbeans/modules/parsing/impl/indexing/Pair.java @@ -0,0 +1,84 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2009 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 Sun Microsystems, Inc. + */ + +package org.netbeans.modules.parsing.impl.indexing; + +/** + * + * @author Tomas Zezula + */ +final class Pair { + + public final P first; + public final K second; + + private Pair (P first, K second) { + this.first = first; + this.second = second; + } + + + public static Pair of (P first, K second) { + return new Pair (first,second); + } + + + @Override + public int hashCode () { + int hashCode = 0; + hashCode ^= first == null ? 0 : first.hashCode(); + hashCode ^= second == null ? 0: second.hashCode(); + return hashCode; + } + + @Override + public boolean equals (final Object other) { + if (other instanceof Pair) { + Pair otherPair = (Pair) other; + return (this.first == null ? otherPair.first == null : this.first.equals(otherPair.first)) && + (this.second == null ? otherPair.second == null : this.second.equals(otherPair.second)); + } + return false; + } + + @Override + public String toString () { + return String.format("Pair[%s,%s]", first,second); + } +} diff --git a/parsing.api/src/org/netbeans/modules/parsing/impl/indexing/RepositoryUpdater.java b/parsing.api/src/org/netbeans/modules/parsing/impl/indexing/RepositoryUpdater.java --- a/parsing.api/src/org/netbeans/modules/parsing/impl/indexing/RepositoryUpdater.java +++ b/parsing.api/src/org/netbeans/modules/parsing/impl/indexing/RepositoryUpdater.java @@ -119,6 +119,7 @@ import org.openide.filesystems.FileStateInvalidException; import org.openide.filesystems.FileUtil; import org.openide.filesystems.URLMapper; +import org.openide.util.Exceptions; import org.openide.util.Lookup; import org.openide.util.NbBundle; import org.openide.util.Parameters; @@ -350,6 +351,8 @@ // ----------------------------------------------------------------------- // FileChangeListener implementation // ----------------------------------------------------------------------- + + final FileEventLog eventQueue = new FileEventLog(); public void fileFolderCreated(FileEvent fe) { if (!authorize(fe)) { @@ -369,13 +372,15 @@ boolean sourcForBinaryRoot = sourcesForBinaryRoots.contains(root); ClassPath.Entry entry = sourcForBinaryRoot ? null : getClassPathEntry(URLMapper.findFileObject(root)); if (entry == null || entry.includes(fo)) { - scheduleWork(new FileListWork(scannedRoots2Dependencies, root, Collections.singleton(fo), false, false, true, sourcForBinaryRoot), false); + final Work wrk = new FileListWork(scannedRoots2Dependencies, root, Collections.singleton(fo), false, false, true, sourcForBinaryRoot); + eventQueue.record(FileEventLog.FileOp.CREATE, root, fe, wrk); processed = true; } } else { root = getOwningBinaryRoot(fo); if (root != null) { - scheduleWork(new BinaryWork(root), false); + final Work wrk = new BinaryWork(root); + eventQueue.record(FileEventLog.FileOp.CREATE, root, root, fe, wrk); processed = true; } } @@ -406,13 +411,15 @@ boolean sourceForBinaryRoot = sourcesForBinaryRoots.contains(root); ClassPath.Entry entry = sourceForBinaryRoot ? null : getClassPathEntry(URLMapper.findFileObject(root)); if (entry == null || entry.includes(fo)) { - scheduleWork(new FileListWork(scannedRoots2Dependencies, root, Collections.singleton(fo), false, false, true, sourceForBinaryRoot), false); + final Work wrk = new FileListWork(scannedRoots2Dependencies, root, Collections.singleton(fo), false, false, true, sourceForBinaryRoot); + eventQueue.record(FileEventLog.FileOp.CREATE, root, fe, wrk); processed = true; } } else { root = getOwningBinaryRoot(fo); if (root != null) { - scheduleWork(new BinaryWork(root), false); + final Work wrk = new BinaryWork(root); + eventQueue.record(FileEventLog.FileOp.CREATE, root, root, fe, wrk); processed = true; } } @@ -439,13 +446,15 @@ if (fo.isData() /*&& FileUtil.getMIMEType(fo, recognizers.getMimeTypes())!=null*/) { String relativePath = FileUtil.getRelativePath(URLMapper.findFileObject(root), fo); assert relativePath != null : "FileObject not under root: f=" + fo + ", root=" + root; //NOI18N - scheduleWork(new DeleteWork(root, Collections.singleton(relativePath)), false); + final Work wrk = new DeleteWork(root, Collections.singleton(relativePath)); + eventQueue.record(FileEventLog.FileOp.DELETE, root, fe, wrk); processed = true; } } else { root = getOwningBinaryRoot(fo); if (root != null) { - scheduleWork(new BinaryWork(root), false); + final Work wrk = new BinaryWork(root); + eventQueue.record(FileEventLog.FileOp.DELETE, root, root, fe, wrk); processed = true; } } @@ -472,26 +481,34 @@ if (root != null) { FileObject rootFo = URLMapper.findFileObject(root); String oldFilePath = FileUtil.getRelativePath(rootFo, newFile.getParent()) + "/" + oldNameExt; //NOI18N - if (newFile.isData()) { - scheduleWork(new DeleteWork(root, Collections.singleton(oldFilePath)), false); + final Work work = new DeleteWork(root, Collections.singleton(oldFilePath)); + try { + eventQueue.record(FileEventLog.FileOp.DELETE, root, Util.resolveUrl(root, oldFilePath), fe, work); + } catch (MalformedURLException e) { + Exceptions.printStackTrace(e); + } } else { Set oldFilePaths = new HashSet(); collectFilePaths(newFile, oldFilePath, oldFilePaths); - scheduleWork(new DeleteWork(root, oldFilePaths), false); + //ugly: need to register work for every file + for (String path : oldFilePaths) { + try { + final Work work = new DeleteWork(root, oldFilePaths); + eventQueue.record(FileEventLog.FileOp.DELETE, root, Util.resolveUrl(root, path), fe, work); + } catch (MalformedURLException e) { + Exceptions.printStackTrace(e); + } + } } + if (VisibilityQuery.getDefault().isVisible(newFile) && newFile.isData()) { final boolean sourceForBinaryRoot = sourcesForBinaryRoots.contains(root); ClassPath.Entry entry = sourceForBinaryRoot ? null : getClassPathEntry(rootFo); - if (entry == null || entry.includes(newFile)) { - // delaying of this task was just copied from the old java.source RepositoryUpdater + if (entry == null || entry.includes(newFile)) { final FileListWork flw = new FileListWork(scannedRoots2Dependencies,root, Collections.singleton(newFile), false, false, true, sourceForBinaryRoot); - RequestProcessor.getDefault().create(new Runnable() { - public void run() { - scheduleWork(flw, false); - } - }).schedule(FILE_LOCKS_DELAY); + eventQueue.record(FileEventLog.FileOp.CREATE, root, fe,flw); } } processed = true; @@ -502,13 +519,13 @@ if (parentFile != null) { try { URL oldBinaryRoot = new File (parentFile, oldNameExt).toURI().toURL(); - scheduleWork(new BinaryWork(oldBinaryRoot), false); + eventQueue.record(FileEventLog.FileOp.DELETE, oldBinaryRoot, oldBinaryRoot, fe, new BinaryWork(oldBinaryRoot)); } catch (MalformedURLException mue) { LOGGER.log(Level.WARNING, null, mue); } } - scheduleWork(new BinaryWork(root), false); + eventQueue.record(FileEventLog.FileOp.CREATE, root, root, fe,new BinaryWork(root)); processed = true; } }