/*
* Copyright (C) 2008 Steve Ratcliffe
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*
* Author: Steve Ratcliffe
* Create date: May 26, 2008
*/
package uk.me.parabola.mkgmap;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.Reader;
import java.util.Collection;
import java.util.HashSet;
import uk.me.parabola.log.Logger;
import uk.me.parabola.mkgmap.scan.TokType;
import uk.me.parabola.mkgmap.scan.Token;
import uk.me.parabola.mkgmap.scan.TokenScanner;
/**
* Holds and reads options. Like a properties file, but order is important
* and events are generated when options are read.
*
* You can use the normal option syntax <tt>foo=bar</tt>.
* You can also use <tt>foo: bar</tt> and for longer options that
* span several lines <tt>foo { this can span lines }</tt>
*
* @author Steve Ratcliffe
*/
public class Options {
private static final Logger log = Logger.getLogger(Options.class);
private final OptionProcessor proc;
// Used to prevent the same file being read more than once.
private final Collection<String> readFiles = new HashSet<String>();
public Options(OptionProcessor proc) {
this.proc = proc;
}
/**
* Read a config file that contains more options. When the number of
* options becomes large it is more convenient to place them in a file.
*
* If the same file is read more than once, then the second time
* will be ignored.
*
* @param filename The filename to obtain options from.
*/
public void readOptionFile(String filename) throws IOException {
log.info("reading option file", filename);
File file = new File(filename);
try {
// Don't read the same file twice.
String path = file.getCanonicalPath();
if (readFiles.contains(path))
return;
readFiles.add(path);
} catch (IOException e) {
// Probably want to do more than warn here.
log.warn("the config file could not be read");
return;
}
try (Reader r = new FileReader(filename)) {
readOptionFile(r, filename);
}
}
public void readOptionFile(Reader r, String filename) {
BufferedReader br = new BufferedReader(r);
TokenScanner ts = new TokenScanner(filename, br);
ts.setExtraWordChars("-");
File file = new File(filename);
String parent = file.getParent();
while (!ts.isEndOfFile()) {
Token tok = ts.nextToken();
if (tok.isValue("#")) {
ts.skipLine();
continue;
}
String key = tok.getValue();
ts.skipSpace();
tok = ts.peekToken();
if (tok.getType() == TokType.SYMBOL) {
String punc = ts.nextValue();
String val;
if (punc.equals(":") || punc.equals("=")) {
val = ts.readLine();
} else if (punc.equals("{")) {
ts.skipSpace();
val = ts.readUntil(TokType.SYMBOL, "}");
ts.nextToken(); // discard the closing brace
} else {
ts.skipLine();
continue;
}
// Relative file names in the file are taken relative to the
// location of the argument file.
if (key.equals("input-file") && !new File(val).isAbsolute())
val = new File(parent, val).getPath();
proc.processOption(new Option(key, val));
} else if (key != null){
proc.processOption(new Option(key, ""));
} else {
ts.skipLine();
}
}
}
}