blob: baf319c90d125a7040e20da3fc84362e3dc17dad [file] [log] [blame]
/*
* 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;
}
}
}