From 2886f96da21b9063e86610e2edf2e8da03e1f3aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christoph=20L=C3=A4ubrich?= Date: Mon, 30 Oct 2023 15:21:51 +0100 Subject: [PATCH] Add schema-to-html mojo as a replacement for ant ConvertSchemaToHTML Currently platform build contains ant targets to run ConvertSchemaToHTML for documentation purpose. This adds as a first step a new mojo tycho-document-bundle-plugin:schema-to-html that offers the same functionality and should act as a base to further improve the brittle manually maintained references the current process requires and already allows to move things from the ant build to the maven build files. (cherry picked from commit 4c276d99b3ac591e130a60303f19043c35434757) --- .../tycho-document-bundle-plugin/pom.xml | 8 +- .../docbundle/ConvertSchemaToHtmlMojo.java | 101 +++++++++ .../docbundle/PdeApplicationManager.java | 51 +++++ .../runner/ConvertSchemaToHtmlResult.java | 32 +++ .../runner/ConvertSchemaToHtmlRunner.java | 212 ++++++++++++++++++ 5 files changed, 403 insertions(+), 1 deletion(-) create mode 100644 tycho-extras/tycho-document-bundle-plugin/src/main/java/org/eclipse/tycho/extras/docbundle/ConvertSchemaToHtmlMojo.java create mode 100644 tycho-extras/tycho-document-bundle-plugin/src/main/java/org/eclipse/tycho/extras/docbundle/PdeApplicationManager.java create mode 100644 tycho-extras/tycho-document-bundle-plugin/src/main/java/org/eclipse/tycho/extras/docbundle/runner/ConvertSchemaToHtmlResult.java create mode 100644 tycho-extras/tycho-document-bundle-plugin/src/main/java/org/eclipse/tycho/extras/docbundle/runner/ConvertSchemaToHtmlRunner.java diff --git a/tycho-extras/tycho-document-bundle-plugin/pom.xml b/tycho-extras/tycho-document-bundle-plugin/pom.xml index 2a99c989d3..8c65d4843f 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; + } + } +}