package org.basex.build;
import static org.basex.core.Text.*;
import static org.basex.data.DataText.*;
import java.io.IOException;
import java.util.Locale;
import java.util.regex.Pattern;
import org.basex.core.Prop;
import org.basex.core.cmd.Store;
import org.basex.io.IO;
import org.basex.io.IOContent;
import org.basex.io.IOFile;
import org.basex.util.TokenBuilder;
import org.basex.util.Util;
import org.basex.util.list.StringList;
/**
* This class recursively scans files and directories and parses all
* relevant files.
*
* @author BaseX Team 2005-12, BSD License
* @author Christian Gruen
*/
public final class DirParser extends TargetParser {
/** Number of skipped files to log. */
private static final int SKIPLOG = 10;
/** Skipped files. */
private final StringList skipped = new StringList();
/** File pattern. */
private final Pattern filter;
/** Properties. */
private final Prop prop;
/** Initial file path. */
private final String root;
/** Parse archives in directories. */
private final boolean archives;
/** Skip corrupt files in directories. */
private final boolean skip;
/** Database path for storing binary files. */
protected IOFile binaries;
/** Last source. */
private IO lastSrc;
/** Parser reference. */
private Parser parser;
/** Element counter. */
private int c;
/**
* Constructor.
* @param source source path
* @param pr database properties
* @param path future database path
*/
public DirParser(final IO source, final Prop pr, final IOFile path) {
this(source, "", pr, path);
}
/**
* Constructor, specifying a target path.
* @param source source path
* @param target target path
* @param pr database properties
* @param path future database path
*/
public DirParser(final IO source, final String target, final Prop pr,
final IOFile path) {
super(source, target);
prop = pr;
final String parent = source.dir();
root = parent.endsWith("/") ? parent : parent + '/';
skip = prop.is(Prop.SKIPCORRUPT);
archives = prop.is(Prop.ADDARCHIVES);
filter = !source.isDir() && !source.isArchive() ? null :
Pattern.compile(IOFile.regex(pr.get(Prop.CREATEFILTER)));
binaries = path != null && prop.is(Prop.ADDRAW) ?
new IOFile(path, M_RAW) : null;
}
@Override
public void parse(final Builder build) throws IOException {
build.meta.filesize = 0;
build.meta.original = src.path();
parse(build, src);
}
/**
* Parses the specified file or its children.
* @param b builder
* @param io current input
* @throws IOException I/O exception
*/
private void parse(final Builder b, final IO io) throws IOException {
if(io.isDir()) {
// only {@link IOFile} instances can have children
for(final IO f : ((IOFile) io).children()) parse(b, f);
} else {
src = io;
while(io.more(archives)) {
b.checkStop();
String nm = io.name();
if(Prop.WIN) nm = nm.toLowerCase(Locale.ENGLISH);
final long l = src.length();
if(l != -1) b.meta.filesize += l;
// use global target as prefix
String targ = trg;
final String name = src.name();
String path = src.path();
// add relative path without root (prefix) and file name (suffix)
if(path.endsWith('/' + name)) {
path = path.substring(0, path.length() - name.length());
if(path.startsWith(root)) path = path.substring(root.length());
targ = (targ + path).replace("//", "/");
}
if(filter != null && !filter.matcher(nm).matches()) {
// store binary files
if(binaries != null) {
Store.store(src.inputSource(), new IOFile(binaries, targ + name));
}
} else {
boolean ok = true;
IO in = io;
if(skip) {
// parse file twice to ensure that it is well-formed
try {
// cache file contents to allow or speed up a second run
in = new IOContent(io.read());
in.name(io.name());
parser = Parser.fileParser(in, prop, targ);
MemBuilder.build("", parser, prop);
} catch(final IOException ex) {
Util.debug(ex.getMessage());
skipped.add(io.path());
ok = false;
}
}
if(ok) {
parser = Parser.fileParser(in, prop, targ);
parser.parse(b);
}
parser = null;
if(Util.debug && (++c & 0x3FF) == 0) Util.err(";");
}
}
}
}
@Override
public String info() {
final TokenBuilder tb = new TokenBuilder();
if(skipped.size() != 0) {
tb.add(SKIPPED).add(COL).add(NL);
final int s = skipped.size();
for(int i = 0; i < s && i < SKIPLOG; i++) {
tb.add(LI).add(skipped.get(i)).add(NL);
}
if(s > SKIPLOG) {
tb.add(LI).addExt(MORE_SKIPPED_X, s - SKIPLOG).add(NL);
}
}
return tb.toString();
}
@Override
public String det() {
return parser != null ? parser.detail() : src.path();
}
@Override
public double prog() {
if(parser != null) return parser.progress();
if(lastSrc == src) return 1;
lastSrc = src;
return Math.random();
}
@Override
public void close() throws IOException {
if(parser != null) parser.close();
}
}