diff --git a/tycho-extras/tycho-document-bundle-plugin/pom.xml b/tycho-extras/tycho-document-bundle-plugin/pom.xml index 52a0823dc5..c22c701022 100644 --- a/tycho-extras/tycho-document-bundle-plugin/pom.xml +++ b/tycho-extras/tycho-document-bundle-plugin/pom.xml @@ -9,7 +9,8 @@ - Contributors: - IBH SYSTEMS GmbH - initial API and implementation --> - 4.0.0 @@ -60,6 +61,11 @@ org.eclipse.help.base 4.4.100 + + org.eclipse.pde + org.eclipse.pde.core + 3.17.100 + diff --git a/tycho-extras/tycho-document-bundle-plugin/src/main/java/org/eclipse/tycho/extras/docbundle/ConvertSchemaToHtmlMojo.java b/tycho-extras/tycho-document-bundle-plugin/src/main/java/org/eclipse/tycho/extras/docbundle/ConvertSchemaToHtmlMojo.java new file mode 100644 index 0000000000..67889193e6 --- /dev/null +++ b/tycho-extras/tycho-document-bundle-plugin/src/main/java/org/eclipse/tycho/extras/docbundle/ConvertSchemaToHtmlMojo.java @@ -0,0 +1,101 @@ +/******************************************************************************* + * Copyright (c) 2023 Christoph Läubrich and others. + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Christoph Läubrich - initial API and implementation + *******************************************************************************/ +package org.eclipse.tycho.extras.docbundle; + +import java.io.File; +import java.lang.reflect.InvocationTargetException; +import java.net.URL; +import java.util.List; + +import org.apache.maven.model.Repository; +import org.apache.maven.plugin.AbstractMojo; +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugin.MojoFailureException; +import org.apache.maven.plugin.logging.Log; +import org.apache.maven.plugins.annotations.Component; +import org.apache.maven.plugins.annotations.LifecyclePhase; +import org.apache.maven.plugins.annotations.Mojo; +import org.apache.maven.plugins.annotations.Parameter; +import org.apache.maven.plugins.annotations.ResolutionScope; +import org.apache.maven.project.MavenProject; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.tycho.MavenRepositoryLocation; +import org.eclipse.tycho.extras.docbundle.runner.ConvertSchemaToHtmlResult; +import org.eclipse.tycho.extras.docbundle.runner.ConvertSchemaToHtmlRunner; +import org.eclipse.tycho.osgi.framework.EclipseApplication; +import org.eclipse.tycho.osgi.framework.EclipseFramework; +import org.eclipse.tycho.osgi.framework.EclipseWorkspace; +import org.eclipse.tycho.osgi.framework.EclipseWorkspaceManager; +import org.osgi.framework.BundleException; + +/** + * This mojo provides the functionality of + * org.eclipse.pde.internal.core.ant.ConvertSchemaToHTML + */ +@Mojo(name = "schema-to-html", defaultPhase = LifecyclePhase.PREPARE_PACKAGE, threadSafe = true, requiresDependencyCollection = ResolutionScope.COMPILE_PLUS_RUNTIME) +public class ConvertSchemaToHtmlMojo extends AbstractMojo { + + @Parameter() + private Repository pdeToolsRepository; + + @Parameter() + private File manifest; + @Parameter() + private List manifests; + @Parameter() + private File destination; + @Parameter() + private URL cssURL; + @Parameter() + private String additionalSearchPaths; + + @Parameter(property = "project") + private MavenProject project; + + @Component + private EclipseWorkspaceManager workspaceManager; + @Component + private PdeApplicationManager applicationManager; + + @Override + public void execute() throws MojoExecutionException, MojoFailureException { + MavenRepositoryLocation repository = PdeApplicationManager.getRepository(pdeToolsRepository); + EclipseApplication application = applicationManager.getApplication(repository); + EclipseWorkspace workspace = workspaceManager.getWorkspace(repository.getURL(), this); + try (EclipseFramework framework = application.startFramework(workspace, List.of())) { + ConvertSchemaToHtmlResult result = framework.execute(new ConvertSchemaToHtmlRunner(getManifestList(), + destination, cssURL, additionalSearchPaths, project.getBasedir())); + Log log = getLog(); + result.errors().forEach(log::error); + } catch (BundleException e) { + throw new MojoFailureException("Can't start framework!", e); + } catch (InvocationTargetException e) { + Throwable cause = e.getCause(); + if (cause.getClass().getName().equals(CoreException.class.getName())) { + throw new MojoFailureException(cause.getMessage(), cause); + } + throw new MojoExecutionException(cause); + } + } + + private List getManifestList() { + if (manifests != null && !manifests.isEmpty()) { + return manifests; + } + if (manifest != null) { + return List.of(manifest); + } + return List.of(); + } + +} diff --git a/tycho-extras/tycho-document-bundle-plugin/src/main/java/org/eclipse/tycho/extras/docbundle/PdeApplicationManager.java b/tycho-extras/tycho-document-bundle-plugin/src/main/java/org/eclipse/tycho/extras/docbundle/PdeApplicationManager.java new file mode 100644 index 0000000000..cd5482bcdc --- /dev/null +++ b/tycho-extras/tycho-document-bundle-plugin/src/main/java/org/eclipse/tycho/extras/docbundle/PdeApplicationManager.java @@ -0,0 +1,51 @@ +/******************************************************************************* + * Copyright (c) 2023 Christoph Läubrich and others. + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Christoph Läubrich - initial API and implementation + *******************************************************************************/ +package org.eclipse.tycho.extras.docbundle; + +import java.net.URI; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import org.apache.maven.model.Repository; +import org.codehaus.plexus.component.annotations.Component; +import org.codehaus.plexus.component.annotations.Requirement; +import org.eclipse.tycho.MavenRepositoryLocation; +import org.eclipse.tycho.TychoConstants; +import org.eclipse.tycho.osgi.framework.EclipseApplication; +import org.eclipse.tycho.osgi.framework.EclipseApplicationFactory; + +@Component(role = PdeApplicationManager.class) +public class PdeApplicationManager { + + static MavenRepositoryLocation getRepository(Repository location) { + if (location == null) { + return new MavenRepositoryLocation(null, URI.create(TychoConstants.ECLIPSE_LATEST)); + } + return new MavenRepositoryLocation(location.getId(), URI.create(location.getUrl())); + } + + private final Map buildIndexCache = new ConcurrentHashMap<>(); + + @Requirement + private EclipseApplicationFactory applicationFactory; + + public EclipseApplication getApplication(MavenRepositoryLocation repository) { + return buildIndexCache.computeIfAbsent(repository.getURL().normalize(), x -> { + EclipseApplication application = applicationFactory.createEclipseApplication(repository, "PDE Tools"); + application.addBundle("org.eclipse.pde.core"); + application.addBundle("org.eclipse.osgi.compatibility.state"); + return application; + }); + + } +} diff --git a/tycho-extras/tycho-document-bundle-plugin/src/main/java/org/eclipse/tycho/extras/docbundle/runner/ConvertSchemaToHtmlResult.java b/tycho-extras/tycho-document-bundle-plugin/src/main/java/org/eclipse/tycho/extras/docbundle/runner/ConvertSchemaToHtmlResult.java new file mode 100644 index 0000000000..2c168195e6 --- /dev/null +++ b/tycho-extras/tycho-document-bundle-plugin/src/main/java/org/eclipse/tycho/extras/docbundle/runner/ConvertSchemaToHtmlResult.java @@ -0,0 +1,32 @@ +/******************************************************************************* + * Copyright (c) 2023 Christoph Läubrich and others. + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Christoph Läubrich - initial API and implementation + *******************************************************************************/ +package org.eclipse.tycho.extras.docbundle.runner; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Stream; + +public class ConvertSchemaToHtmlResult implements Serializable { + + private List errors = new ArrayList<>(); + + public void addError(String error) { + errors.add(error); + } + + public Stream errors() { + return errors.stream(); + } + +} diff --git a/tycho-extras/tycho-document-bundle-plugin/src/main/java/org/eclipse/tycho/extras/docbundle/runner/ConvertSchemaToHtmlRunner.java b/tycho-extras/tycho-document-bundle-plugin/src/main/java/org/eclipse/tycho/extras/docbundle/runner/ConvertSchemaToHtmlRunner.java new file mode 100644 index 0000000000..b78a38c0cd --- /dev/null +++ b/tycho-extras/tycho-document-bundle-plugin/src/main/java/org/eclipse/tycho/extras/docbundle/runner/ConvertSchemaToHtmlRunner.java @@ -0,0 +1,212 @@ +/******************************************************************************* + * Copyright (c) 2000, 2023 IBM Corporation and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * IBM Corporation - initial API and implementation + * Chrsitoph Läubrich - adapt for using with Tycho + *******************************************************************************/ +package org.eclipse.tycho.extras.docbundle.runner; + +import java.io.BufferedInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.io.Serializable; +import java.net.URL; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.concurrent.Callable; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IPath; +import org.eclipse.osgi.util.ManifestElement; +import org.eclipse.osgi.util.NLS; +import org.eclipse.pde.core.plugin.IPluginExtensionPoint; +import org.eclipse.pde.core.plugin.IPluginModelBase; +import org.eclipse.pde.internal.core.ICoreConstants; +import org.eclipse.pde.internal.core.PDECoreMessages; +import org.eclipse.pde.internal.core.XMLDefaultHandler; +import org.eclipse.pde.internal.core.builders.SchemaTransformer; +import org.eclipse.pde.internal.core.ischema.ISchema; +import org.eclipse.pde.internal.core.ischema.ISchemaInclude; +import org.eclipse.pde.internal.core.plugin.ExternalFragmentModel; +import org.eclipse.pde.internal.core.plugin.ExternalPluginModel; +import org.eclipse.pde.internal.core.plugin.ExternalPluginModelBase; +import org.eclipse.pde.internal.core.schema.Schema; +import org.eclipse.pde.internal.core.schema.SchemaDescriptor; +import org.eclipse.pde.internal.core.util.HeaderMap; +import org.osgi.framework.Constants; + +/** + * This class is based on org.eclipse.pde.internal.core.ant.ConvertSchemaToHTML + */ +public class ConvertSchemaToHtmlRunner implements Callable, Serializable { + private List manifests; + private File destination; + private URL cssURL; + private String additionalSearchPaths; + private File baseDir; + + public ConvertSchemaToHtmlRunner(List manifests, File destination, URL cssURL, String additionalSearchPaths, + File baseDir) { + this.manifests = manifests; + this.destination = destination; + this.cssURL = cssURL; + this.additionalSearchPaths = additionalSearchPaths; + this.baseDir = baseDir; + } + + @Override + public ConvertSchemaToHtmlResult call() throws Exception { + SchemaTransformer fTransformer = new SchemaTransformer(); + ConvertSchemaToHtmlResult result = new ConvertSchemaToHtmlResult(); + for (File manifest : manifests) { + + IPluginModelBase model = readManifestFile(manifest); + if (model == null) { + return result; + } + + String pluginID = model.getPluginBase().getId(); + if (pluginID == null) { + pluginID = getPluginID(manifest); + } + + List searchPaths = getSearchPaths(); + + IPluginExtensionPoint[] extPoints = model.getPluginBase().getExtensionPoints(); + for (IPluginExtensionPoint extPoint : extPoints) { + String schemaLocation = extPoint.getSchema(); + + if (schemaLocation == null || schemaLocation.equals("")) { //$NON-NLS-1$ + continue; + } + Schema schema = null; + try { + File schemaFile = new File(model.getInstallLocation(), schemaLocation); + XMLDefaultHandler handler = new XMLDefaultHandler(); + org.eclipse.core.internal.runtime.XmlProcessorFactory.createSAXParserWithErrorOnDOCTYPE() + .parse(schemaFile, handler); + @SuppressWarnings("deprecation") + URL url = schemaFile.toURL(); + SchemaDescriptor desc = new SchemaDescriptor(extPoint.getFullId(), url, searchPaths); + schema = (Schema) desc.getSchema(false); + + // Check that all included schemas are available + ISchemaInclude[] includes = schema.getIncludes(); + for (ISchemaInclude include : includes) { + ISchema includedSchema = include.getIncludedSchema(); + if (includedSchema == null) { + result.addError(NLS.bind(PDECoreMessages.ConvertSchemaToHTML_CannotFindIncludedSchema, + include.getLocation(), schemaFile)); + } + } + + File directory = destination; + if (!directory.exists() || !directory.isDirectory()) { + if (!directory.mkdirs()) { + schema.dispose(); + return result; + } + } + + String id = extPoint.getId(); + if (id.indexOf('.') == -1) { + id = pluginID + "." + id; //$NON-NLS-1$ + } + File file = new File(directory, id.replace('.', '_') + ".html"); //$NON-NLS-1$ + try (PrintWriter out = new PrintWriter( + new OutputStreamWriter(new FileOutputStream(file), StandardCharsets.UTF_8), true)) { + fTransformer.transform(schema, out, cssURL, SchemaTransformer.BUILD); + } + } finally { + if (schema != null) { + schema.dispose(); + } + } + } + } + return result; + } + + private String getPluginID(File manifest) { + File OSGiFile = new File(manifest.getParentFile(), ICoreConstants.BUNDLE_FILENAME_DESCRIPTOR); + + if (OSGiFile.exists()) { + try (FileInputStream manifestStream = new FileInputStream(OSGiFile)) { + Map headers = ManifestElement.parseBundleManifest(manifestStream, new HeaderMap<>()); + String value = headers.get(Constants.BUNDLE_SYMBOLICNAME); + if (value == null) { + return null; + } + ManifestElement[] elements = ManifestElement.parseHeader(Constants.BUNDLE_SYMBOLICNAME, value); + if (elements.length > 0) { + return elements[0].getValue(); + } + } catch (Exception e1) { + System.out.print(e1.getMessage()); + } + } + return null; + } + + /** + * @return user specified search paths or null + */ + private List getSearchPaths() { + if (this.additionalSearchPaths == null) { + return null; + } + String[] paths = this.additionalSearchPaths.split(","); //$NON-NLS-1$ + List result = new ArrayList<>(paths.length); + for (String pathString : paths) { + IPath path = IPath.fromOSString(pathString); + if (path.isValidPath(pathString)) { + if (!path.isAbsolute()) { + path = IPath.fromOSString(baseDir.getPath()).append(path); + } + result.add(path); + } else { + System.out + .println(NLS.bind(PDECoreMessages.ConvertSchemaToHTML_InvalidAdditionalSearchPath, pathString)); + } + } + return result; + } + + private IPluginModelBase readManifestFile(File manifest) throws IOException, CoreException { + try (InputStream stream = new BufferedInputStream(new FileInputStream(manifest))) { + ExternalPluginModelBase model = null; + switch (manifest.getName().toLowerCase(Locale.ENGLISH)) { + case ICoreConstants.FRAGMENT_FILENAME_DESCRIPTOR: + model = new ExternalFragmentModel(); + break; + case ICoreConstants.PLUGIN_FILENAME_DESCRIPTOR: + model = new ExternalPluginModel(); + break; + default: + stream.close(); + throw new IOException(NLS.bind(PDECoreMessages.Builders_Convert_illegalValue, "manifest")); //$NON-NLS-1$ + } + String parentPath = manifest.getParentFile().getAbsolutePath(); + model.setInstallLocation(parentPath); + model.load(stream, false); + stream.close(); + return model; + } + } +}