| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| |
| package org.netbeans.modules.turbo; |
| |
| import java.io.File; |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.HashSet; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| import java.util.concurrent.ConcurrentHashMap; |
| import java.util.logging.Level; |
| import java.util.logging.Logger; |
| |
| /** |
| * |
| * @author Tomas Stupka |
| */ |
| public abstract class CacheIndex { |
| |
| private static final Logger LOG = Logger.getLogger("org.netbeans.modules.turbo.CacheIndex"); |
| private Map<File, Set<File>> index = new ConcurrentHashMap<File, Set<File>>(); |
| |
| public CacheIndex() { } |
| |
| /** |
| * Returns true if the given file is managed by the particular vcs |
| * @param file |
| * @return true if the given file is managed by the particular vcs otherwise false |
| */ |
| protected abstract boolean isManaged(File file); |
| |
| public File[] get(File key) { |
| LOG.log(Level.FINE, "get({0})", new Object[]{key}); |
| |
| if(key == null) { |
| return new File[0]; |
| } |
| |
| synchronized(this) { |
| Set<File> ret = index.get(key); |
| if(ret == null) { |
| LOG.log(Level.FINE, " get({0}) returns no files", new Object[]{key}); |
| return new File[0]; |
| } |
| |
| LOG.log(Level.FINE, " get({0}) returns {1}", new Object[]{key, ret.size()}); |
| if(LOG.isLoggable(Level.FINER)) { |
| LOG.finer(" " + ret); |
| } |
| |
| return ret.toArray(new File[ret.size()]); |
| } |
| } |
| |
| public File[] getAllValues() { |
| LOG.fine("getAllValues()"); |
| |
| List<Set<File>> values; |
| synchronized(this) { |
| Collection<Set<File>> c = index.values(); |
| values = new ArrayList<Set<File>>(c.size()); |
| values.addAll(c); |
| } |
| |
| Set<File> ret = new HashSet<>(); |
| for (Set<File> valuesSet : values) { |
| synchronized(this) { |
| ret.addAll(valuesSet); |
| } |
| } |
| |
| LOG.log(Level.FINE, " getAllValues() returns {0}", new Object[]{ret.size()}); |
| if(LOG.isLoggable(Level.FINER)) { |
| LOG.finer(" " + ret); |
| } |
| |
| return ret.toArray(new File[ret.size()]); |
| } |
| |
| public void add(File file) { |
| LOG.log(Level.FINE, "add({0})", new Object[]{file}); |
| |
| assert file != null; |
| if(file == null) { |
| return; |
| } |
| |
| File parent = file.getParentFile(); |
| if (parent == null) { |
| // this should not happen |
| LOG.log(Level.INFO, "add: trying to add a FS root {0})", new Object[]{file}); //NOI18N |
| if (LOG.isLoggable(Level.FINE)) { |
| LOG.log(Level.FINE, "add: trying to add a FS root", new Exception()); //log also the stacktrace |
| } |
| return; |
| } |
| synchronized(this) { |
| Set<File> set = index.get(parent); |
| if(set == null) { |
| LOG.log(Level.FINER, " add({0}) - creating new file entry", new Object[]{file}); |
| set = Collections.synchronizedSet(new HashSet<File>()); |
| set.add(file); |
| index.put(parent, set); |
| } else { |
| set.add(file); |
| } |
| |
| ensureParents(parent); |
| } |
| } |
| |
| public void add(File file, Set<File> files) { |
| LOG.log(Level.FINE, "add({0}, Set<File>)", new Object[]{file}); |
| if(LOG.isLoggable(Level.FINER)) { |
| LOG.finer(" " + files); |
| } |
| if(files == null) { |
| files = new HashSet<File>(0); |
| } |
| Set<File> newSet = new HashSet<File>(files.size()); |
| |
| synchronized(this) { |
| Set<File> oldSet = index.get(file); |
| if(oldSet != null) { |
| for (File f : oldSet) { |
| if(!files.contains(f) && // removed from the set but |
| index.get(f) != null) // remove only if there are no files underneath |
| { |
| newSet.add(f); |
| } |
| } |
| } |
| newSet.addAll(files); |
| |
| LOG.log(Level.FINE, " add({0}, Set<File>) - add entries", new Object[]{file}); |
| index.put(file, Collections.synchronizedSet(newSet)); |
| if(newSet.size() > 0) { |
| ensureParents(file); |
| } else { |
| cleanUpParents(file); |
| } |
| |
| } |
| } |
| |
| private void ensureParents(File file) { |
| File pFile = file; |
| LOG.log(Level.FINE, " ensureParents({0})", new Object[]{pFile}); |
| |
| while (true) { |
| File parent = file.getParentFile(); |
| LOG.log(Level.FINE, " ensureParents({0}) - parent {1}", new Object[]{pFile, parent}); |
| if (parent == null) { |
| LOG.log(Level.FINE, " ensureParents({0}) - done", new Object[]{pFile, parent}); |
| break; |
| } |
| |
| synchronized(this) { |
| Set<File> set = index.get(parent); |
| if (set == null) { |
| LOG.log(Level.FINE, " ensureParents({0}) - parent {1} - no entry" , new Object[]{pFile, parent}); |
| if (!isManaged(parent)) { |
| LOG.log(Level.FINE, " ensureParents({0}) - parent {1} - not managed - done!", new Object[]{pFile, parent}); |
| break; |
| } |
| set = new HashSet<File>(); |
| LOG.log(Level.FINE, " ensureParents({0}) - parent {1} - creating parent node", new Object[]{pFile, parent}); |
| index.put(parent, set); |
| } |
| LOG.log(Level.FINE, " ensureParents({0}) - parent {1} - adding file {2}", new Object[]{pFile, parent, file}); |
| set.add(file); |
| } |
| file = parent; |
| } |
| } |
| |
| private void cleanUpParents(File file) { |
| File pFile = file; |
| LOG.log(Level.FINE, " cleanUpParents({0})", new Object[]{pFile}); |
| |
| Set<File> set = index.get(file); |
| if(set != null && set.size() > 0) { |
| LOG.log(Level.FINE, " cleanUpParents({0}) - children underneath. stop.", new Object[]{pFile}); |
| return; |
| } |
| |
| LOG.log(Level.FINE, " cleanUpParents({0}) - removing node", new Object[]{pFile}); |
| index.remove(file); |
| while(true) { |
| File parent = file.getParentFile(); |
| LOG.log(Level.FINE, " cleanUpParents({0}) - parent {1}", new Object[]{pFile, parent}); |
| |
| if (parent == null) { |
| LOG.log(Level.FINE, " cleanUpParents({0}) - done", new Object[]{pFile, parent}); |
| break; |
| } |
| |
| synchronized(this) { |
| set = index.get(parent); |
| if(set == null) { |
| LOG.log(Level.FINE, " cleanUpParents({0}) - parent {1} empty - stop", new Object[]{pFile, parent}); |
| break; |
| } |
| |
| if(set.size() == 1) { |
| File lastLonelyFile = set.iterator().next(); |
| if(file.equals(lastLonelyFile) && index.get(lastLonelyFile) == null) { // remove the last node only when it indeed equals the removing file |
| // file under parent point to nowhere -> remove whole parent node |
| LOG.log(Level.FINE, " cleanUpParents({0}) - parent {1} size 1 - remove", new Object[]{pFile, parent}); |
| index.remove(parent); |
| } else { |
| break; |
| } |
| } else { |
| LOG.log(Level.FINE, " cleanUpParents({0}) - parent {1} - remove file {2}", new Object[]{pFile, parent, file}); |
| // more then one file under parent node -> remove only file from parents children if it's pointing to nowhere |
| if(index.get(file) == null) { |
| set.remove(file); |
| } |
| break; |
| } |
| } |
| file = parent; |
| } |
| |
| } |
| } |