/*
* Copyright (C) 2007 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: Apr 13, 2008
*/
package uk.me.parabola.mkgmap.osmstyle;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.PrintStream;
import java.io.Reader;
import java.io.StringReader;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Pattern;
import uk.me.parabola.imgfmt.Utils;
import uk.me.parabola.log.Logger;
/**
* Load a style from a single file. All the files that make up the style
* are held in the same file and delimited by a simple header.
*
* <p>Lines before the first header are ignored. A header looks like this:
* <<<filename>>>, that is three opening angle brackets, the name
* of the file and three closing angle brackets. The opening brackets
* must be at the beginning of the line, there can be trailing junk after
* the closing brackets which is ignored.
*
* <p>All lines after the header and before the next header or end of file
* are part of the named file.
*
* @author Steve Ratcliffe
*/
public class CombinedStyleFileLoader extends StyleFileLoader {
private static final Logger log = Logger.getLogger(CombinedStyleFileLoader.class);
private final Map<String, String> files = new HashMap<String, String>();
private final String styleName;
private static final Pattern STYLE_SUFFIX = Pattern.compile("\\.style$");
private static final Pattern FILENAME_START_MARK = Pattern.compile("<<<");
private static final Pattern FILENAME_END_MARK = Pattern.compile(">>>.*");
public CombinedStyleFileLoader(String filename) throws FileNotFoundException {
styleName = STYLE_SUFFIX.matcher(filename).replaceFirst("");
Reader in = new FileReader(filename);
try {
loadFiles(in);
} finally {
Utils.closeFile(in);
}
}
private void loadFiles(Reader in) {
BufferedReader r = new BufferedReader(in);
StringBuffer currentFile = new StringBuffer();
try {
String line;
String currentName = null;
while ((line = r.readLine()) != null) {
if (line.startsWith("<<<")) {
if (currentName != null) {
// Save previous file if any.
files.put(currentName, currentFile.toString());
}
line = FILENAME_START_MARK.matcher(line).replaceFirst("");
line = FILENAME_END_MARK.matcher(line).replaceFirst("");
log.debug("reading file", line);
currentName = line;
currentFile = new StringBuffer();
} else {
currentFile.append(line);
currentFile.append('\n');
}
}
if (currentName == null) {
log.error("failed to read style file");
} else {
files.put(currentName, currentFile.toString());
}
} catch (IOException e) {
log.error("failed to read style file");
}
}
/**
* Open the specified file in the style definition.
*
* @param filename The name of the file in the style.
* @return An open file reader for the file.
* @throws FileNotFoundException When the file can't be opened.
*/
public Reader open(String filename) throws FileNotFoundException {
log.info("opening", filename);
String contents = files.get(filename);
if (contents == null)
throw new FileNotFoundException(filename);
log.debug("file", filename, "found");
return new StringReader(contents);
}
/**
* Close the FileLoader. This is different from closing individual files that
* were opened via {@link #open}. After this call then you shouldn't open any
* more files.
*/
public void close() {
files.clear();
}
public String[] list() {
String basename = styleName.replaceFirst(".*[/\\\\]", "");
basename = basename.replaceFirst("\\.[^.]+$", "");
return new String[] {basename};
}
/**
* Covert between the single file simple-archive form and the directory
* form. Mostly for fun.
*
* @param args Arguments, you supply a directory or a file. If its a
* directory then covert into a simple-archive file and if it is a
* file then expand into separate files.
*/
public static void main(String[] args) {
String name = args[0];
File file = new File(name);
PrintStream out = System.out;
try {
if (file.isDirectory()) {
convertToFile(file, out);
} else {
String dirname;
int ind = name.lastIndexOf('.');
if (ind > 0)
dirname = name.substring(0, ind);
else
dirname = name + ".d"; // got to do something...
convertToDirectory(name, dirname);
}
} catch (FileNotFoundException e) {
System.err.println("Could not open file " + e);
System.exit(1);
} catch (IOException e) {
System.err.println("Could not read file " + e);
System.exit(1);
}
}
private static void convertToDirectory(String name, String dirname) throws IOException {
CombinedStyleFileLoader loader = new CombinedStyleFileLoader(name);
File dir = new File(dirname);
dir.mkdir();
for (String s : loader.files.keySet()) {
File ent = new File(dir, s);
ent.getParentFile().mkdirs();
FileWriter writer = new FileWriter(ent);
BufferedReader r = null;
try {
r = new BufferedReader(loader.open(s));
String line;
while ((line = r.readLine()) != null) {
writer.write(line);
writer.write('\n');
}
} finally {
if (r != null) r.close();
writer.close();
}
}
loader.close();
}
private static void convertToFile(File file, PrintStream out) throws IOException {
File[] list = file.listFiles(new NoHiddenFilter());
convertToFile(out, list, null);
}
private static void convertToFile(PrintStream out, File[] list, String prefix) throws IOException {
for (File entry : list) {
if (entry.isFile()) {
out.print("<<<");
if (prefix != null) {
out.print(prefix);
out.print('/');
}
out.print(entry.getName());
out.println(">>>");
BufferedReader r = new BufferedReader(new FileReader(entry));
String line;
while ((line = r.readLine()) != null)
out.println(line);
r.close();
} else {
convertToFile(out, entry.listFiles(new NoHiddenFilter()), entry.getName());
}
}
}
private static class NoHiddenFilter implements FilenameFilter {
public boolean accept(File dir, String name) {
return !(name.isEmpty() || name.charAt(0) == '.');
}
}
}