| /******************************************************************************* |
| * Copyright (c) 2000, 2008 IBM Corporation and others. |
| * 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: |
| * IBM Corporation - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.equinox.internal.p2.metadata.generator.features; |
| |
| import java.io.*; |
| import java.net.URL; |
| import java.util.*; |
| import java.util.jar.JarEntry; |
| import java.util.jar.JarFile; |
| import javax.xml.parsers.*; |
| import org.eclipse.core.runtime.IStatus; |
| import org.eclipse.core.runtime.Status; |
| import org.eclipse.equinox.internal.p2.core.helpers.LogHelper; |
| import org.eclipse.equinox.internal.p2.metadata.generator.Activator; |
| import org.eclipse.equinox.internal.p2.metadata.generator.LocalizationHelper; |
| import org.eclipse.equinox.internal.provisional.p2.metadata.generator.Feature; |
| import org.eclipse.equinox.internal.provisional.p2.metadata.generator.FeatureEntry; |
| import org.eclipse.osgi.util.NLS; |
| import org.xml.sax.*; |
| import org.xml.sax.helpers.DefaultHandler; |
| |
| /** |
| * Default feature parser. |
| * Parses the feature manifest file as defined by the platform. |
| * |
| * @since 3.0 |
| */ |
| public class FeatureParser extends DefaultHandler { |
| |
| private final static SAXParserFactory parserFactory = SAXParserFactory.newInstance(); |
| private SAXParser parser; |
| protected Feature result; |
| private URL url; |
| private StringBuffer characters = null; |
| |
| private Properties messages = null; |
| private List messageKeys = null; |
| |
| public FeatureParser() { |
| this(true); |
| } |
| |
| protected FeatureParser(boolean createParser) { |
| super(); |
| if (!createParser) |
| return; |
| try { |
| parserFactory.setNamespaceAware(true); |
| this.parser = parserFactory.newSAXParser(); |
| } catch (ParserConfigurationException e) { |
| System.out.println(e); |
| } catch (SAXException e) { |
| System.out.println(e); |
| } |
| } |
| |
| public void characters(char[] ch, int start, int length) throws SAXException { |
| if (characters == null) |
| return; |
| characters.append(ch, start, length); |
| } |
| |
| protected Feature createFeature(String id, String version) { |
| return new Feature(id, version); |
| } |
| |
| public void endElement(String uri, String localName, String qName) throws SAXException { |
| if (characters == null) |
| return; |
| if ("description".equals(localName)) { //$NON-NLS-1$ |
| result.setDescription(localize(characters.toString().trim())); |
| } else if ("license".equals(localName)) { //$NON-NLS-1$ |
| result.setLicense(localize(characters.toString().trim())); |
| } else if ("copyright".equals(localName)) { //$NON-NLS-1$ |
| result.setCopyright(localize(characters.toString().trim())); |
| } |
| characters = null; |
| } |
| |
| private void loadProperties(File directory, Properties properties) { |
| //skip directories that don't contain a feature.properties file |
| File file = new File(directory, "feature.properties"); //$NON-NLS-1$ |
| if (!file.exists()) |
| return; |
| try { |
| InputStream input = new BufferedInputStream(new FileInputStream(file)); |
| try { |
| properties.load(input); |
| } finally { |
| if (input != null) |
| input.close(); |
| } |
| } catch (IOException e) { |
| e.printStackTrace(); |
| } |
| } |
| |
| private void loadProperties(JarFile jar, Properties properties) { |
| JarEntry entry = jar.getJarEntry("feature.properties"); //$NON-NLS-1$ |
| if (entry == null) |
| return; |
| try { |
| InputStream input = new BufferedInputStream(jar.getInputStream(entry)); |
| try { |
| properties.load(input); |
| } finally { |
| if (input != null) |
| input.close(); |
| } |
| } catch (IOException e) { |
| e.printStackTrace(); |
| } |
| } |
| |
| private String localize(String value) { |
| if (messages == null || value == null) |
| return value; |
| if (!value.startsWith("%")) //$NON-NLS-1$ |
| return value; |
| String key = value.substring(1); |
| messageKeys.add(key); |
| return value; |
| } |
| |
| /** |
| * Parses the specified location and constructs a feature. The given location |
| * should be either the location of the feature JAR or the directory containing |
| * the feature. |
| * |
| * @param location the location of the feature to parse. |
| */ |
| public Feature parse(File location) { |
| if (!location.exists()) |
| return null; |
| |
| Feature feature = null; |
| Properties properties = new Properties(); |
| |
| if (location.isDirectory()) { |
| //skip directories that don't contain a feature.xml file |
| File file = new File(location, "feature.xml"); //$NON-NLS-1$ |
| if (!file.exists()) |
| return null; |
| loadProperties(location, properties); |
| try { |
| InputStream input = new BufferedInputStream(new FileInputStream(file)); |
| feature = parse(input, properties); |
| if (feature != null) { |
| String[] keyStrings = (String[]) messageKeys.toArray(new String[messageKeys.size()]); |
| feature.setLocalizations(LocalizationHelper.getDirPropertyLocalizations(location, "feature", null, keyStrings)); //$NON-NLS-1$ |
| } |
| } catch (FileNotFoundException e) { |
| e.printStackTrace(); |
| } |
| } else if (location.getName().endsWith(".jar")) { //$NON-NLS-1$ |
| JarFile jar = null; |
| try { |
| jar = new JarFile(location); |
| loadProperties(jar, properties); |
| JarEntry entry = jar.getJarEntry("feature.xml"); //$NON-NLS-1$ |
| if (entry == null) |
| return null; |
| InputStream input = new BufferedInputStream(jar.getInputStream(entry)); |
| feature = parse(input, properties); |
| if (feature != null) { |
| String[] keyStrings = (String[]) messageKeys.toArray(new String[messageKeys.size()]); |
| feature.setLocalizations(LocalizationHelper.getJarPropertyLocalizations(location, "feature", null, keyStrings)); //$NON-NLS-1$ |
| } |
| } catch (IOException e) { |
| e.printStackTrace(); |
| } catch (SecurityException e) { |
| LogHelper.log(new Status(IStatus.WARNING, Activator.ID, "Exception parsing feature: " + location.getAbsolutePath(), e)); //$NON-NLS-1$ |
| } finally { |
| try { |
| if (jar != null) |
| jar.close(); |
| } catch (IOException e) { |
| // |
| } |
| } |
| } |
| return feature; |
| } |
| |
| /** |
| * Parse the given input stream and return a feature object |
| * or null. This method closes the input stream. |
| */ |
| public Feature parse(InputStream in, Properties messages) { |
| this.messages = messages; |
| this.messageKeys = new ArrayList(messages.size()); |
| result = null; |
| try { |
| parser.parse(new InputSource(in), this); |
| } catch (SAXException e) { |
| e.printStackTrace(); |
| } catch (IOException e) { |
| e.printStackTrace(); |
| } finally { |
| try { |
| in.close(); |
| } catch (IOException e1) { |
| // Utils.log(e1.getLocalizedMessage()); |
| } |
| } |
| return result; |
| } |
| |
| private void processCopyright(Attributes attributes) { |
| result.setCopyrightURL(attributes.getValue("url")); //$NON-NLS-1$ |
| characters = new StringBuffer(); |
| } |
| |
| private void processDescription(Attributes attributes) { |
| result.setDescriptionURL(attributes.getValue("url")); //$NON-NLS-1$ |
| characters = new StringBuffer(); |
| } |
| |
| private void processDiscoverySite(Attributes attributes) { |
| //ignore discovery sites of type 'web' |
| if ("web".equals(attributes.getValue("type"))) //$NON-NLS-1$ //$NON-NLS-2$ |
| return; |
| result.addDiscoverySite(attributes.getValue("label"), attributes.getValue("url")); //$NON-NLS-1$ //$NON-NLS-2$ |
| } |
| |
| protected void processFeature(Attributes attributes) { |
| String id = attributes.getValue("id"); //$NON-NLS-1$ |
| String ver = attributes.getValue("version"); //$NON-NLS-1$ |
| |
| if (id == null || id.trim().equals("") //$NON-NLS-1$ |
| || ver == null || ver.trim().equals("")) { //$NON-NLS-1$ |
| // System.out.println(NLS.bind(Messages.FeatureParser_IdOrVersionInvalid, (new String[] { id, ver}))); |
| } else { |
| result = createFeature(id, ver); |
| result.setApplication(attributes.getValue("application")); //$NON-NLS-1$ |
| result.setPlugin(attributes.getValue("plugin")); //$NON-NLS-1$ |
| result.setExclusive(Boolean.valueOf(attributes.getValue("exclusive")).booleanValue()); //$NON-NLS-1$ |
| result.setPrimary(Boolean.valueOf(attributes.getValue("primary")).booleanValue()); //$NON-NLS-1$ |
| |
| //TODO rootURLs |
| if (url != null && "file".equals(url.getProtocol())) { //$NON-NLS-1$ |
| File f = new File(url.getFile().replace('/', File.separatorChar)); |
| result.setURL("features" + "/" + f.getParentFile().getName() + "/");// + f.getName()); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ |
| } else { |
| // externalized URLs might be in relative form, ensure they are absolute |
| // feature.setURL(Utils.makeAbsolute(Utils.getInstallURL(), url).toExternalForm()); |
| } |
| |
| result.setProviderName(localize(attributes.getValue("provider-name"))); //$NON-NLS-1$ |
| result.setLabel(localize(attributes.getValue("label"))); //$NON-NLS-1$ |
| |
| // Utils.debug("End process DefaultFeature tag: id:" +id + " ver:" +ver + " url:" + feature.getURL()); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ |
| } |
| } |
| |
| private void processImport(Attributes attributes) { |
| String id = attributes.getValue("feature"); //$NON-NLS-1$ |
| FeatureEntry entry = null; |
| if (id != null) { |
| if ("true".equalsIgnoreCase(attributes.getValue("patch"))) { //$NON-NLS-1$ //$NON-NLS-2$ |
| entry = FeatureEntry.createRequires(id, attributes.getValue("version"), "perfect", attributes.getValue("filter"), false); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ |
| entry.setPatch(true); |
| } else { |
| entry = FeatureEntry.createRequires(id, attributes.getValue("version"), attributes.getValue("match"), attributes.getValue("filter"), false); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ |
| } |
| } else { |
| id = attributes.getValue("plugin"); //$NON-NLS-1$ |
| entry = FeatureEntry.createRequires(id, attributes.getValue("version"), attributes.getValue("match"), attributes.getValue("filter"), true); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ |
| } |
| result.addEntry(entry); |
| } |
| |
| private void processIncludes(Attributes attributes) { |
| FeatureEntry entry = new FeatureEntry(attributes.getValue("id"), attributes.getValue("version"), false); //$NON-NLS-1$ //$NON-NLS-2$ |
| String flag = attributes.getValue("optional"); //$NON-NLS-1$ |
| if (flag != null) |
| entry.setOptional(Boolean.valueOf(flag).booleanValue()); |
| setEnvironment(attributes, entry); |
| result.addEntry(entry); |
| } |
| |
| private void processInstallHandler(Attributes attributes) { |
| result.setInstallHandler(attributes.getValue("handler")); //$NON-NLS-1$ |
| result.setInstallHandlerLibrary(attributes.getValue("library")); //$NON-NLS-1$ |
| result.setInstallHandlerURL(attributes.getValue("url")); //$NON-NLS-1$ |
| } |
| |
| private void processLicense(Attributes attributes) { |
| result.setLicenseURL(attributes.getValue("url")); //$NON-NLS-1$ |
| characters = new StringBuffer(); |
| } |
| |
| private void processPlugin(Attributes attributes) { |
| String id = attributes.getValue("id"); //$NON-NLS-1$ |
| String version = attributes.getValue("version"); //$NON-NLS-1$ |
| |
| if (id == null || id.trim().equals("") || version == null || version.trim().equals("")) { //$NON-NLS-1$ //$NON-NLS-2$ |
| System.out.println(NLS.bind("FeatureParser#processPlugin, ID {0} or version {1} invalid", (new String[] {id, version}))); //$NON-NLS-1$ |
| } else { |
| FeatureEntry plugin = new FeatureEntry(id, version, true); |
| setEnvironment(attributes, plugin); |
| String unpack = attributes.getValue("unpack"); //$NON-NLS-1$ |
| if (unpack != null) |
| plugin.setUnpack(Boolean.valueOf(unpack).booleanValue()); |
| String fragment = attributes.getValue("fragment"); //$NON-NLS-1$ |
| if (fragment != null) |
| plugin.setFragment(Boolean.valueOf(fragment).booleanValue()); |
| String filter = attributes.getValue("filter"); //$NON-NLS-1$ |
| if (filter != null) |
| plugin.setFilter(filter); |
| result.addEntry(plugin); |
| |
| // Utils.debug("End process DefaultFeature tag: id:" + id + " ver:" + ver + " url:" + feature.getURL()); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ |
| } |
| } |
| |
| private void processUpdateSite(Attributes attributes) { |
| result.setUpdateSiteLabel(attributes.getValue("label")); //$NON-NLS-1$ |
| result.setUpdateSiteURL(attributes.getValue("url")); //$NON-NLS-1$ |
| } |
| |
| private void setEnvironment(Attributes attributes, FeatureEntry entry) { |
| String os = attributes.getValue("os"); //$NON-NLS-1$ |
| String ws = attributes.getValue("ws"); //$NON-NLS-1$ |
| String nl = attributes.getValue("nl"); //$NON-NLS-1$ |
| String arch = attributes.getValue("arch"); //$NON-NLS-1$ |
| entry.setEnvironment(os, ws, arch, nl); |
| } |
| |
| public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { |
| // Utils.debug("Start Element: uri:" + uri + " local Name:" + localName + " qName:" + qName); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ |
| if ("plugin".equals(localName)) { //$NON-NLS-1$ |
| processPlugin(attributes); |
| } else if ("description".equals(localName)) { //$NON-NLS-1$ |
| processDescription(attributes); |
| } else if ("license".equals(localName)) { //$NON-NLS-1$ |
| processLicense(attributes); |
| } else if ("copyright".equals(localName)) { //$NON-NLS-1$ |
| processCopyright(attributes); |
| } else if ("feature".equals(localName)) { //$NON-NLS-1$ |
| processFeature(attributes); |
| } else if ("import".equals(localName)) { //$NON-NLS-1$ |
| processImport(attributes); |
| } else if ("includes".equals(localName)) { //$NON-NLS-1$ |
| processIncludes(attributes); |
| } else if ("install-handler".equals(localName)) { //$NON-NLS-1$ |
| processInstallHandler(attributes); |
| } else if ("update".equals(localName)) { //$NON-NLS-1$ |
| processUpdateSite(attributes); |
| } else if ("discovery".equals(localName)) { //$NON-NLS-1$ |
| processDiscoverySite(attributes); |
| } |
| } |
| |
| } |