/* * Copyright 2014 Bernd Vogt and others. * * Licensed 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.sourcepit.b2.files; import static com.google.common.base.Preconditions.checkArgument; import static java.lang.String.valueOf; import static java.util.Collections.sort; import static org.sourcepit.common.utils.file.FileUtils.isParentOf; import static org.sourcepit.common.utils.path.PathUtils.getRelativePath; import java.io.File; import java.util.ArrayList; import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.concurrent.ConcurrentHashMap; import org.sourcepit.common.constraints.NotNull; import org.sourcepit.common.utils.file.FileUtils; import org.sourcepit.common.utils.file.FileUtils.AbstractFileFilter; import org.sourcepit.common.utils.props.LinkedPropertiesMap; import org.sourcepit.common.utils.props.PropertiesMap; public class ModuleDirectory { public static final int DEPTH_INFINITE = -1; public static final int MASK_NONE = 0xffffffff; public static final int FLAG_MODULE_DIR = 0x00000008; public static final int FLAG_FORBIDDEN = 0x00000004; public static final int FLAG_HIDDEN = 0x00000002; public static final int FLAG_DERIVED = 0x00000001; private final File moduleDir; private final Map<File, Integer> fileFlags; private final Map<File, Integer> aggregatedFlags = new ConcurrentHashMap<File, Integer>(); public static ModuleDirectory load(final File moduleDir, final File srcFile) { final PropertiesMap props = new LinkedPropertiesMap(); props.load(srcFile); final Map<File, Integer> fileFlags = new HashMap<File, Integer>(); for (Entry<String, String> entry : props.entrySet()) { final File file = new File(moduleDir, entry.getKey()); final Integer flags = Integer.valueOf(entry.getValue()); fileFlags.put(file, flags); } return new ModuleDirectory(moduleDir, fileFlags); } public static void save(ModuleDirectory moduleDirectory, File destFile) { final File moduleDir = moduleDirectory.getFile(); final Map<File, Integer> fileToFlagsMap = moduleDirectory.getFileFlags(); final List<File> files = new ArrayList<File>(fileToFlagsMap.keySet()); sort(files, new Comparator<File>() { @Override public int compare(File f1, File f2) { return f1.getPath().compareToIgnoreCase(f2.getPath()); } }); final PropertiesMap out = new LinkedPropertiesMap(); for (File file : files) { final int flags = fileToFlagsMap.get(file).intValue(); out.put(getRelativePath(file, moduleDir, "/"), valueOf(flags)); } out.store(destFile); } public ModuleDirectory(@NotNull File moduleDir, Map<File, Integer> fileFlags) { checkArgument(moduleDir != null); this.moduleDir = moduleDir; if (fileFlags == null) { this.fileFlags = new HashMap<File, Integer>(); } else { this.fileFlags = new HashMap<File, Integer>(fileFlags); } } Map<File, Integer> getFileFlags() { return fileFlags; } public File getFile() { return moduleDir; } public boolean isModuleFile(File file) { return isModuleFile(file, FLAG_DERIVED); } public boolean isModuleFile(File file, int flags) { return isModuleFile0(file, ~flags); } public boolean isModuleFile(File file, boolean includeHidden, boolean includeDerived) { return isModuleFile0(file, toFlagMask(includeHidden, includeDerived)); } private boolean isModuleFile0(File file, int flagMask) { if ((getFlags(file) & flagMask) != 0) { return false; } return isParentOf(moduleDir, file); } public boolean isHidden(File file) { return hasFlag(file, FLAG_HIDDEN); } public boolean isDerived(File file) { return hasFlag(file, FLAG_DERIVED); } public boolean hasFlag(File file, int flag) { return (getFlags(file) & flag) != 0; } public void addFlags(File file, int flags) { fileFlags.put(file, Integer.valueOf(getDirectFlags(file) | flags)); aggregatedFlags.remove(file); } public void removeFlags(File file, int flags) { fileFlags.put(file, Integer.valueOf(getDirectFlags(file) & ~flags)); aggregatedFlags.remove(file); } public int getFlags(File file) { if (file.equals(moduleDir)) { return 0; } else { Integer oFlags = aggregatedFlags.get(file); if (oFlags == null) { final int flags = getFlags(file.getParentFile()) | getDirectFlags(file); oFlags = Integer.valueOf(flags); aggregatedFlags.put(file, oFlags); } return oFlags.intValue(); } } private int getDirectFlags(File file) { final Integer oFlags = fileFlags.get(file); return oFlags == null ? 0 : oFlags.intValue(); } public <E extends Exception> void accept(FileVisitor<E> visitor) throws E { accept(visitor, DEPTH_INFINITE); } public <E extends Exception> void accept(FileVisitor<E> visitor, int depth) throws E { accept(visitor, depth, FLAG_DERIVED); } public <E extends Exception> void accept(FileVisitor<E> visitor, int depth, int flags) throws E { acceptRecursive(getFile(), visitor, 0, depth, ~flags); } public <E extends Exception> void accept(File file, FileVisitor<E> visitor) throws E { accept(file, visitor, DEPTH_INFINITE); } public <E extends Exception> void accept(File file, FileVisitor<E> visitor, int depth) throws E { accept(file, visitor, depth, FLAG_DERIVED); } public <E extends Exception> void accept(File file, FileVisitor<E> visitor, int depth, int flags) throws E { final int flagMask = ~flags; if (file.equals(getFile()) || isModuleFile(file, flagMask)) { acceptRecursive(file, visitor, 0, depth, flagMask); } } public <E extends Exception> void accept(FileVisitor<E> visitor, boolean visitHidden, boolean visitDerived) throws E { acceptRecursive(getFile(), visitor, 0, DEPTH_INFINITE, toFlagMask(visitHidden, visitDerived)); } private <E extends Exception> void acceptRecursive(File file, final FileVisitor<E> visitor, final int currentDepth, final int maxDepth, final int flagMask) throws E { if (currentDepth <= maxDepth || maxDepth == DEPTH_INFINITE) { FileUtils.listFiles(file, new AbstractFileFilter<E>() { @Override protected boolean acceptFile(File child) throws E { final int flags = getFlags(child); if ((flags & flagMask) == 0 && visitor.visit(child, flags)) { ModuleDirectory.this.acceptRecursive(child, visitor, currentDepth + 1, maxDepth, flagMask); } return false; } }); } } private static int toFlagMask(boolean includeHidden, boolean includeDerived) { int flagMask = MASK_NONE; if (includeHidden) { flagMask ^= FLAG_HIDDEN; } if (includeDerived) { flagMask ^= FLAG_DERIVED; } return flagMask; } public <E extends Exception> List<File> getFiles(final FileVisitor<E> filter, int flags) throws E { final List<File> files = new ArrayList<File>(); accept(new FileVisitor<E>() { @Override public boolean visit(File file, int flags) throws E { if (filter.visit(file, flags)) { files.add(file); } return false; } }, 0, flags); return files; } }