/* * 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) == '.'); } } }