/*
* $Id$
*
* Copyright (C) 2003-2015 JNode.org
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library 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 Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library; If not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
package org.jnode.build;
import java.io.File;
import java.io.FileReader;
import java.io.FilenameFilter;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import org.apache.tools.ant.taskdefs.Manifest;
import org.apache.tools.ant.taskdefs.ManifestException;
import org.jnode.nanoxml.XMLElement;
import org.jnode.plugin.PluginException;
/**
* @author epr
*/
public final class PluginList {
private final List<URL> descrList;
private final List<URL> pluginList;
private final String name;
private Manifest manifest;
private List<PluginList> includes = new ArrayList<PluginList>();
private final File defaultDir;
public PluginList(File file, File defaultDir, String targetArch)
throws PluginException, MalformedURLException {
this.defaultDir = defaultDir;
descrList = new ArrayList<URL>();
pluginList = new ArrayList<URL>();
final XMLElement root = new XMLElement(new Hashtable<Object, Object>(), true, false);
try {
final FileReader r = new FileReader(file);
try {
root.parseFromReader(r);
} finally {
r.close();
}
} catch (IOException ex) {
throw new PluginException(ex);
}
if (!root.getName().equals("plugin-list")) {
throw new PluginException("plugin-list element expected");
}
this.name = (String) root.getAttribute("name");
if (name == null) {
throw new PluginException("name attribute is missing in " + file);
}
for (Iterator<?> i = root.getChildren().iterator(); i.hasNext();) {
final XMLElement e = (XMLElement) i.next();
if (e.getName().equals("plugin")) {
final String id = e.getStringAttribute("id");
if (id == null) {
throw new PluginException("id attribute expected on "
+ e.getName());
}
// version attribute is optional
// if not specified, then the latest version found will be used.
final String version = e.getStringAttribute("version");
addPlugin(descrList, pluginList, id, version);
} else if (e.getName().equals("manifest")) {
manifest = parseManifest(e);
} else if (e.getName().equals("include")) {
parseInclude(e, file.getParentFile(), defaultDir, targetArch,
descrList, pluginList);
} else {
throw new PluginException("Unknown element " + e.getName());
}
}
}
/**
*
* @param id
* @param version optional
* @throws MalformedURLException
* @throws PluginException
*/
public void addPlugin(String id, String version) throws MalformedURLException, PluginException {
addPlugin(descrList, pluginList, id, version);
}
/**
*
* @param descrList
* @param pluginList
* @param id
* @param version optional parameter
* @throws MalformedURLException
* @throws PluginException
*/
private void addPlugin(List<URL> descrList, List<URL> pluginList, String id, String version)
throws MalformedURLException, PluginException {
final File f = findPlugin(defaultDir, id, version);
final URL pluginUrl = f.toURI().toURL(); // ensure proper escaping ...
final URL descrUrl = new URL("jar:" + pluginUrl + "!/plugin.xml");
if (pluginList.contains(pluginUrl)) {
String versionStr = (version == null) ? "unspecified" : version;
throw new PluginException("can't use the same id(" + id + ") and version(" + versionStr
+ ") for multiple plugins");
}
descrList.add(descrUrl);
pluginList.add(pluginUrl);
}
private File findPlugin(File dir, final String id, String version) {
// System.out.println("Find " + id + " in " + dir);
final String begin = id + "_";
final String end = ".jar";
String[] names = dir.list(new FilenameFilter() {
public boolean accept(File dir, String name) {
return name.startsWith(begin) && name.endsWith(end);
}
});
if (names.length == 0) {
throw new IllegalArgumentException("Cannot find plugin " + id + " in " + dir + " for list " + this.name);
}
String filename = null;
if (version != null) {
// version specified, try to find it
for (String name : names) {
String v = name.substring(begin.length(), name.length() - end.length());
if (version.equals(v)) {
filename = name; // found exact version
break;
}
}
}
if (filename == null) {
// by default, take the latest version
filename = names[names.length - 1];
}
return new File(dir, filename);
}
private Manifest parseManifest(XMLElement me) throws PluginException {
Manifest mf = this.manifest;
if (mf == null) {
mf = new Manifest();
}
for (Iterator<?> i = me.getChildren().iterator(); i.hasNext();) {
final XMLElement e = (XMLElement) i.next();
if (e.getName().equals("attribute")) {
final String k = e.getStringAttribute("key");
final String v = e.getStringAttribute("value");
try {
mf.addConfiguredAttribute(new Manifest.Attribute(k, v));
} catch (ManifestException ex) {
throw new PluginException("Error in manifest", ex);
}
} else {
throw new PluginException("Unknown element " + e.getName());
}
}
return mf;
}
private void parseInclude(XMLElement e, File curDir, File defaultDir, String targetArch, List<URL> descrList,
List<URL> pluginList)
throws MalformedURLException, PluginException {
File file = new File(e.getStringAttribute("file"));
if (!file.isAbsolute()) {
file = new File(curDir, file.getPath());
}
final PluginList inc = new PluginList(file, defaultDir, targetArch);
includes.add(inc);
if (this.manifest == null) {
this.manifest = inc.manifest;
} else if (inc.manifest != null) {
try {
final Manifest merged = new Manifest();
merged.merge(inc.manifest);
merged.merge(this.manifest);
this.manifest = merged;
} catch (ManifestException ex) {
throw new PluginException(ex);
}
}
descrList.addAll(inc.descrList);
pluginList.addAll(inc.pluginList);
}
/**
* Gets the maximum last modification date of all URL's.
*
* @return last modification date
* @throws IOException
*/
public long lastModified() throws IOException {
long max = 0;
for (URL url : descrList) {
final URLConnection conn2 = url.openConnection();
max = Math.max(max, conn2.getLastModified());
}
for (PluginList inc : includes) {
max = Math.max(max, inc.lastModified());
}
return max;
}
/**
* Gets all URL's to plugin descriptors.
*
* @return URL[]
*/
public URL[] getDescriptorUrlList() {
return descrList.toArray(new URL[descrList.size()]);
}
/**
* Gets all URL's to the plugin files (jar format).
*
* @return URL[]
*/
public URL[] getPluginList() {
return pluginList.toArray(new URL[pluginList.size()]);
}
/**
* @return Returns the manifest.
*/
public final Manifest getManifest() {
return this.manifest;
}
/**
* @return Returns the name.
*/
public final String getName() {
return name;
}
/**
* Create an URL to a plugin with a given id and version.
*/
public final URL createPluginURL(String id, String version) {
try {
// The toURI().toURL() pattern is to ensure that funky characters
// in the plugin pathname are properly escaped in the URL
return findPlugin(defaultDir, id, version).toURI().toURL();
} catch (MalformedURLException e) {
throw new RuntimeException(e);
}
}
}