diff options
Diffstat (limited to 'plugins/toolsmiths/org.eclipse.papyrus.dev.project.management/src/org/eclipse/papyrus/dev/project/management/handlers/SyncManifestAndPOMVersions.java')
-rw-r--r-- | plugins/toolsmiths/org.eclipse.papyrus.dev.project.management/src/org/eclipse/papyrus/dev/project/management/handlers/SyncManifestAndPOMVersions.java | 273 |
1 files changed, 273 insertions, 0 deletions
diff --git a/plugins/toolsmiths/org.eclipse.papyrus.dev.project.management/src/org/eclipse/papyrus/dev/project/management/handlers/SyncManifestAndPOMVersions.java b/plugins/toolsmiths/org.eclipse.papyrus.dev.project.management/src/org/eclipse/papyrus/dev/project/management/handlers/SyncManifestAndPOMVersions.java new file mode 100644 index 00000000000..66772afd2c7 --- /dev/null +++ b/plugins/toolsmiths/org.eclipse.papyrus.dev.project.management/src/org/eclipse/papyrus/dev/project/management/handlers/SyncManifestAndPOMVersions.java @@ -0,0 +1,273 @@ +/***************************************************************************** + * Copyright (c) 2016 Christian W. Damus 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: + * Christian W. Damus - Initial API and implementation + * + *****************************************************************************/ + +package org.eclipse.papyrus.dev.project.management.handlers; + +import java.io.BufferedReader; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.StringWriter; +import java.nio.CharBuffer; +import java.nio.charset.Charset; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Stream; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.transform.OutputKeys; +import javax.xml.transform.Result; +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.dom.DOMSource; +import javax.xml.transform.stream.StreamResult; + +import org.eclipse.core.commands.AbstractHandler; +import org.eclipse.core.commands.ExecutionEvent; +import org.eclipse.core.commands.ExecutionException; +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.IProjectDescription; +import org.eclipse.core.resources.ResourcesPlugin; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.OperationCanceledException; +import org.eclipse.core.runtime.Status; +import org.eclipse.core.runtime.SubMonitor; +import org.eclipse.core.runtime.jobs.ISchedulingRule; +import org.eclipse.core.runtime.jobs.Job; +import org.eclipse.core.runtime.jobs.MultiRule; +import org.eclipse.papyrus.dev.project.management.Activator; +import org.eclipse.ui.IWorkbenchWindow; +import org.eclipse.ui.handlers.HandlerUtil; +import org.eclipse.ui.progress.IProgressService; +import org.eclipse.ui.statushandlers.StatusManager; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +/** + * Ensures that <tt>pom.xml</tt> files have the same version number as their + * corresponding <tt>MANIFEST.MF</tt> or <tt>feature.xml</tt> file. + * + * @since 1.2 + */ +public class SyncManifestAndPOMVersions extends AbstractHandler { + private static final Pattern BUNDLE_VERSION = Pattern.compile("^Bundle-Version:\\s*(\\S+)\\s*$", Pattern.MULTILINE); //$NON-NLS-1$ + private static final Pattern POM_FIXUP = Pattern.compile("^(<\\?xml\\s.*?\\?>)\\s*(<project>)\\s*$", Pattern.MULTILINE); + + public SyncManifestAndPOMVersions() { + super(); + } + + @Override + public Object execute(ExecutionEvent event) throws ExecutionException { + Map<IFile, IFile> pomsByManifest = getAllPOMsByManifest(); + + ISchedulingRule[] rules = Stream.concat( + pomsByManifest.keySet().stream(), + pomsByManifest.values().stream()).toArray(ISchedulingRule[]::new); + MultiRule jobRule = new MultiRule(rules); + + Job updateJob = new Job("Synchronize POM versions") { + + { + setRule(jobRule); + } + + @Override + protected IStatus run(IProgressMonitor monitor) { + SubMonitor sub = SubMonitor.convert(monitor, "Updating POMs...", pomsByManifest.size()); + + pomsByManifest.forEach((manifest, pom) -> { + try { + if (sub.isCanceled()) { + throw new OperationCanceledException(); + } + + String version = getVersion(manifest); + if (version != null) { + version = version.replace(".qualifier", "-SNAPSHOT"); //$NON-NLS-1$//$NON-NLS-2$ + Document xml = slurpXML(pom); + Element versionElement = findVersion(xml); + if (versionElement != null) { + if (!version.equals(versionElement.getTextContent())) { + versionElement.setTextContent(version); + write(xml, pom); + } + } + } + + sub.worked(1); + } catch (CoreException e) { + StatusManager.getManager().handle(e.getStatus(), StatusManager.SHOW); + } + }); + + sub.done(); + + return Status.OK_STATUS; + } + }; + updateJob.schedule(); + + IWorkbenchWindow window = HandlerUtil.getActiveWorkbenchWindow(event); + IProgressService progress = window.getWorkbench().getProgressService(); + progress.showInDialog(window.getShell(), updateJob); + + return null; + } + + Map<IFile, IFile> getAllPOMsByManifest() { + Map<IFile, IFile> result = new HashMap<>(); + + Stream.of(ResourcesPlugin.getWorkspace().getRoot().getProjects()) + .filter(this::hasPluginOrFeatureNature) + .forEach(project -> { + IFile pom = project.getFile("pom.xml"); //$NON-NLS-1$ + if ((pom != null) && pom.isAccessible()) { + IFile manifest = project.getFile("feature.xml"); + if (!manifest.isAccessible()) { + manifest = project.getFile("META-INF/MANIFEST.MF"); + } + + if (manifest.isAccessible()) { + result.put(manifest, pom); + } + } + }); + + return result; + } + + String slurpText(IFile file) throws CoreException { + StringBuilder result = new StringBuilder(); + CharBuffer buf = CharBuffer.allocate(4096); + + try (BufferedReader reader = new BufferedReader(new InputStreamReader(file.getContents(), Charset.forName(file.getCharset())))) { + for (;;) { + if (reader.read(buf) < 0) { + break; // Done + } + buf.flip(); + result.append(buf); + buf.rewind(); + } + } catch (IOException e) { + throw new CoreException(new Status(IStatus.ERROR, Activator.PLUGIN_ID, "Failed to read manifest", e)); + } + + return result.toString(); + } + + Document slurpXML(IFile xmlFile) throws CoreException { + try { + DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder(); + try (InputStream input = xmlFile.getContents()) { + return builder.parse(input); + } + } catch (Exception e) { + throw new CoreException(new Status(IStatus.ERROR, Activator.PLUGIN_ID, "Failed to read manifest file", e)); //$NON-NLS-1$ + } + } + + void write(Document xml, IFile xmlFile) throws CoreException { + try (StringWriter writer = new StringWriter()) { + Transformer transformer = TransformerFactory.newInstance().newTransformer(); + + transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8"); //$NON-NLS-1$ + xml.setXmlStandalone(true); + xml.setXmlVersion("1.0"); + + Result result = new StreamResult(writer); + transformer.transform(new DOMSource(xml), result); + String text = writer.toString(); + + // Fix up the transformer's sometime failure to put a newline after the XML declaration + Matcher fixup = POM_FIXUP.matcher(text); + if (fixup.find()) { + StringBuffer fixed = new StringBuffer(text.length() + 2); + fixup.appendReplacement(fixed, fixup.group(1) + '\n' + fixup.group(2)); + fixup.appendTail(fixed); + text = fixed.toString(); + } + + // Write it out + ByteArrayInputStream input = new ByteArrayInputStream(text.getBytes(Charset.forName("UTF-8"))); //$NON-NLS-1$ + xmlFile.setContents(input, false, true, null); + } catch (Exception e) { + throw new CoreException(new Status(IStatus.ERROR, Activator.PLUGIN_ID, "Failed to update POM", e)); + } + } + + String getVersion(IFile manifest) throws CoreException { + String result = null; + + switch (manifest.getName()) { + case "MANIFEST.MF": //$NON-NLS-1$ + String text = slurpText(manifest); + Matcher m = BUNDLE_VERSION.matcher(text); + if (m.find()) { + result = m.group(1); + } + break; + case "feature.xml": //$NON-NLS-1$ + Document xml = slurpXML(manifest); + result = xml.getDocumentElement().getAttribute("version"); //$NON-NLS-1$ + } + + return result; + } + + Element findVersion(Document pom) { + Element project = pom.getDocumentElement(); + return stream(project.getChildNodes()) + .filter(n -> "version".equals(n.getNodeName())) + .filter(Element.class::isInstance).map(Element.class::cast) + .findFirst().orElse(null); + } + + Stream<Node> stream(NodeList nodes) { + Stream.Builder<Node> result = Stream.builder(); + for (int i = 0; i < nodes.getLength(); i++) { + result.add(nodes.item(i)); + } + return result.build(); + } + + boolean hasPluginOrFeatureNature(IProject project) { + boolean result = false; + + if (project.isAccessible()) { + try { + IProjectDescription desc = project.getDescription(); + List<String> natures = Arrays.asList(desc.getNatureIds()); + result = natures.contains("org.eclipse.pde.PluginNature") //$NON-NLS-1$ + || natures.contains("org.eclipse.pde.FeatureNature"); //$NON-NLS-1$ + } catch (CoreException e) { + // Guess it's not an interesting project + Activator.log.log(e.getStatus()); + } + } + + return result; + } +} |