Subversion Repositories mkgmap

Rev

Rev 4514 | Blame | Compare with Previous | Last modification | View Log | RSS feed

/*
 * 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.FileInputStream;
import java.io.InputStreamReader;
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<>();

        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 InputStreamReader(new FileInputStream(filename)/*NB: DefaultCharset*/)) {
                        readOptionFile(r, filename);
                }
               
        }

        public void readOptionFile(Reader r, String filename) {
                BufferedReader br;
                if (r instanceof BufferedReader)
                        br = (BufferedReader) r;
                else
                        br = new BufferedReader(r);
                TokenScanner ts = new TokenScanner(filename, br);
                ts.setExtraWordChars("-");
                if (r instanceof InputStreamReader)
                        ts.setCharset(((InputStreamReader)r).getEncoding());

                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 (":".equals(punc) || "=".equals(punc)) {
                                        val = ts.readLine();
                                } else if ("{".equals(punc)) {
                                        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 ("input-file".equals(key) && !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();
                        }
                }
        }
}