package org.wyona.yarep.impl.repo.treefs.map;
import org.apache.avalon.framework.configuration.Configuration;
import org.wyona.commons.io.FileUtil;
import org.wyona.yarep.core.Map;
import org.wyona.yarep.core.Path;
import org.wyona.yarep.core.RepositoryException;
import org.wyona.yarep.core.UID;
import org.wyona.yarep.impl.VFileSystemMapImpl;
import org.wyona.yarep.impl.repo.treefs.TreeFileSystemRepository;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.BufferedReader;
import java.io.FilenameFilter;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.LogManager;
/**
*
*/
public class TreeFileSystemMap extends VFileSystemMapImpl {
private static final Logger log = LogManager.getLogger(TreeFileSystemMap.class);
protected File pathsDir;
protected Pattern[] ignorePatterns;
protected ChildrenFilter childrenFilter = new ChildrenFilter();
protected int splitInterval;
protected int maxSplits;
public int getMaxSplits() {
return maxSplits;
}
public void setMaxSplits(int maxSplits) {
this.maxSplits = maxSplits;
}
public int getSplitInterval() {
return splitInterval;
}
public void setSplitInterval(int splitInterval) {
this.splitInterval = splitInterval;
}
/**
*
*/
public void readConfig(Configuration mapConfig, File repoConfigFile) throws RepositoryException {
try {
setPathsDir(new File(mapConfig.getAttribute("src")), repoConfigFile);
Configuration[] ignoreElements = mapConfig.getChildren("ignore");
ignorePatterns = new Pattern[ignoreElements.length];
for (int i=0; i<ignoreElements.length; i++) {
String patternString = ignoreElements[i].getAttribute("pattern");
ignorePatterns[i] = Pattern.compile(patternString);
log.debug("adding ignore pattern: " + ignorePatterns[i].pattern());
}
} catch(Exception e) {
log.error(e);
throw new RepositoryException("Could not read map configuration: "
+ repoConfigFile.getAbsolutePath() + e.getMessage(), e);
}
}
/**
* Set paths directory
*/
@Override
public void setPathsDir(File src, File repoConfigFile) throws RepositoryException {
pathsDir = src;
if (!pathsDir.isAbsolute()) {
pathsDir = FileUtil.file(repoConfigFile.getParent(), pathsDir.toString());
}
log.info("Paths dir: " + pathsDir.toString());
if (!pathsDir.exists()) {
log.error("No such file or directory: " + pathsDir);
throw new RepositoryException("No such file or directory: " + pathsDir);
}
}
/**
* Split path
*/
protected String splitPath(String path) {
path = stripLeadingSlash(path);
int splitInterval = getSplitInterval();
int maxSplits = getMaxSplits();
//log.debug("map path: " + path);
//log.debug("map splitInterval: " + splitInterval);
//log.debug("map maxSplits: " + maxSplits);
String splitPath = "";
int slashIndex = path.indexOf("/");
String part1 = path;
String part2 = "";
if (slashIndex > -1) {
part1 = path.substring(0, slashIndex);
part2 = path.substring(slashIndex + 1);
}
for (int i = 0; i < maxSplits && part1.length() > splitInterval; i++) {
if (splitPath.length() > 0) {
splitPath = splitPath + "/";
}
splitPath = splitPath + part1.substring(0, splitInterval);
part1 = part1.substring(splitInterval);
//log.debug("splitLevel: " + splitLevel);
//log.debug("part1: " + part1);
}
if (part1.length() > 0) {
splitPath = splitPath + "/" + part1;
}
if (part2.length() > 0) {
splitPath = splitPath + "/" + part2;
}
//splitPath = "/" + splitPath;
//log.debug("map split path: " + splitPath);
return splitPath;
}
/**
* Test if path should be ignored
*/
protected boolean ignorePath(String path) {
if (ignorePatterns != null) {
for (int i=0; i<this.ignorePatterns.length; i++) {
Matcher matcher = this.ignorePatterns[i].matcher(path);
if (matcher.matches()) {
if (log.isDebugEnabled()) {
log.debug(path + " matched ignore pattern " + ignorePatterns[i].pattern());
}
return true;
}
}
}
if (log.isDebugEnabled()) {
log.debug(path + " did not match any ignore patterns");
}
return false;
}
/**
*
*/
public boolean isResource(Path path) throws RepositoryException {
File file = new File(pathsDir, splitPath(path.toString()));
return file.isFile();
}
/**
*
*/
public boolean exists(Path path) throws RepositoryException {
//log.debug("Check if path exists: " + path);
File file = new File(pathsDir, splitPath(path.toString()));
//log.debug("Check if file exists: " + file);
// TODO: Get name of repository for debugging ...
return file.exists() && !ignorePath(file.getPath());
}
/**
*
*/
public boolean delete(Path path) throws RepositoryException {
// don't do anything because if we delete the file here, the delete
// in the storage will fail later
//File file = new File(pathsDir + path.toString());
//return file.delete();
return true;
}
/**
*
*/
public boolean isCollection(Path path) throws RepositoryException {
File file = new File(pathsDir, splitPath(path.toString()));
return file.isDirectory();
}
/**
* Get children
*/
public Path[] getChildren(Path path) throws RepositoryException {
String splitPath = splitPath(path.toString());
File file = new File(pathsDir, splitPath);
if (!file.exists()) {
log.warn("No such file or directory: " + file);
return new Path[0];
}
String[] filenames = file.list(this.childrenFilter);
// NOTE: This situation should only occur if one is trying to get children for a file than a directory! One might want to consider to test first with isResource() or isCollection().
if (filenames == null) {
log.warn("No children: " + path + " (" + file + ")");
return new Path[0];
}
log.debug("Number of children: " + filenames.length + " (" + file + ")");
Path[] children = new Path[filenames.length];
for (int i = 0;i < children.length; i++) {
if (path.toString().endsWith(File.separator)) {
children[i] = new Path(path + filenames[i]);
} else {
// NOTE: Do not use File.separator here, because it's the repository path and not the Operating System File System path
children[i] = new Path(path + "/" + filenames[i]);
}
log.debug("Child: " + children[i]);
}
return children;
}
/**
* Get UID
*/
public synchronized UID getUID(Path path) throws RepositoryException {
/*
String p = path.toString();
if (p.startsWith("/")) {
p = p.substring(1);
}
return new UID(p);
*/
return new UID(splitPath(path.toString()));
}
/**
* Create UID (and node within map!)
*/
public synchronized UID create(Path path, int type) throws RepositoryException {
log.debug("Create new node: " + path);
// TODO: Check if leading slash should be removed ...
File parent = new File(pathsDir, splitPath(path.getParent().toString()));
if (!parent.exists()) {
log.warn("Directory will be created: " + parent);
parent.mkdirs();
} else {
log.debug("Seems to exist already: " + parent);
}
if (type == org.wyona.yarep.core.NodeType.COLLECTION) {
//new File(parent, path.getName()).mkdir();
new File(parent, splitPath(path.getName())).mkdirs();
log.warn("Directory will be created: " + new File(parent, splitPath(path.getName())));
//log.debug("New UUID of collection: " + new UID(splitPath(path.getName())));
return new UID(splitPath(path.getName()));
} else {
try {
if(!new File(parent, path.getName()).createNewFile()) {
log.error("File has NOT been created: " + new File(parent, path.getName()));
} else {
log.warn("File has been created: " + new File(parent, path.getName()));
}
} catch (Exception e) {
log.error(e.getMessage(), e);
}
//log.debug("New UUID of resource: " + new UID(path.toString()));
return new UID(path.toString());
}
}
/**
*
*/
public void addSymbolicLink(Path path, UID uid) throws RepositoryException {
throw new RepositoryException("Symbolic links not implemented for virtual file system!");
}
/**
* Ignore all children which are matched by an ignore pattern (see repository configuration, e.g. src/test/repository/node-fs-example/repository.xml)
*/
protected class ChildrenFilter implements FilenameFilter {
public ChildrenFilter() {
}
public boolean accept(File dir, String name) {
if (TreeFileSystemMap.this.ignorePath(name)) {
return false;
} else {
return true;
}
}
}
/**
* Strip leading forward slash
*/
private String stripLeadingSlash(String path) {
if (path.startsWith("/")) {
String pathWithoutLeadingSlash = path.substring(1); // strip leading slash
return stripLeadingSlash(pathWithoutLeadingSlash);
} else {
return path;
}
}
}