/*
* 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<>();
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
);
StringBuilder currentFile =
new StringBuilder();
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 StringBuilder();
} else {
currentFile.
append(line
).
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 {
try (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();
try (FileWriter writer =
new FileWriter(ent
);
BufferedReader r =
new BufferedReader(loader.
open(s
))) {
String line
;
while ((line = r.
readLine()) !=
null) {
writer.
write(line
);
writer.
write('\n');
}
}
}
}
}
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(">>>");
try (BufferedReader r =
new BufferedReader(new FileReader(entry
))) {
String line
;
while ((line = r.
readLine()) !=
null)
out.
println(line
);
}
} 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) ==
'.');
}
}
}