aQute.libg.remote.source.SourceFS Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of biz.aQute.bndlib Show documentation
Show all versions of biz.aQute.bndlib Show documentation
bndlib: A Swiss Army Knife for OSGi
package aQute.libg.remote.source;
import java.io.File;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import aQute.lib.collections.MultiMap;
import aQute.lib.io.IO;
import aQute.libg.cryptography.SHA1;
import aQute.libg.remote.Delta;
import aQute.libg.remote.Sink;
class SourceFS {
static Pattern WINDOWS_PREFIX = Pattern.compile("(\\p{Alpha}):\\\\(.*)");
static Pattern WINDOWS_FILE_P = Pattern
.compile("(?:\\p{Alpha}:|\\\\)(\\\\[\\p{Alnum}-_+.~@$%&=]+)*");
static Pattern UNIX_FILE_P = Pattern.compile("(/[\\p{Alnum}-_+.~@$%&=]+)+");
static Pattern LOCAL_P = File.separatorChar == '\\' ? WINDOWS_FILE_P : UNIX_FILE_P;
private MultiMap shas = new MultiMap();
private final Map files = new HashMap();
private final boolean pathConversion;
private final String cwd;
private final char separatorChar;
private Sink sink;
private String areaId;
/*
* The information we maintain per file that we sync remotely.
*/
static class FileDescription {
File file;
String path;
String sha;
long modified;
boolean touched;
public boolean transform;
public boolean dir;
}
SourceFS(char separatorChar, File cwd, Sink sink, String areaId) {
this.separatorChar = separatorChar;
this.cwd = cwd.getAbsolutePath();
pathConversion = File.separatorChar != separatorChar;
this.sink = sink;
this.areaId = areaId;
}
public String transform(String s) throws Exception {
Matcher m = LOCAL_P.matcher(s);
StringBuffer sb = new StringBuffer();
boolean found = false;
while (m.find()) {
FileDescription fd = toRemote(m.group(0));
fd.touched = true;
m.appendReplacement(sb, fd.path);
found = true;
}
if (!found)
return s;
m.appendTail(sb);
return sb.toString();
}
private FileDescription toRemote(String localPath) throws Exception {
File f = new File(localPath);
return toRemote(f);
}
private FileDescription toRemote(File f) throws NoSuchAlgorithmException, Exception {
FileDescription fd = files.get(f);
if (fd != null)
return fd;
String remotePath = toRemotePath(f);
fd = new FileDescription();
fd.file = f;
fd.path = remotePath;
fd.modified = f.lastModified();
if (f.isFile()) {
fd.sha = updateSha(null, f);
fd.touched = true;
} else if (f.isDirectory()) {
fd.dir = true;
for (File sub : f.listFiles()) {
toRemote(sub);
}
}
files.put(f, fd);
return fd;
}
private String toRemotePath(File f) {
String remotePath;
String abs = f.getAbsolutePath();
if (abs.startsWith(cwd)) {
remotePath = abs.substring(cwd.length());
while (remotePath.startsWith(File.separator))
remotePath = remotePath.substring(1);
} else {
if (File.separatorChar == '\\') {
//
// Why is windows always 10x more complicated??
// Buffoons ...
//
// WE have
// Remote names: \\foo\zoo
// device names c:\foo
// absolute \zoo\zoo
//
if (abs.startsWith("\\\\")) { // remote file path, should be
// followed by remote name
remotePath = "_ABS\\REMOTE" + abs.substring(1);
} else {
Matcher m = WINDOWS_PREFIX.matcher(abs);
if (m.matches()) {
remotePath = "_ABS\\" + m.group(1) + "\\" + m.group(2);
} else
remotePath = "_ABS\\" + abs;
}
} else
remotePath = "_ABS" + abs;
}
if (pathConversion)
remotePath = remotePath.replace(File.separatorChar, separatorChar);
return remotePath;
}
public void sync() throws Exception {
Set toBeDeleted = new HashSet();
List deltas = new ArrayList();
for (FileDescription fd : new HashSet(files.values())) {
if (fd.transform) {
Delta delta = new Delta();
delta.path = fd.path;
delta.content = transform(IO.collect(fd.file));
deltas.add(delta);
}
}
for (FileDescription fd : files.values()) {
if (fd.file.isDirectory())
continue;
if (fd.modified != fd.file.lastModified() || fd.touched) {
fd.touched = false;
Delta delta = new Delta();
delta.path = fd.path;
fd.modified = fd.file.lastModified();
fd.touched = true;
if (!fd.file.isFile()) {
delta.delete = true;
toBeDeleted.add(fd);
deltas.add(delta);
continue;
}
if (!fd.transform) {
String updateSha = updateSha(fd.sha, fd.file);
delta.sha = fd.sha = updateSha;
deltas.add(delta);
}
}
}
files.values().removeAll(toBeDeleted);
sync(deltas);
}
protected void sync(List deltas) throws Exception {
sink.sync(areaId, deltas);
}
public String updateSha(String oldSha, File file) throws NoSuchAlgorithmException, Exception {
if (oldSha != null)
shas.remove(oldSha);
if (file != null && file.isFile()) {
String sha = SHA1.digest(file).asHex();
shas.add(sha, file);
return sha;
}
return null;
}
public byte[] getData(String sha) throws Exception {
List files = shas.get(sha);
if (files == null)
return null;
for (File f : files) {
if (f.isFile()) {
assert sha.equals(SHA1.digest(f).asHex());
return IO.read(f);
}
}
return null;
}
public void markTransform(File f) throws Exception {
FileDescription fd = toRemote(f);
fd.transform = true;
}
public String add(File file) throws Exception {
return toRemote(file).path;
}
}