/*
* 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: Feb 17, 2008
*/
package uk.me.parabola.mkgmap.osmstyle;
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.UnsupportedEncodingException;
import java.net.JarURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import uk.me.parabola.log.Logger;
/**
* Load a style from a jar file.
*
* The style can just be jared up at the top level or it can be
* contained within a directory in the jar. You can have more than one
* style in the jar. In this case a name will be required to select
* the one that you want to use. It looks for a file with a name that
* ends with 'version' to work out where the style is. If a name is given
* then it looks for a file path ending {@code name/version}.
*
* @author Steve Ratcliffe
*/
public class JarFileLoader extends StyleFileLoader {
private static final Logger log = Logger.getLogger(JarFileLoader.class);
private JarFile jarFile;
private String prefix;
public JarFileLoader(URL url) throws FileNotFoundException {
jarInit(url, null);
}
public JarFileLoader(String url, String name) throws FileNotFoundException {
try {
jarInit(new URL(makeJarUrl(url)), name);
} catch (MalformedURLException e) {
throw new FileNotFoundException("Could not open style at " + url);
}
}
private String makeJarUrl(String url) {
if (url.toLowerCase().startsWith("jar:"))
return url;
else
return "jar:" + url + "!/";
}
private void jarInit(URL url, String name) throws FileNotFoundException {
log.debug("opening", url);
try {
JarURLConnection jurl = (JarURLConnection) url.openConnection();
jurl.setUseCaches(false);
jarFile = jurl.getJarFile();
prefix = jurl.getEntryName();
if (prefix == null) {
prefix = searchVersion(jarFile, name);
}
log.debug("jar prefix is", prefix);
} catch (IOException e) {
throw new FileNotFoundException("Could not open style at " + url);
}
}
/**
* Find path in archive
* @param file the JarFile instance
* @param style a style name or null to find any version file
* @return return prefix of (first) entry that contains file version
*/
private String searchVersion(JarFile file, String style) {
Enumeration<JarEntry> en = file.entries();
String flatEnd = style==null ? "version" : style + "/version";
String end = "/" + flatEnd;
while (en.hasMoreElements()) {
JarEntry entry = en.nextElement();
String ename = entry.getName();
if (ename.endsWith(end) || ename.equals(flatEnd))
return ename.substring(0, ename.length() - "version".length());
}
return null;
}
/**
* 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 {
if (jarFile == null)
throw new FileNotFoundException("Could not open file " + filename);
String path = filename;
if (prefix != null)
path = prefix + filename;
JarEntry jarEntry = jarFile.getJarEntry(path);
if (jarEntry == null)
throw new FileNotFoundException("Could not open style file " + filename);
InputStream stream;
try {
stream = jarFile.getInputStream(jarEntry);
} catch (IOException e) {
throw new FileNotFoundException("Could not open " + filename);
}
Reader reader = null;
try {
reader = new InputStreamReader(stream, "UTF-8");
} catch (UnsupportedEncodingException e) {
System.out.println("JarFileLoader: Encoding UTF-8 not supported");
reader = new InputStreamReader(stream);
}
return new BufferedReader(reader);
}
public void close() {
try {
jarFile.close();
} catch (IOException e) {
log.debug("failed to close jar file");
}
}
protected void finalize() throws Throwable {
super.finalize();
close();
}
public String[] list() {
Enumeration<JarEntry> en = jarFile.entries();
List<String> list = new ArrayList<String>();
while (en.hasMoreElements()) {
JarEntry entry = en.nextElement();
if (!entry.isDirectory()) {
String name = entry.getName();
if (name.endsWith("version")) {
log.debug("name is", name);
String[] dirs = name.split("/");
if (dirs.length == 1) {
String s = jarFile.getName();
s = s.replaceFirst("\\..*$", "");
s = s.replaceAll(".*/", "");
list.add(s);
}
else
list.add(dirs[dirs.length - 2]);
}
}
}
return list.toArray(new String[list.size()]);
}
}