/******************************************************************************* * Copyright (c) 2006-2015 * Software Technology Group, Dresden University of Technology * DevBoost GmbH, Dresden, Amtsgericht Dresden, HRB 34001 * * 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: * Software Technology Group - TU Dresden, Germany; * DevBoost GmbH - Dresden, Germany * - initial API and implementation ******************************************************************************/ package de.devboost.buildboost.discovery.reader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.util.LinkedHashSet; import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; import de.devboost.buildboost.artifacts.Package; import de.devboost.buildboost.artifacts.Plugin; import de.devboost.buildboost.model.UnresolvedDependency; /** * This class can be used to extract information from 'MANIFEST.MF' files. */ public class ManifestReader { public static final String UNKNOWN_SYMBOLIC_NAME = "UNKNOWN_SYMBOLIC_NAME"; public final static String ALPHA = "[a-zA-Z0-9_\\.-]"; public final static String ALPHA_AND_MINUS = "[a-zA-Z0-9_\\.]|-"; public final static String QUALIFIED_NAME_REGEX = "(" + ALPHA + "+)"; public final static String OPTION_NAME_REGEX = "((" + ALPHA_AND_MINUS + ")+)"; public final static String QUALIFIED_NUMBER_REGEX = "([0-9\\.]+)"; public final static String INCLUDING_EXCLUDING_REGEX = "(\\[|\\))"; public final static String BUNDLE_VERSION_REGEX = "bundle-version=\\\"(" + QUALIFIED_NUMBER_REGEX + "|" + INCLUDING_EXCLUDING_REGEX + QUALIFIED_NUMBER_REGEX + "," + QUALIFIED_NUMBER_REGEX + INCLUDING_EXCLUDING_REGEX + ")\\\""; public final static String RESOLUTION_REFEX = "resolution:=\\\"?optional\\\"?"; public final static String VISIBILITY_REGEX = "visibility:=\\\"?reexport\\\"?"; public final static String USES_REGEX = "uses:=\\\"?" + ALPHA + "\\\"?"; public final static String OPTION_VALUE_REGEX = "((\\\"[^\\\"]+\\\")+|[^\\\"]([^;,])+)"; public final static String OPTION_REGEX = ";[ ]*(" + OPTION_NAME_REGEX + "[:]?=" + OPTION_VALUE_REGEX + ")"; public final static String DEPENDENCY_REGEX = "(" + QUALIFIED_NAME_REGEX + "((" + OPTION_REGEX + ")*)" + ")" + ",?"; private final static Pattern DEPENDENCY_PATTERN = Pattern.compile(DEPENDENCY_REGEX); public final static String REQUIRED_BUNDLE_PREFIX = "Require-Bundle: "; public final static String IMPORT_PACKAGE_PREFIX = "Import-Package: "; public final static String EXPORT_PACKAGE_PREFIX = "Export-Package: "; public final static String NAME_PREFIX = "Bundle-Name: "; public final static String SYMBOLIC_NAME_PREFIX = "Bundle-SymbolicName: "; public final static String CLASSPATH_PREFIX = "Bundle-ClassPath: "; public final static String REQUIRE_VERSION_PREFIX = "bundle-version="; public final static String VERSION_PREFIX = "Bundle-Version: "; public final static String FRAGMENT_HOST_PREFIX = "Fragment-Host: "; public final static String RESOLUTION_OPTIONAL = "resolution:=\"optional\""; public final static Pattern REQUIRED_BUNDLE_REGEX = Pattern.compile(REQUIRED_BUNDLE_PREFIX + ".*(\r)?\n"); public final static Pattern IMPORT_PACKAGE_REGEX = Pattern.compile("(?<!c)" + IMPORT_PACKAGE_PREFIX + ".*(\r)?\n"); public final static Pattern EXPORT_PACKAGE_REGEX = Pattern.compile(EXPORT_PACKAGE_PREFIX + ".*(\r)?\n"); public final static Pattern NAME_REGEX = Pattern.compile(NAME_PREFIX + ".*(\r)?\n"); public final static Pattern SYMBOLIC_NAME_REGEX = Pattern.compile(SYMBOLIC_NAME_PREFIX + ".*(\r)?\n"); public final static Pattern CLASSPATH_REGEX = Pattern.compile(CLASSPATH_PREFIX + ".*(\r)?\n"); public static final Pattern VERSION_REGEX = Pattern.compile(VERSION_PREFIX + ".*(\r)?\n"); public static final Pattern FRAGMENT_HOST_REGEX = Pattern.compile(FRAGMENT_HOST_PREFIX + ".*(\r)?\n"); private String content; private Set<String> classpath; private Set<UnresolvedDependency> dependencies; private Set<String> exportedPackages; private String name; private String symbolicName; private UnresolvedDependency fragmentHost; private String version; public ManifestReader(InputStream manifestInputStream) throws IOException { content = getContentAsString(manifestInputStream); manifestInputStream.close(); content = content.replace("\r\n ", ""); content = content.replace("\n ", ""); content = content.replace("\r ", ""); } public Set<UnresolvedDependency> getDependencies() { if (dependencies == null) { dependencies = new LinkedHashSet<UnresolvedDependency>(); findDependencies(content, Plugin.class, REQUIRED_BUNDLE_REGEX, REQUIRED_BUNDLE_PREFIX); findDependencies(content, Package.class, IMPORT_PACKAGE_REGEX, IMPORT_PACKAGE_PREFIX); } return dependencies; } public Set<String> getExportedPackages() { if (exportedPackages == null) { exportedPackages = findDependencies(content, null, EXPORT_PACKAGE_REGEX, EXPORT_PACKAGE_PREFIX); if (getSymbolicName().equals("org.eclipse.osgi")) { addOSGINativeDefaultPackageExports(); } } return exportedPackages; } public String getSymbolicName() { if (symbolicName == null) { symbolicName = getValue(SYMBOLIC_NAME_REGEX, SYMBOLIC_NAME_PREFIX, UNKNOWN_SYMBOLIC_NAME); } return symbolicName; } public String getName() { if (name == null) { name = getValue(NAME_REGEX, NAME_PREFIX, "UNKNOWN_NAME"); } return name; } public Set<String> getBundleClassPath() { if (classpath == null) { classpath = new LinkedHashSet<String>(); Matcher matcher = CLASSPATH_REGEX.matcher(content); if (matcher.find()) { String classpathLine = matcher.group(); classpathLine = classpathLine.substring(CLASSPATH_PREFIX.length()); String[] classpathEntries = classpathLine.split(","); for (String classpathEntry : classpathEntries) { String[] parts = classpathEntry.split("\\;"); String path = parts[0].trim(); if (".".equals(path)) { continue; } // TODO this is declared in "org.eclipse.equinox.registry" but the JAR does not exist if ("runtime_registry_compatibility.jar".equals(path)) { continue; } classpath.add(path); } } } return classpath; } private Set<String> findDependencies(String content, Class<?> dependencyType, Pattern regex, String prefix) { Set<String> names = new LinkedHashSet<String>(); Matcher matcher = regex.matcher(content); if (matcher.find()) { String requiredBundlesLine = matcher.group(); requiredBundlesLine = requiredBundlesLine.substring(prefix.length()); Matcher matcher2 = DEPENDENCY_PATTERN.matcher(requiredBundlesLine); while (matcher2.find()) { String bundleOrPackageName = matcher2.group(2).trim(); String allOptions = matcher2.group(3).trim(); String[] options = allOptions.split(";( )*"); // TODO use these two properties boolean inclusiveMin = true; boolean inclusiveMax = true; String minVersion = null; String maxVersion = null; boolean optional = false; boolean reexport = false; // boolean uses = false; for (String option : options) { if ("".equals(option)) { continue; } String[] groups = getGroups(option, BUNDLE_VERSION_REGEX); if (groups != null) { inclusiveMin = "[".equals(groups[3]); String group4 = groups[4]; minVersion = group4 == null ? null : group4.trim(); String group5 = groups[5]; maxVersion = group5 == null ? null : group5.trim(); String group6 = groups[6]; inclusiveMax = "]".equals(group6); continue; } groups = getGroups(option, RESOLUTION_REFEX); if (groups != null) { optional = true; continue; } groups = getGroups(option, VISIBILITY_REGEX); if (groups != null) { reexport = true; continue; } /* * groups = getGroups(option, USES_REGEX); if (groups != null) { uses = true; continue; } */ } if ("system.bundle".equals(bundleOrPackageName)) { bundleOrPackageName = "org.eclipse.osgi"; } if (dependencyType != null) { UnresolvedDependency dependency = new UnresolvedDependency(dependencyType, bundleOrPackageName, minVersion, inclusiveMin, maxVersion, inclusiveMax, optional, reexport); if (dependencyType == Package.class && getExportedPackages().contains(bundleOrPackageName)) { // cyclic! // TODO improve package import/export support } else { dependencies.add(dependency); } } names.add(bundleOrPackageName); } } return names; } private String[] getGroups(String text, String regex) { Pattern pattern = Pattern.compile(regex); Matcher matcher = pattern.matcher(text); if (matcher.find()) { String[] groups = new String[matcher.groupCount() + 1]; for (int i = 0; i < groups.length; i++) { groups[i] = matcher.group(i); // System.out.println("groups[" + i +"] = " + groups[i]); } return groups; } return null; } public static String getContentAsString(InputStream inputStream) throws IOException { StringBuffer content = new StringBuffer(); InputStreamReader reader = new InputStreamReader(inputStream); int next = -1; while ((next = reader.read()) >= 0) { content.append((char) next); } return content.toString(); } public String getVersion() { if (version == null) { String rawVersion = getValue(VERSION_REGEX, VERSION_PREFIX, "UNKNOWN_VERSION"); Matcher matcher = Pattern.compile("[0-9]+(\\.[0-9]+)*").matcher(rawVersion); boolean found = matcher.find(); if (found) { version = matcher.group(); } } return version; } public UnresolvedDependency getFragmentHost() { if (fragmentHost == null) { String fragmentHostBundleName = getValue(FRAGMENT_HOST_REGEX, FRAGMENT_HOST_PREFIX, null); fragmentHost = new UnresolvedDependency(Plugin.class, fragmentHostBundleName, null, true, null, true, false, false); } return fragmentHost; } private String getValue(Pattern regex, String prefix, String defaultValue) { Matcher matcher = regex.matcher(content); if (matcher.find()) { String symbolicNameLine = matcher.group(); String[] parts = symbolicNameLine.split(";"); symbolicNameLine = parts[0]; symbolicNameLine = symbolicNameLine.replace("\n", ""); symbolicNameLine = symbolicNameLine.replace("\r", ""); symbolicNameLine = symbolicNameLine.substring(prefix.length()); return symbolicNameLine; } return defaultValue; } // TODO read .profile file(s) instead! private void addOSGINativeDefaultPackageExports() { exportedPackages.add("javax.accessibility"); exportedPackages.add("javax.activity"); exportedPackages.add("javax.crypto"); exportedPackages.add("javax.crypto.interfaces"); exportedPackages.add("javax.crypto.spec"); exportedPackages.add("javax.imageio"); exportedPackages.add("javax.imageio.event"); exportedPackages.add("javax.imageio.metadata"); exportedPackages.add("javax.imageio.plugins.bmp"); exportedPackages.add("javax.imageio.plugins.jpeg"); exportedPackages.add("javax.imageio.spi"); exportedPackages.add("javax.imageio.stream"); exportedPackages.add("javax.management"); exportedPackages.add("javax.management.loading"); exportedPackages.add("javax.management.modelmbean"); exportedPackages.add("javax.management.monitor"); exportedPackages.add("javax.management.openmbean"); exportedPackages.add("javax.management.relation"); exportedPackages.add("javax.management.remote"); exportedPackages.add("javax.management.remote.rmi"); exportedPackages.add("javax.management.timer"); exportedPackages.add("javax.naming"); exportedPackages.add("javax.naming.directory"); exportedPackages.add("javax.naming.event"); exportedPackages.add("javax.naming.ldap"); exportedPackages.add("javax.naming.spi"); exportedPackages.add("javax.net"); exportedPackages.add("javax.net.ssl"); exportedPackages.add("javax.print"); exportedPackages.add("javax.print.attribute"); exportedPackages.add("javax.print.attribute.standard"); exportedPackages.add("javax.print.event"); exportedPackages.add("javax.rmi"); exportedPackages.add("javax.rmi.CORBA"); exportedPackages.add("javax.rmi.ssl"); exportedPackages.add("javax.security.auth"); exportedPackages.add("javax.security.auth.callback"); exportedPackages.add("javax.security.auth.kerberos"); exportedPackages.add("javax.security.auth.login"); exportedPackages.add("javax.security.auth.spi"); exportedPackages.add("javax.security.auth.x500"); exportedPackages.add("javax.security.cert"); exportedPackages.add("javax.security.sasl"); exportedPackages.add("javax.sound.midi"); exportedPackages.add("javax.sound.midi.spi"); exportedPackages.add("javax.sound.sampled"); exportedPackages.add("javax.sound.sampled.spi"); exportedPackages.add("javax.sql"); exportedPackages.add("javax.sql.rowset"); exportedPackages.add("javax.sql.rowset.serial"); exportedPackages.add("javax.sql.rowset.spi"); exportedPackages.add("javax.swing"); exportedPackages.add("javax.swing.border"); exportedPackages.add("javax.swing.colorchooser"); exportedPackages.add("javax.swing.event"); exportedPackages.add("javax.swing.filechooser"); exportedPackages.add("javax.swing.plaf"); exportedPackages.add("javax.swing.plaf.basic"); exportedPackages.add("javax.swing.plaf.metal"); exportedPackages.add("javax.swing.plaf.multi"); exportedPackages.add("javax.swing.plaf.synth"); exportedPackages.add("javax.swing.table"); exportedPackages.add("javax.swing.text"); exportedPackages.add("javax.swing.text.html"); exportedPackages.add("javax.swing.text.html.parser"); exportedPackages.add("javax.swing.text.rtf"); exportedPackages.add("javax.swing.tree"); exportedPackages.add("javax.swing.undo"); exportedPackages.add("javax.transaction"); exportedPackages.add("javax.transaction.xa"); exportedPackages.add("javax.xml"); exportedPackages.add("javax.xml.datatype"); exportedPackages.add("javax.xml.namespace"); exportedPackages.add("javax.xml.parsers"); exportedPackages.add("javax.xml.transform"); exportedPackages.add("javax.xml.transform.dom"); exportedPackages.add("javax.xml.transform.sax"); exportedPackages.add("javax.xml.transform.stream"); exportedPackages.add("javax.xml.validation"); exportedPackages.add("javax.xml.xpath"); exportedPackages.add("org.ietf.jgss"); exportedPackages.add("org.omg.CORBA"); exportedPackages.add("org.omg.CORBA_2_3"); exportedPackages.add("org.omg.CORBA_2_3.portable"); exportedPackages.add("org.omg.CORBA.DynAnyPackage"); exportedPackages.add("org.omg.CORBA.ORBPackage"); exportedPackages.add("org.omg.CORBA.portable"); exportedPackages.add("org.omg.CORBA.TypeCodePackage"); exportedPackages.add("org.omg.CosNaming"); exportedPackages.add("org.omg.CosNaming.NamingContextExtPackage"); exportedPackages.add("org.omg.CosNaming.NamingContextPackage"); exportedPackages.add("org.omg.Dynamic"); exportedPackages.add("org.omg.DynamicAny"); exportedPackages.add("org.omg.DynamicAny.DynAnyFactoryPackage"); exportedPackages.add("org.omg.DynamicAny.DynAnyPackage"); exportedPackages.add("org.omg.IOP"); exportedPackages.add("org.omg.IOP.CodecFactoryPackage"); exportedPackages.add("org.omg.IOP.CodecPackage"); exportedPackages.add("org.omg.Messaging"); exportedPackages.add("org.omg.PortableInterceptor"); exportedPackages.add("org.omg.PortableInterceptor.ORBInitInfoPackage"); exportedPackages.add("org.omg.PortableServer"); exportedPackages.add("org.omg.PortableServer.CurrentPackage"); exportedPackages.add("org.omg.PortableServer.POAManagerPackage"); exportedPackages.add("org.omg.PortableServer.POAPackage"); exportedPackages.add("org.omg.PortableServer.portable"); exportedPackages.add("org.omg.PortableServer.ServantLocatorPackage"); exportedPackages.add("org.omg.SendingContext"); exportedPackages.add("org.omg.stub.java.rmi"); exportedPackages.add("org.w3c.dom"); exportedPackages.add("org.w3c.dom.bootstrap"); exportedPackages.add("org.w3c.dom.css"); exportedPackages.add("org.w3c.dom.events"); exportedPackages.add("org.w3c.dom.html"); exportedPackages.add("org.w3c.dom.ls"); exportedPackages.add("org.w3c.dom.ranges"); exportedPackages.add("org.w3c.dom.stylesheets"); exportedPackages.add("org.w3c.dom.traversal"); exportedPackages.add("org.w3c.dom.views "); exportedPackages.add("org.xml.sax"); exportedPackages.add("org.xml.sax.ext"); exportedPackages.add("org.xml.sax.helpers"); } }