/** * Copyright (C) 2013 Colorado School of Mines * * This file is part of the Interface Software Development Kit (SDK). * * The InterfaceSDK is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * The InterfaceSDK 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. * * You should have received a copy of the GNU General Public License * along with the InterfaceSDK. If not, see <http://www.gnu.org/licenses/>. */ package edu.mines.acmX.exhibit.module_management.loaders; import java.io.InputStream; import java.util.jar.JarEntry; import java.util.jar.JarFile; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.NodeList; import edu.mines.acmX.exhibit.module_management.metas.DependencyType; import edu.mines.acmX.exhibit.module_management.metas.ModuleMetaData; import edu.mines.acmX.exhibit.module_management.metas.ModuleMetaDataBuilder; /** * This class loads a single module manifest file and returns a ModuleMetaData object. * * @author Andrew DeMaria * @author Austin Diviness */ public class ModuleManifestLoader { public static final String MANIFEST_PATH = "manifest.xml"; // takes a path to a module jar and returns a ModuleMetaData object of the module manifest /** * Creates a ModuleMetaData object from a given path to a module jar. * * @param jarPath path to the Module that is to have its manifest loaded */ public static ModuleMetaData load(String jarPath) throws ManifestLoadException { ModuleMetaData toReturn; try { JarFile jar = new JarFile( jarPath ); JarEntry manifestEntry = jar.getJarEntry(MANIFEST_PATH); InputStream manifestStream = jar.getInputStream( manifestEntry ); DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance(); DocumentBuilder docBuilder = docFactory.newDocumentBuilder(); Document manifestDocument = docBuilder.parse( manifestStream ); manifestDocument.getDocumentElement().normalize(); // We are all setup now to parse the document. call helper functions toReturn = parseManifest( manifestDocument ); jar.close(); } catch(Exception e) { throw new ManifestLoadException("Could not load manifest" + "\n" + e.toString()); } return toReturn; } private static void checkAttribute( Element node, String attr ) throws ManifestLoadException { if( !node.hasAttribute(attr)) { throw new ManifestLoadException("Could not find the " + attr + " attribute"); } } /** * Helper function that parses the xml file to create a ModuleMetaData object. * * @param manifest The xml document to be parsed * * @return ModuleMetaData object describing the manifest * @throws ManifestLoadException */ private static ModuleMetaData parseManifest( Document manifest ) throws ManifestLoadException { ModuleMetaDataBuilder builder = new ModuleMetaDataBuilder(); Element manifestTag = (Element) manifest.getElementsByTagName("manifest").item(0); checkAttribute(manifestTag, "package"); builder.setPackageName(manifestTag.getAttribute("package")); checkAttribute(manifestTag, "class"); builder.setClassName(manifestTag.getAttribute("class")); checkAttribute( manifestTag, "icon"); builder.setIconPath( manifestTag.getAttribute("icon")); checkAttribute( manifestTag, "title"); builder.setTitle( manifestTag.getAttribute("title")); checkAttribute( manifestTag, "author"); builder.setAuthor( manifestTag.getAttribute("author")); checkAttribute( manifestTag, "version"); builder.setVersion( manifestTag.getAttribute("version")); NodeList sdk = manifestTag.getElementsByTagName("uses-sdk"); parseSdkVersion(sdk, builder); parseInputs(manifestTag.getElementsByTagName("inputs"), builder); parseModuleDependencies(manifestTag.getElementsByTagName("requires-module"), builder); return builder.build(); } /** * Helper function that parses the manifest sdk version information. * * @param sdkTag The NodeList of all instances of the 'uses-sdk' tag * @param builder The builder object that is gathering data * @throws ManifestLoadException */ private static void parseSdkVersion( NodeList sdkTag, ModuleMetaDataBuilder builder) throws ManifestLoadException { Element singleUsesTag = (Element) sdkTag.item(0); checkAttribute(singleUsesTag, "minSdkVersion"); checkAttribute(singleUsesTag, "targetSdkVersion"); builder.setMinSdkVersion(singleUsesTag.getAttribute("minSdkVersion")); builder.setTargetSdkVersion(singleUsesTag.getAttribute("targetSdkVersion")); } /** * Helper function that parses the manifest for required input services. * * @param inputs The NodeList of all instances of the 'required-inputs' tag * @param builder The builder object that is gathering data * @throws ManifestLoadException */ private static void parseInputs( NodeList inputs, ModuleMetaDataBuilder builder ) throws ManifestLoadException { Element singleInputTag = (Element) inputs.item(0); NodeList nodeList = singleInputTag.getElementsByTagName("input"); for( int i = 0; i < nodeList.getLength(); ++i ) { parseInput( (Element) nodeList.item(i), builder ); } } /** * Helper function that parses an input tag for its attributes. * * @param input An element representing a single input service requirement * @param builder The builder object that is gathering data * @throws ManifestLoadException */ private static void parseInput( Element input, ModuleMetaDataBuilder builder ) throws ManifestLoadException { checkAttribute( input, "input-type" ); String inputType = input.getAttribute("input-type").toLowerCase(); builder.addInputType(inputType, parseDependencyType(input)); } /** * Helper function that parses the manifest for required modules * * @param nodeList The NodeList of all instances of the 'required-module' tag * @param builder The builder object that is gathering data * @throws ManifestLoadException */ private static void parseModuleDependencies(NodeList nodeList, ModuleMetaDataBuilder builder) throws ManifestLoadException { Element element = (Element) nodeList.item(0); if (element.hasAttribute("optional-all")) { boolean optional_all = Boolean.parseBoolean(element.getAttribute("optional-all")); if (optional_all) { builder.setOptionalAll(true); return; } } NodeList modules = element.getElementsByTagName("module"); for( int i = 0; i < modules.getLength(); ++i ) { parseModuleDependency( (Element) modules.item(i), builder ); } } /** * Helper function that parses a single module dependency tag. * * @param element A single module dependency element * @param builder The builder object that is gathering data * @throws ManifestLoadException */ private static void parseModuleDependency(Element element, ModuleMetaDataBuilder builder) throws ManifestLoadException { checkAttribute(element, "package"); builder.addModuleDependency(element.getAttribute("package"), parseDependencyType(element)); } /** * Helper function that translates a 'optional' attribute to the proper * DependencyType. * * @param element element that has an 'optional' attribute * * @return The parsed DependencyType */ private static DependencyType parseDependencyType(Element element) { boolean optional = false; DependencyType dependencyType = DependencyType.REQUIRED; if (element.hasAttribute("optional")) { optional = Boolean.parseBoolean(element.getAttribute("optional")); } if (optional) { dependencyType = DependencyType.OPTIONAL; } return dependencyType; } }