/*******************************************************************************
* Copyright (c) 2007, 2010 Association for Decentralized Information Management
* in Industry THTH ry.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* VTT Technical Research Centre of Finland - initial API and implementation
*******************************************************************************/
package org.simantics.project.features.registry;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Enumeration;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;
import java.util.jar.Attributes;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.jar.Manifest;
import java.util.logging.Logger;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import org.eclipse.equinox.p2.metadata.Version;
import org.eclipse.equinox.p2.metadata.VersionedId;
import org.simantics.databoard.Bindings;
import org.simantics.databoard.Files;
import org.simantics.databoard.binding.Binding;
import org.simantics.databoard.serialization.SerializationException;
import org.simantics.graph.representation.TransferableGraph1;
import org.simantics.project.management.GraphBundleEx;
import org.simantics.utils.strings.StringUtils;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
/**
* A class for parsing plug-in bundle repositories for finding:
*
* - bundles that contain transferable graphs (graph.tg) or project feature,
* represented as {@link GraphBundleEx} objects
* - project feature extensions that contain installGroup definitions,
* represented as {@link GroupReference} objects
*
*
*
* Use {@link #parse(String)} or {@link #parse(File)} to parse a bundle or
* bundles from a directory. After parsing, {@link #getGraphBundles()} and
* {@link #getGroupReferences()} can be used to get what was found during
* parsing.
*
* @author J-P Laine
*/
public class PluginParser {
protected Logger log = Logger.getLogger(PluginParser.class.toString());
protected List graphBundles = new ArrayList();
protected Set groupReferences = new TreeSet();
/**
* @param args
*/
public static void main(String[] args) {
PluginParser tester = new PluginParser();
tester.parse("/home/jplaine/tmp/");
}
/**
* @return the list of versioned graph bundles found in the parsed bundle
* repositories so far
*/
public List getGraphBundles() {
return graphBundles;
}
public Set getGroupReferences() {
return groupReferences;
}
/**
* @param filename Jar file, or folder that contain jar files
*/
public void parse(String filename) {
parse(new File(filename));
}
/**
* @param root Jar file, or folder that contains jar files
*/
public void parse(File root) {
if(root.isFile()) {
parseJar(root.getAbsoluteFile().toString());
} else if(root.isDirectory()) {
for(File file : root.listFiles()) {
if (file.isFile()) {
parseJar(file.getAbsoluteFile().toString());
}
}
}
}
/*
* This is what to parse:
*
*
*/
public Collection parsePluginXML(InputStream is) throws IOException {
Collection result = new ArrayList();
try {
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db = dbf.newDocumentBuilder();
Document doc = db.parse(is);
doc.getDocumentElement().normalize();
for(Element element : getElementsByTagName(doc.getDocumentElement(), "extension")) {
if("org.simantics.project.feature".equals(element.getAttribute("point"))) {
for(Element feature : getElementsByTagName(element, "feature")) {
// log.info("class = "+feature.getAttribute("class"));
// log.info("description = "+feature.getAttribute("description"));
// log.info("id = "+feature.getAttribute("id"));
// log.info("label = "+feature.getAttribute("label"));
// log.info("published = "+feature.getAttribute("published"));
for(Element installGroup : getElementsByTagName(feature, "installGroup")) {
// log.info("id = "+installGroup.getAttribute("id"));
// log.info("version = "+installGroup.getAttribute("version"));
String id = StringUtils.safeString(installGroup.getAttribute("id"));
if (id.isEmpty()) {
// Invalid extension
// TODO: log warning
continue;
}
String version = StringUtils.safeString(installGroup.getAttribute("version"));
if (version.isEmpty())
// Empty version implies no version, mark that with null.
version = null;
result.add(new GroupReference(id, version));
}
}
}
}
} catch (ParserConfigurationException e) {
throw new IOException("Problem loading plugin.xml ", e);
} catch (SAXException e) {
throw new IOException("Problem loading plugin.xml ", e);
} finally {
is.close();
}
return result;
}
public GraphBundleEx parseGraph(InputStream is, Manifest mf) throws IOException {
String name = "";
String symbolicName = "";
VersionedId vid = null;
if(mf != null) {
Attributes attr = mf.getMainAttributes();
String versionInfo = attr.getValue("Bundle-Version");
symbolicName = attr.getValue("Bundle-SymbolicName");
org.osgi.framework.Version osgiVersion = new org.osgi.framework.Version(versionInfo);
Version p2Version = Version.createOSGi(osgiVersion.getMajor(), osgiVersion.getMinor(), osgiVersion.getMicro(), osgiVersion.getQualifier());
vid = new VersionedId(symbolicName, p2Version);
name = attr.getValue("Bundle-Name");
if(name == null) name = symbolicName;
}
GraphBundleEx bundleEntry = null;
try {
Binding binding = Bindings.getBindingUnchecked( TransferableGraph1.class );
TransferableGraph1 graph = (TransferableGraph1) Files.readFile(is, binding);
//System.out.println("getGraph(" + bundle.getSymbolicName() + "): before hashcode calculation in " + (System.nanoTime()-start)*1e-6 + "ms");
bundleEntry = new GraphBundleEx(name, graph, vid);
//System.out.println("getGraph(" + bundle.getSymbolicName() + "): completed in " + (System.nanoTime()-start)*1e-6 + "ms");
} catch (SerializationException ex) {
throw new IOException(ex);
} catch (IOException ex) {
throw new IOException("Problem loading graph.tg from bundle " + symbolicName, ex);
} catch (RuntimeException ex) {
throw new IOException("Problem loading graph.tg from bundle " + symbolicName, ex);
} finally {
is.close();
}
return bundleEntry;
}
public void parseJar(String filename) {
try {
JarFile jar = new JarFile(filename);
Enumeration e = jar.entries();
while (e.hasMoreElements()) {
JarEntry entry = e.nextElement();
if(entry.isDirectory()) continue;
if("plugin.xml".equals(entry.getName())) {
InputStream is = jar.getInputStream(entry);
Collection groupReferences = parsePluginXML(is);
for(GroupReference ref : groupReferences) {
log.info("Found group reference: "+ref);
}
} else if("graph.tg".equals(entry.getName())) {
InputStream is = jar.getInputStream(entry);
Manifest mf = jar.getManifest();
GraphBundleEx bundleEntry = parseGraph(is, mf);
log.info("Found graph bundle: "+bundleEntry);
}
}
jar.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
public static Collection getElementsByTagName(Element parent, String name) {
List elements = new ArrayList();
NodeList nodeLst = parent.getElementsByTagName(name);
for (int s = 0; s < nodeLst.getLength(); s++) {
if (nodeLst.item(s).getNodeType() == Node.ELEMENT_NODE) {
elements.add((Element) nodeLst.item(s));
}
}
return elements;
}
}