Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAnton Tanasenko2015-01-24 14:30:01 -0500
committerAnton Tanasenko2015-01-28 16:10:17 -0500
commite84152165805547b1fad2dbc775da711bd169383 (patch)
tree16da124eab5b33f61bd5651aacb09371d6f5c5bf /org.eclipse.m2e.editor.xml
parented44306cf76f1ae0d89a82edaf1b893c4c479498 (diff)
downloadm2e-core-e84152165805547b1fad2dbc775da711bd169383.tar.gz
m2e-core-e84152165805547b1fad2dbc775da711bd169383.tar.xz
m2e-core-e84152165805547b1fad2dbc775da711bd169383.zip
442560 Plugin configuration content assist
Change-Id: I4ab03c3da77c5b4c474ec96ac44e50e751e7860e Signed-off-by: Anton Tanasenko <atg.sleepless@gmail.com>
Diffstat (limited to 'org.eclipse.m2e.editor.xml')
-rw-r--r--org.eclipse.m2e.editor.xml/META-INF/MANIFEST.MF1
-rw-r--r--org.eclipse.m2e.editor.xml/src/main/java/org/eclipse/m2e/editor/xml/PomContentAssistProcessor.java44
-rw-r--r--org.eclipse.m2e.editor.xml/src/main/java/org/eclipse/m2e/editor/xml/PomTemplateContext.java202
-rw-r--r--org.eclipse.m2e.editor.xml/src/main/java/org/eclipse/m2e/editor/xml/internal/Messages.java4
-rw-r--r--org.eclipse.m2e.editor.xml/src/main/java/org/eclipse/m2e/editor/xml/internal/messages.properties2
-rw-r--r--org.eclipse.m2e.editor.xml/src/main/java/org/eclipse/m2e/editor/xml/internal/mojo/MojoParameter.java162
-rw-r--r--org.eclipse.m2e.editor.xml/src/main/java/org/eclipse/m2e/editor/xml/internal/mojo/MojoParameterMetadataProvider.java568
7 files changed, 932 insertions, 51 deletions
diff --git a/org.eclipse.m2e.editor.xml/META-INF/MANIFEST.MF b/org.eclipse.m2e.editor.xml/META-INF/MANIFEST.MF
index bdc13978..b16b9efb 100644
--- a/org.eclipse.m2e.editor.xml/META-INF/MANIFEST.MF
+++ b/org.eclipse.m2e.editor.xml/META-INF/MANIFEST.MF
@@ -25,6 +25,7 @@ Require-Bundle: org.eclipse.core.runtime,
Export-Package: org.eclipse.m2e.editor.xml;x-internal:=true,
org.eclipse.m2e.editor.xml.internal;x-internal:=true,
org.eclipse.m2e.editor.xml.internal.lifecycle;x-internal:=true,
+ org.eclipse.m2e.editor.xml.internal.mojo;x-internal:=true,
org.eclipse.m2e.editor.xml.preferences;x-internal:=true
Bundle-ClassPath: .
Bundle-Vendor: %Bundle-Vendor
diff --git a/org.eclipse.m2e.editor.xml/src/main/java/org/eclipse/m2e/editor/xml/PomContentAssistProcessor.java b/org.eclipse.m2e.editor.xml/src/main/java/org/eclipse/m2e/editor/xml/PomContentAssistProcessor.java
index dc97394c..9957ab03 100644
--- a/org.eclipse.m2e.editor.xml/src/main/java/org/eclipse/m2e/editor/xml/PomContentAssistProcessor.java
+++ b/org.eclipse.m2e.editor.xml/src/main/java/org/eclipse/m2e/editor/xml/PomContentAssistProcessor.java
@@ -50,6 +50,7 @@ import org.eclipse.ui.ISharedImages;
import org.eclipse.ui.PlatformUI;
import org.eclipse.wst.sse.core.internal.provisional.IndexedRegion;
import org.eclipse.wst.sse.core.utils.StringUtils;
+import org.eclipse.wst.xml.core.internal.regions.DOMRegionContext;
import org.eclipse.wst.xml.ui.internal.contentassist.ContentAssistRequest;
import org.eclipse.wst.xml.ui.internal.contentassist.XMLContentAssistProcessor;
@@ -79,8 +80,20 @@ public class PomContentAssistProcessor extends XMLContentAssistProcessor {
//broken
protected void addTagNameProposals(ContentAssistRequest contentAssistRequest, int childPosition) {
- String currentNodeName = getCurrentNode(contentAssistRequest).getNodeName();
+ Node node = getCurrentNode(contentAssistRequest);
+ String currentNodeName = node.getNodeName();
PomTemplateContext context = PomTemplateContext.fromNodeName(currentNodeName);
+
+ // find an ancestor whose context impl can handle all of its subtree
+ PomTemplateContext ancestorContext = context;
+ while(!ancestorContext.handlesSubtree() && node != null) {
+ ancestorContext = PomTemplateContext.fromNodeName(node.getNodeName());
+ node = node.getParentNode();
+ }
+ if(ancestorContext.handlesSubtree()) {
+ context = ancestorContext;
+ }
+
if(PomTemplateContext.CONFIGURATION == context) {
//this is sort of hack that makes sure the config proposals appear even
// when you type <prefix
@@ -91,27 +104,36 @@ public class PomContentAssistProcessor extends XMLContentAssistProcessor {
addProposals(contentAssistRequest, context, getCurrentNode(contentAssistRequest),
contentAssistRequest.getMatchString());
}
- if(PomTemplateContext.UNKNOWN == context) {
- context = PomTemplateContext.fromNodeName(getCurrentNode(contentAssistRequest).getParentNode().getNodeName());
- if(PomTemplateContext.CONFIGURATION == context) {
- addProposals(contentAssistRequest, context, getCurrentNode(contentAssistRequest).getParentNode(),
- contentAssistRequest.getMatchString());
- }
- }
super.addTagNameProposals(contentAssistRequest, childPosition);
}
@Override
protected void addTagInsertionProposals(ContentAssistRequest contentAssistRequest, int childPosition) {
- String currentNodeName = getCurrentNode(contentAssistRequest).getNodeName();
+ Node node = getCurrentNode(contentAssistRequest);
+ String currentNodeName = node.getNodeName();
+ PomTemplateContext context = PomTemplateContext.fromNodeName(currentNodeName);
- addProposals(contentAssistRequest, PomTemplateContext.fromNodeName(currentNodeName));
+ // find an ancestor whose context impl can handle all of its subtree
+ PomTemplateContext ancestorContext = context;
+ while(!ancestorContext.handlesSubtree() && node != null) {
+ ancestorContext = PomTemplateContext.fromNodeName(node.getNodeName());
+ node = node.getParentNode();
+ }
+ if(ancestorContext.handlesSubtree()) {
+ context = ancestorContext;
+ }
+
+ addProposals(contentAssistRequest, context);
super.addTagInsertionProposals(contentAssistRequest, childPosition);
}
private Node getCurrentNode(ContentAssistRequest contentAssistRequest) {
Node currentNode = contentAssistRequest.getNode();
- if(currentNode instanceof Text) {
+ if(DOMRegionContext.XML_TAG_OPEN.equals(contentAssistRequest.getRegion().getType())) {
+ // when calling content assist just before an opening node, this node is passed in request,
+ // but we need its container
+ currentNode = currentNode.getParentNode();
+ } else if(currentNode instanceof Text) {
currentNode = currentNode.getParentNode();
}
return currentNode;
diff --git a/org.eclipse.m2e.editor.xml/src/main/java/org/eclipse/m2e/editor/xml/PomTemplateContext.java b/org.eclipse.m2e.editor.xml/src/main/java/org/eclipse/m2e/editor/xml/PomTemplateContext.java
index 9741b2f1..891bb878 100644
--- a/org.eclipse.m2e.editor.xml/src/main/java/org/eclipse/m2e/editor/xml/PomTemplateContext.java
+++ b/org.eclipse.m2e.editor.xml/src/main/java/org/eclipse/m2e/editor/xml/PomTemplateContext.java
@@ -13,6 +13,7 @@ package org.eclipse.m2e.editor.xml;
import java.io.File;
import java.io.FileFilter;
+import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
@@ -20,6 +21,7 @@ import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Properties;
+import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -30,8 +32,13 @@ import org.w3c.dom.NodeList;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.jface.text.templates.Template;
import org.eclipse.osgi.util.NLS;
+import org.eclipse.ui.PlatformUI;
import org.codehaus.plexus.interpolation.InterpolationException;
import org.codehaus.plexus.interpolation.PrefixedObjectValueSource;
@@ -44,7 +51,6 @@ import org.apache.maven.model.DependencyManagement;
import org.apache.maven.model.Plugin;
import org.apache.maven.model.PluginManagement;
import org.apache.maven.plugin.descriptor.MojoDescriptor;
-import org.apache.maven.plugin.descriptor.Parameter;
import org.apache.maven.plugin.descriptor.PluginDescriptor;
import org.apache.maven.project.MavenProject;
@@ -54,6 +60,8 @@ import org.eclipse.m2e.core.ui.internal.search.util.Packaging;
import org.eclipse.m2e.core.ui.internal.search.util.SearchEngine;
import org.eclipse.m2e.editor.xml.internal.Messages;
import org.eclipse.m2e.editor.xml.internal.XmlUtils;
+import org.eclipse.m2e.editor.xml.internal.mojo.MojoParameter;
+import org.eclipse.m2e.editor.xml.internal.mojo.MojoParameterMetadataProvider;
/**
@@ -102,60 +110,163 @@ public enum PomTemplateContext {
CONFIGURATION("configuration") { //$NON-NLS-1$
+ public boolean handlesSubtree() {
+ return true;
+ }
+
@Override
protected void addTemplates(MavenProject project, IProject eclipseprj, Collection<Template> proposals, Node node,
String prefix) throws CoreException {
- if("execution".equals(node.getParentNode().getNodeName()) //$NON-NLS-1$
- || "reportSet".equals(node.getParentNode().getNodeName())) { //$NON-NLS-1$
- node = node.getParentNode().getParentNode();
+ // find configuration ancestor
+
+ List<String> pathElements = new ArrayList<String>();
+ String configImpl = null;
+
+ Element configNode = (Element) node;
+ while(configNode != null && !configNode.getNodeName().equals(getNodeName())) {
+
+ String impl = configNode.getAttribute("implementation");
+ if(impl != null && !impl.trim().isEmpty()) {
+ configImpl = impl;
+ }
+
+ if(configImpl == null) {
+ pathElements.add(configNode.getNodeName());
+ }
+ configNode = (Element) configNode.getParentNode();
}
- String groupId = getGroupId(node);
+ if(configNode == null) {
+ return;
+ }
+
+ Collections.reverse(pathElements);
+ String[] configPath = pathElements.toArray(new String[pathElements.size()]);
+
+ Node configContainer = null;
+ Node pluginSubNode = configNode;
+ String containerName = pluginSubNode.getParentNode().getNodeName();
+ if("execution".equals(containerName) //$NON-NLS-1$
+ || "reportSet".equals(containerName)) { //$NON-NLS-1$
+ configContainer = pluginSubNode.getParentNode();
+ pluginSubNode = configContainer.getParentNode();
+ }
+ String groupId = getGroupId(pluginSubNode);
if(groupId == null) {
groupId = "org.apache.maven.plugins"; // TODO support other default groups //$NON-NLS-1$
}
- String artifactId = getArtifactId(node);
- String version = extractVersion(project, eclipseprj, getVersion(node), groupId, artifactId,
+ String artifactId = getArtifactId(pluginSubNode);
+ String version = extractVersion(project, eclipseprj, getVersion(pluginSubNode), groupId, artifactId,
EXTRACT_STRATEGY_PLUGIN | EXTRACT_STRATEGY_SEARCH);
if(version == null) {
return;
}
- PluginDescriptor descriptor = PomTemplateContextUtil.INSTANCE.getPluginDescriptor(groupId, artifactId, version);
- if(descriptor != null) {
- List<MojoDescriptor> mojos = descriptor.getMojos();
- HashSet<String> params = new HashSet<String>();
- for(MojoDescriptor mojo : mojos) {
- List<Parameter> parameters = (List<Parameter>) mojo.getParameters();
- if(parameters == null || parameters.isEmpty()) {
- continue;
+
+ // collect used mojo goals
+ final Set<String> usedMojos = new HashSet<>();
+ if("execution".equals(containerName)) { //$NON-NLS-1$
+ Node goalsNode = getChildWithName(configContainer, "goals"); //$NON-NLS-1$
+ if(goalsNode != null) {
+
+ NodeList children = goalsNode.getChildNodes();
+ int l = children.getLength();
+ for(int i = 0; i < l; i++ ) {
+ Node goalNode = children.item(i);
+ if("goal".equals(goalNode.getNodeName())) { //$NON-NLS-1$
+ String goal = XmlUtils.getTextValue(goalNode);
+ if(goal != null && !goal.isEmpty()) {
+ usedMojos.add(goal);
+ }
+ }
}
- for(Parameter parameter : parameters) {
- boolean editable = parameter.isEditable();
- if(editable) {
- String name = parameter.getName();
- if(!params.contains(name) && name.startsWith(prefix)) {
- params.add(name);
-
- String text = NLS.bind(Messages.PomTemplateContext_param, parameter.isRequired(), parameter.getType());
-
- String expression = parameter.getExpression();
- if(expression != null) {
- text += NLS.bind(Messages.PomTemplateContext_param_expr, expression);
- }
-
- String defaultValue = parameter.getDefaultValue();
- if(defaultValue != null) {
- text += NLS.bind(Messages.PomTemplateContext_param_def, defaultValue);
- }
-
- String desc = parameter.getDescription().trim();
- text += desc.startsWith("<p>") ? desc : "<br>" + desc; //$NON-NLS-1$ //$NON-NLS-2$
-
- proposals.add(new Template(name, text, getContextTypeId(), //
- "<" + name + ">${cursor}</" + name + ">", false)); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ }
+ }
+
+ Plugin plugin = new Plugin();
+ plugin.setGroupId(groupId);
+ plugin.setArtifactId(artifactId);
+ plugin.setVersion(version);
+
+ final String fConfigImpl = configImpl;
+ final MojoParameter[] parameterMetadata = new MojoParameter[1];
+ final CoreException[] innerException = new CoreException[1];
+
+ final MojoParameterMetadataProvider prov = new MojoParameterMetadataProvider(project, plugin);
+ try {
+ PlatformUI.getWorkbench().getActiveWorkbenchWindow().run(true, true, new IRunnableWithProgress() {
+ public void run(IProgressMonitor monitor) {
+ monitor.beginTask(Messages.PomTemplateContext_resolvingPlugin, 100);
+ try {
+ MojoParameter parameter;
+ if(fConfigImpl != null) {
+ parameter = prov.getParameterRoot(fConfigImpl, monitor);
+ } else if(usedMojos.isEmpty()) {
+ parameter = prov.getMojoParameterRoot(monitor);
+ } else {
+ parameter = prov.getMojoParameterRoot(usedMojos, monitor);
}
+
+ if(monitor.isCanceled())
+ return;
+ parameterMetadata[0] = parameter;
+ } catch(CoreException ex) {
+ if(monitor.isCanceled())
+ return;
+ innerException[0] = ex;
}
+
}
+ });
+ } catch(InvocationTargetException | InterruptedException ex) {
+ throw new CoreException(new Status(IStatus.ERROR, MvnIndexPlugin.PLUGIN_ID, ex.getMessage(), ex));
+ }
+ if(innerException[0] != null) {
+ throw innerException[0];
+ }
+
+ if(parameterMetadata[0] == null) {
+ return;
+ }
+
+ MojoParameter param = parameterMetadata[0].getContainer(configPath);
+
+ if(param != null) {
+ for(MojoParameter parameter : param.getNestedParameters()) {
+ String name = parameter.getName();
+ if(name.startsWith(prefix)) {
+
+ String text = NLS.bind(Messages.PomTemplateContext_param, parameter.isRequired(), parameter.getType());
+
+ String expression = parameter.getExpression();
+ if(expression != null) {
+ text += NLS.bind(Messages.PomTemplateContext_param_expr, expression);
+ }
+
+ String defaultValue = parameter.getDefaultValue();
+ if(defaultValue != null) {
+ text += NLS.bind(Messages.PomTemplateContext_param_def, defaultValue);
+ }
+
+ String description = parameter.getDescription();
+ if(description != null) {
+ String desc = description.trim();
+ text += desc.startsWith("<p>") ? desc : "<br>" + desc; //$NON-NLS-1$ //$NON-NLS-2$
+ }
+
+ proposals.add(new Template(name, text, getContextTypeId(), //
+ "<" + name + ">${cursor}</" + name + ">", false)); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ }
+ }
+
+ if(param.isMap()) {
+
+ if(prefix != null && !prefix.trim().isEmpty()) {
+ proposals.add(new Template(NLS.bind(Messages.PomTemplateContext_insertParameter, prefix),
+ "", getContextTypeId(), "<" + prefix + ">${cursor}</" + prefix + ">", true)); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
+
+ }
+
}
+
}
}
},
@@ -509,6 +620,10 @@ public enum PomTemplateContext {
this.nodeName = nodeName;
}
+ public boolean handlesSubtree() {
+ return false;
+ }
+
/**
* Return templates depending on the context type.
*/
@@ -748,7 +863,14 @@ public enum PomTemplateContext {
* Returns sibling with given name.
*/
private static Node getSiblingWithName(Node node, String name) {
- NodeList nodeList = node.getParentNode().getChildNodes();
+ return getChildWithName(node.getParentNode(), name);
+ }
+
+ /**
+ * Returns child with given name
+ */
+ private static Node getChildWithName(Node node, String name) {
+ NodeList nodeList = node.getChildNodes();
for(int i = 0; i < nodeList.getLength(); i++ ) {
if(name.equals(nodeList.item(i).getNodeName())) {
return nodeList.item(i);
diff --git a/org.eclipse.m2e.editor.xml/src/main/java/org/eclipse/m2e/editor/xml/internal/Messages.java b/org.eclipse.m2e.editor.xml/src/main/java/org/eclipse/m2e/editor/xml/internal/Messages.java
index d8be49b5..8c2339d2 100644
--- a/org.eclipse.m2e.editor.xml/src/main/java/org/eclipse/m2e/editor/xml/internal/Messages.java
+++ b/org.eclipse.m2e.editor.xml/src/main/java/org/eclipse/m2e/editor/xml/internal/Messages.java
@@ -107,6 +107,10 @@ public class Messages extends NLS {
public static String PomTemplateContext_integrationtest;
+ public static String PomTemplateContext_insertParameter;
+
+ public static String PomTemplateContext_resolvingPlugin;
+
public static String PomTemplateContext_package;
public static String PomTemplateContext_param;
diff --git a/org.eclipse.m2e.editor.xml/src/main/java/org/eclipse/m2e/editor/xml/internal/messages.properties b/org.eclipse.m2e.editor.xml/src/main/java/org/eclipse/m2e/editor/xml/internal/messages.properties
index 3711e2b4..38476dd5 100644
--- a/org.eclipse.m2e.editor.xml/src/main/java/org/eclipse/m2e/editor/xml/internal/messages.properties
+++ b/org.eclipse.m2e.editor.xml/src/main/java/org/eclipse/m2e/editor/xml/internal/messages.properties
@@ -43,6 +43,7 @@ PomTemplateContext_generatetestresources=Create resources for testing
PomTemplateContext_generatetestsources=Generate any test source code for inclusion in compilation
PomTemplateContext_install=Install the package into the local repository, for use as a dependency in other projects locally
PomTemplateContext_integrationtest=Process and deploy the package if necessary into an environment where integration tests can be run
+PomTemplateContext_insertParameter=Insert parameter {0}
PomTemplateContext_package=Take the compiled code and package it in its distributable format, such as a JAR
PomTemplateContext_param=<b>required:</b> {0}<br><b>type:</b> {1}<br>
PomTemplateContext_param_def=default: {0}<br>
@@ -61,6 +62,7 @@ PomTemplateContext_processtestclasses=Post-process the generated files from test
PomTemplateContext_processtestresources=Copy and process the resources into the test destination directory
PomTemplateContext_processtestsources=Process the test source code, for example to filter any values
PomTemplateContext_project_version_hint=For projects developed in sync only.
+PomTemplateContext_resolvingPlugin=Resolving plugin
PomTemplateContext_site=Generates the project's site documentation
PomTemplateContext_sitedeploy=Deploys the generated site documentation to the specified web server
PomTemplateContext_test=Run tests using a suitable unit testing framework. These tests should not require the code be packaged or deployed
diff --git a/org.eclipse.m2e.editor.xml/src/main/java/org/eclipse/m2e/editor/xml/internal/mojo/MojoParameter.java b/org.eclipse.m2e.editor.xml/src/main/java/org/eclipse/m2e/editor/xml/internal/mojo/MojoParameter.java
new file mode 100644
index 00000000..57814148
--- /dev/null
+++ b/org.eclipse.m2e.editor.xml/src/main/java/org/eclipse/m2e/editor/xml/internal/mojo/MojoParameter.java
@@ -0,0 +1,162 @@
+/*******************************************************************************
+ * Copyright (c) 2015 Takari, Inc.
+ * 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:
+ * Anton Tanasenko. - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.m2e.editor.xml.internal.mojo;
+
+import java.util.Collections;
+import java.util.List;
+
+
+/**
+ * @author atanasenko
+ */
+public class MojoParameter {
+
+ private String name;
+
+ private String type;
+
+ private boolean required;
+
+ private String description;
+
+ private String expression;
+
+ private String defaultValue;
+
+ private List<MojoParameter> nested;
+
+ private boolean multiple;
+
+ private boolean map;
+
+ public MojoParameter(String name, String type, List<MojoParameter> parameters) {
+ this.name = name;
+ this.type = type;
+ nested = parameters;
+ }
+
+ public MojoParameter(String name, String type, MojoParameter parameter) {
+ this(name, type, Collections.singletonList(parameter));
+ }
+
+ protected MojoParameter(String name, String type) {
+ this(name, type, Collections.<MojoParameter> emptyList());
+ }
+
+ MojoParameter multiple() {
+ this.multiple = true;
+ return this;
+ }
+
+ MojoParameter map() {
+ this.map = true;
+ return this;
+ }
+
+ public boolean isMultiple() {
+ return multiple;
+ }
+
+ public boolean isMap() {
+ return this.map;
+ }
+
+ public List<MojoParameter> getNestedParameters() {
+ return Collections.unmodifiableList(nested);
+ }
+
+ void setNestedParameters(List<MojoParameter> nested) {
+ this.nested = nested;
+ }
+
+ public String getName() {
+ return this.name;
+ }
+
+ public String getType() {
+ return this.type;
+ }
+
+ public boolean isRequired() {
+ return this.required;
+ }
+
+ public void setRequired(boolean required) {
+ this.required = required;
+ }
+
+ public String getDescription() {
+ return this.description;
+ }
+
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+ public String getExpression() {
+ return this.expression;
+ }
+
+ public void setExpression(String expression) {
+ this.expression = expression;
+ }
+
+ public String getDefaultValue() {
+ return this.defaultValue;
+ }
+
+ public void setDefaultValue(String defaultValue) {
+ this.defaultValue = defaultValue;
+ }
+
+ public String toString() {
+ return name + "{" + type + "}"; //$NON-NLS-1$ //$NON-NLS-2$
+ }
+
+ public MojoParameter getNestedParameter(String name) {
+
+ List<MojoParameter> params = getNestedParameters();
+ if(params.size() == 1) {
+ MojoParameter param = params.get(0);
+ if(param.isMultiple()) {
+ return param;
+ }
+ }
+
+ for(MojoParameter p : params) {
+ if(p.getName().equals(name)) {
+ return p;
+ }
+ }
+ return null;
+ }
+
+ public MojoParameter getContainer(String[] path) {
+
+ if(path == null || path.length == 0) {
+ return this;
+ }
+
+ MojoParameter param = this;
+ int i = 0;
+ while(param != null && i < path.length) {
+ param = param.getNestedParameter(path[i]);
+ i++ ;
+ }
+
+ if(param == null) {
+ return null;
+ }
+
+ return param;
+ }
+}
diff --git a/org.eclipse.m2e.editor.xml/src/main/java/org/eclipse/m2e/editor/xml/internal/mojo/MojoParameterMetadataProvider.java b/org.eclipse.m2e.editor.xml/src/main/java/org/eclipse/m2e/editor/xml/internal/mojo/MojoParameterMetadataProvider.java
new file mode 100644
index 00000000..24ee5cf5
--- /dev/null
+++ b/org.eclipse.m2e.editor.xml/src/main/java/org/eclipse/m2e/editor/xml/internal/mojo/MojoParameterMetadataProvider.java
@@ -0,0 +1,568 @@
+/*******************************************************************************
+ * Copyright (c) 2015 Takari, Inc.
+ * 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:
+ * Anton Tanasenko - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.m2e.editor.xml.internal.mojo;
+
+import java.beans.IntrospectionException;
+import java.beans.Introspector;
+import java.beans.PropertyDescriptor;
+import java.io.File;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.net.URI;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+
+import org.eclipse.aether.repository.RemoteRepository;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+
+import org.codehaus.plexus.component.repository.exception.ComponentLookupException;
+
+import org.apache.maven.DefaultMaven;
+import org.apache.maven.model.Plugin;
+import org.apache.maven.model.building.ModelSource;
+import org.apache.maven.model.building.UrlModelSource;
+import org.apache.maven.plugin.BuildPluginManager;
+import org.apache.maven.plugin.InvalidPluginDescriptorException;
+import org.apache.maven.plugin.MavenPluginManager;
+import org.apache.maven.plugin.PluginDescriptorParsingException;
+import org.apache.maven.plugin.PluginManagerException;
+import org.apache.maven.plugin.PluginResolutionException;
+import org.apache.maven.plugin.descriptor.MojoDescriptor;
+import org.apache.maven.plugin.descriptor.Parameter;
+import org.apache.maven.plugin.descriptor.PluginDescriptor;
+import org.apache.maven.project.MavenProject;
+import org.apache.maven.project.ProjectBuilder;
+import org.apache.maven.project.ProjectBuildingException;
+
+import org.eclipse.m2e.core.MavenPlugin;
+import org.eclipse.m2e.core.embedder.ICallable;
+import org.eclipse.m2e.core.embedder.IMavenExecutionContext;
+import org.eclipse.m2e.core.internal.IMavenConstants;
+import org.eclipse.m2e.core.internal.Messages;
+import org.eclipse.m2e.core.internal.embedder.MavenExecutionContext;
+import org.eclipse.m2e.core.internal.embedder.MavenImpl;
+import org.eclipse.m2e.editor.xml.MvnIndexPlugin;
+
+
+/**
+ * @author atanasenko
+ */
+@SuppressWarnings("restriction")
+public class MojoParameterMetadataProvider {
+
+ private final MavenProject project;
+
+ private final Plugin plugin;
+
+ protected final MavenImpl maven;
+
+ private final Map<String, List<MojoParameter>> parameters;
+
+ public MojoParameterMetadataProvider(MavenProject project, Plugin plugin) {
+ this.project = project;
+ this.plugin = plugin;
+ maven = (MavenImpl) MavenPlugin.getMaven();
+ parameters = new HashMap<>();
+ }
+
+ public MojoParameter getParameterRoot(final String className, IProgressMonitor monitor) throws CoreException {
+
+ List<MojoParameter> plist = parameters.get(className);
+ if(plist == null) {
+ plist = new ArrayList<>();
+ this.parameters.put(className, plist);
+ final List<MojoParameter> parameters = plist;
+
+ MavenExecutionContext context = maven.createExecutionContext();
+ context.getExecutionRequest().setCacheTransferError(false);
+ context.execute(new ICallable<Void>() {
+ public Void call(IMavenExecutionContext context, IProgressMonitor monitor) throws CoreException {
+
+ return context.execute(getProject(context), new ICallable<Void>() {
+ public Void call(IMavenExecutionContext context, IProgressMonitor monitor) throws CoreException {
+ PluginDescriptor pd = getPluginDescriptor(context, monitor);
+
+ Class<?> clazz;
+ try {
+ clazz = pd.getClassRealm().loadClass(className);
+ } catch(ClassNotFoundException ex) {
+ return null;
+ }
+
+ loadParameters(pd, clazz, parameters, monitor);
+ return null;
+ }
+ }, monitor);
+
+ }
+ }, monitor);
+ }
+
+ return new MojoParameter("", className, plist); //$NON-NLS-1$
+ }
+
+ protected List<MojoParameter> getParameters(PluginDescriptor desc, final Class<?> clazz, IProgressMonitor monitor)
+ throws CoreException {
+
+ String key = clazz.getName();
+ List<MojoParameter> plist = parameters.get(key);
+
+ if(plist == null) {
+ plist = new ArrayList<>();
+ this.parameters.put(key, plist);
+
+ loadParameters(desc, clazz, plist, monitor);
+ }
+
+ return plist;
+ }
+
+ public MojoParameter getMojoParameterRoot(final Collection<String> mojos, IProgressMonitor monitor)
+ throws CoreException {
+ List<MojoParameter> params = new ArrayList<>();
+ Set<String> collected = new HashSet<>();
+ for(String mojo : mojos) {
+ MojoParameter md = getMojoParameterRoot(mojo, monitor);
+ for(MojoParameter p : md.getNestedParameters()) {
+ if(!collected.add(p.getName()))
+ continue;
+ params.add(p);
+ }
+ }
+ return new MojoParameter("", "", params); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+
+ public MojoParameter getMojoParameterRoot(IProgressMonitor monitor) throws CoreException {
+ return getMojoParameterRoot("*", monitor); //$NON-NLS-1$
+ }
+
+ public MojoParameter getMojoParameterRoot(final String mojo, IProgressMonitor monitor) throws CoreException {
+
+ String key = "mojo:" + (mojo == null ? "*" : mojo); //$NON-NLS-1$ //$NON-NLS-2$
+
+ List<MojoParameter> plist = parameters.get(key);
+
+ if(plist == null) {
+
+ final List<MojoParameter> parameters = new ArrayList<>();
+ plist = parameters;
+ this.parameters.put(key, plist);
+
+ MavenExecutionContext context = maven.createExecutionContext();
+ context.getExecutionRequest().setCacheTransferError(false);
+ context.execute(new ICallable<Void>() {
+ public Void call(IMavenExecutionContext context, IProgressMonitor monitor) throws CoreException {
+ return context.execute(getProject(context), new ICallable<Void>() {
+ public Void call(IMavenExecutionContext context, IProgressMonitor monitor) throws CoreException {
+ loadMojoParameters(getPluginDescriptor(context, monitor), mojo, parameters, monitor);
+ return null;
+ }
+ }, monitor);
+
+ }
+ }, monitor);
+ }
+
+ return new MojoParameter("", mojo, plist); //$NON-NLS-1$
+ }
+
+ void loadMojoParameters(PluginDescriptor desc, String goal, List<MojoParameter> parameters, IProgressMonitor monitor)
+ throws CoreException {
+
+ Set<String> collected = new HashSet<>();
+
+ if(goal.equals("*")) { //$NON-NLS-1$
+ for(MojoDescriptor mojo : desc.getMojos()) {
+ loadMojoParameters(desc, mojo, parameters, collected, monitor);
+ }
+ return;
+ }
+
+ MojoDescriptor mojo = desc.getMojo(goal);
+ loadMojoParameters(desc, mojo, parameters, collected, monitor);
+ }
+
+ protected void loadMojoParameters(PluginDescriptor desc, MojoDescriptor mojo, List<MojoParameter> parameters,
+ Set<String> collected, IProgressMonitor monitor) throws CoreException {
+
+ if(monitor.isCanceled()) {
+ return;
+ }
+ Class<?> clazz;
+ try {
+ clazz = mojo.getImplementationClass();
+ if(clazz == null) {
+ clazz = desc.getClassRealm().loadClass(mojo.getImplementation());
+ }
+ } catch(ClassNotFoundException | TypeNotPresentException ex) {
+ throw new CoreException(new Status(IStatus.ERROR, IMavenConstants.PLUGIN_ID, -1, ex.getMessage(), ex));
+ }
+ List<Parameter> ps = mojo.getParameters();
+ if(ps != null) {
+ for(Parameter p : ps) {
+ if(monitor.isCanceled()) {
+ return;
+ }
+ if(!p.isEditable()) {
+ continue;
+ }
+ if(collected.add(p.getName())) {
+ addParameter(desc, getType(clazz, p.getName()), p.getName(), p.getAlias(), parameters, p.isRequired(),
+ p.getExpression(), p.getDescription(), p.getDefaultValue(), monitor);
+ }
+ }
+ }
+ }
+
+ protected void loadParameters(PluginDescriptor desc, Class<?> clazz, List<MojoParameter> parameters,
+ IProgressMonitor monitor) throws CoreException {
+ if(monitor.isCanceled()) {
+ return;
+ }
+ PropertyDescriptor[] propertyDescriptors;
+ try {
+ propertyDescriptors = Introspector.getBeanInfo(clazz).getPropertyDescriptors();
+ } catch(IntrospectionException ex) {
+ throw new CoreException(new Status(IStatus.ERROR, IMavenConstants.PLUGIN_ID, -1, ex.getMessage(), ex));
+ }
+
+ for(PropertyDescriptor pd : propertyDescriptors) {
+ if(monitor.isCanceled()) {
+ return;
+ }
+ if(pd.getWriteMethod() == null) {
+ continue;
+ }
+ String name = pd.getName();
+ addParameter(desc, getType(clazz, name), name, null, parameters, false, null, null, null, monitor);
+ }
+ }
+
+ protected MavenProject getProject(IMavenExecutionContext context) throws CoreException {
+ if(project != null) {
+ return project;
+ }
+
+ ModelSource modelSource = new UrlModelSource(DefaultMaven.class.getResource("project/standalone.xml")); //$NON-NLS-1$
+ try {
+ return lookup(ProjectBuilder.class).build(modelSource, context.newProjectBuildingRequest()).getProject();
+ } catch(ProjectBuildingException ex) {
+ throw new CoreException(new Status(IStatus.ERROR, IMavenConstants.PLUGIN_ID, -1, ex.getMessage(), ex));
+ }
+ }
+
+ PluginDescriptor getPluginDescriptor(IMavenExecutionContext context, IProgressMonitor monitor) throws CoreException {
+ PluginDescriptor desc;
+
+ List<RemoteRepository> remoteRepos = context.getSession().getCurrentProject().getRemotePluginRepositories();
+ try {
+ desc = lookup(MavenPluginManager.class).getPluginDescriptor(plugin, remoteRepos, context.getRepositorySession());
+ } catch(PluginResolutionException | PluginDescriptorParsingException | InvalidPluginDescriptorException ex) {
+ throw new CoreException(new Status(IStatus.ERROR, MvnIndexPlugin.PLUGIN_ID, ex.getMessage(), ex));
+ }
+
+ try {
+ lookup(BuildPluginManager.class).getPluginRealm(context.getSession(), desc);
+ return desc;
+ } catch(PluginResolutionException | PluginManagerException ex) {
+ throw new CoreException(new Status(IStatus.ERROR, MvnIndexPlugin.PLUGIN_ID, ex.getMessage(), ex));
+ }
+
+ }
+
+ <T> T lookup(Class<T> clazz) throws CoreException {
+ try {
+ return maven.getPlexusContainer().lookup(clazz);
+ } catch(ComponentLookupException ex) {
+ throw new CoreException(new Status(IStatus.ERROR, IMavenConstants.PLUGIN_ID, -1, Messages.MavenImpl_error_lookup,
+ ex));
+ }
+ }
+
+ private void addParameter(PluginDescriptor desc, Type paramType, String name, String alias,
+ List<MojoParameter> parameters, boolean required, String expression, String description, String defaultValue,
+ IProgressMonitor monitor) throws CoreException {
+
+ Class<?> paramClass = getRawType(paramType);
+
+ // inline
+ if(INLINE_TYPES.contains(paramClass)) {
+ parameters.add(configure(new MojoParameter(name, getTypeDisplayName(paramType)), required, expression,
+ description, defaultValue));
+ if(alias != null) {
+ parameters.add(configure(new MojoParameter(alias, getTypeDisplayName(paramType)), required, expression,
+ description, defaultValue));
+ }
+ return;
+ }
+
+ // map
+ if(Map.class.isAssignableFrom(paramClass) || Properties.class.isAssignableFrom(paramClass)) {
+ // we can't do anything with maps, unfortunately
+ parameters.add(configure(new MojoParameter(name, getTypeDisplayName(paramType)).map(), required, expression,
+ description, defaultValue));
+ if(alias != null) {
+ parameters.add(configure(new MojoParameter(alias, getTypeDisplayName(paramType)).map(), required, expression,
+ description, defaultValue));
+ }
+ return;
+ }
+
+ // collection/array
+ Type itemType = getItemType(paramType);
+
+ if(itemType != null) {
+ MojoParameter inner = new MojoParameter(toSingular(name), getTypeDisplayName(itemType)).multiple();
+ getItemParameters(desc, name, itemType, inner, monitor);
+
+ parameters.add(configure(new MojoParameter(name, getTypeDisplayName(paramType), inner), required, expression,
+ description, defaultValue));
+
+ if(alias != null) {
+ inner = new MojoParameter(toSingular(alias), getTypeDisplayName(itemType)).multiple();
+ getItemParameters(desc, alias, itemType, inner, monitor);
+ parameters.add(configure(new MojoParameter(alias, getTypeDisplayName(paramType), inner), required, expression,
+ description, defaultValue));
+ }
+
+ return;
+ }
+
+ // pojo
+ List<MojoParameter> params = getParameters(desc, paramClass, monitor);
+ parameters.add(configure(new MojoParameter(name, getTypeDisplayName(paramType), params), required, expression,
+ description, defaultValue));
+ if(alias != null) {
+ parameters.add(configure(new MojoParameter(alias, getTypeDisplayName(paramType), params), required, expression,
+ description, defaultValue));
+ }
+
+ }
+
+ private void getItemParameters(PluginDescriptor desc, String name, Type paramType, MojoParameter container,
+ IProgressMonitor monitor) throws CoreException {
+
+ Class<?> paramClass = getRawType(paramType);
+
+ if(INLINE_TYPES.contains(paramClass)) {
+ return;
+ }
+
+ if(Map.class.isAssignableFrom(paramClass) || Properties.class.isAssignableFrom(paramClass)) {
+ container.map();
+ return;
+ }
+
+ Type itemType = getItemType(paramType);
+
+ if(itemType != null) {
+ MojoParameter inner = new MojoParameter(toSingular(name), getTypeDisplayName(paramType)).multiple();
+ getItemParameters(desc, name, itemType, inner, monitor);
+ container.setNestedParameters(Collections.singletonList(inner));
+ return;
+ }
+
+ container.setNestedParameters(getParameters(desc, paramClass, monitor));
+ }
+
+ private static MojoParameter configure(MojoParameter p, boolean required, String expression, String description,
+ String defaultValue) {
+ p.setRequired(required);
+ p.setExpression(expression);
+ p.setDescription(description);
+ p.setDefaultValue(defaultValue);
+ return p;
+ }
+
+ private static Class<?> getRawType(Type type) {
+ if(type instanceof Class) {
+ return (Class<?>) type;
+ }
+ if(type instanceof ParameterizedType) {
+ return (Class<?>) ((ParameterizedType) type).getRawType();
+ }
+ return null;
+ }
+
+ private static Type getItemType(Type paramType) {
+
+ Class<?> paramClass = getRawType(paramType);
+
+ if(paramClass.isArray()) {
+ return paramClass.getComponentType();
+ }
+ if(!Collection.class.isAssignableFrom(paramClass)) {
+ return null;
+ }
+
+ if(paramType instanceof ParameterizedType) {
+ ParameterizedType pt = (ParameterizedType) paramType;
+ paramClass = (Class<?>) pt.getRawType();
+
+ Type[] args = pt.getActualTypeArguments();
+ if(args.length > 0) {
+ return args[0];
+ }
+ }
+
+ return null;
+ }
+
+ private static Type getType(Class<?> clazz, String property) {
+
+ String title = Character.toTitleCase(property.charAt(0)) + property.substring(1);
+
+ Method setter = findMethod(clazz, "set" + title); //$NON-NLS-1$
+ if(setter == null) {
+ setter = findMethod(clazz, "add" + title); //$NON-NLS-1$
+ }
+
+ if(setter != null) {
+ Type[] paramTypes = setter.getGenericParameterTypes();
+ if(paramTypes.length > 0) {
+ return paramTypes[0];
+ }
+ }
+
+ Field field = findField(clazz, property);
+ if(field != null) {
+ return field.getGenericType();
+ }
+
+ return null;
+ }
+
+ private static Method findMethod(Class<?> clazz, String name) {
+ while(clazz != null) {
+ for(Method m : clazz.getDeclaredMethods()) {
+ if(Modifier.isPublic(m.getModifiers()) && m.getName().equals(name)) {
+ return m;
+ }
+ }
+ clazz = clazz.getSuperclass();
+ }
+ return null;
+ }
+
+ private static Field findField(Class<?> clazz, String name) {
+ while(clazz != null) {
+ for(Field f : clazz.getDeclaredFields()) {
+ if(f.getName().equals(name)) {
+ return f;
+ }
+ }
+ clazz = clazz.getSuperclass();
+ }
+ return null;
+ }
+
+ private static String getTypeDisplayName(Type type) {
+ Class<?> clazz = getRawType(type);
+
+ if(clazz == null) {
+ return type.toString();
+ }
+
+ if(clazz.isArray()) {
+ return getTypeDisplayName(clazz.getComponentType()) + "[]"; //$NON-NLS-1$
+ }
+
+ if(type instanceof ParameterizedType) {
+ ParameterizedType ptype = (ParameterizedType) type;
+ StringBuilder sb = new StringBuilder();
+ sb.append(getTypeDisplayName(clazz)).append("&lt;"); //$NON-NLS-1$
+
+ boolean first = true;
+ for(Type arg : ptype.getActualTypeArguments()) {
+ if(first)
+ first = false;
+ else
+ sb.append(", "); //$NON-NLS-1$
+ sb.append(getTypeDisplayName(arg));
+ }
+
+ return sb.append("&gt;").toString(); //$NON-NLS-1$
+ }
+
+ String name = clazz.getName();
+ int idx = name.lastIndexOf('.');
+ if(idx == -1) {
+ return name;
+ }
+ // remove common package names
+ String pkg = name.substring(0, idx);
+ if(pkg.equals("java.lang") || pkg.equals("java.util") || pkg.equals("java.io")) { //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ return clazz.getSimpleName();
+ }
+ return name;
+ }
+
+ private static String toSingular(String name) {
+ if(name == null || name.trim().isEmpty()) {
+ return name;
+ }
+ if(name.endsWith("ies")) { //$NON-NLS-1$
+ return name.substring(0, name.length() - 3) + "y"; //$NON-NLS-1$
+ } else if(name.endsWith("ches")) { //$NON-NLS-1$ $NON-NLS-2$
+ return name.substring(0, name.length() - 2);
+ } else if(name.endsWith("xes")) { //$NON-NLS-1$
+ return name.substring(0, name.length() - 2);
+ } else if(name.endsWith("s") && (name.length() != 1)) { //$NON-NLS-1$
+ return name.substring(0, name.length() - 1);
+ }
+ return name;
+ }
+
+ private static final Set<Class<?>> INLINE_TYPES = new HashSet<>();
+ static {
+ INLINE_TYPES.add(byte.class);
+ INLINE_TYPES.add(Byte.class);
+ INLINE_TYPES.add(short.class);
+ INLINE_TYPES.add(Short.class);
+ INLINE_TYPES.add(int.class);
+ INLINE_TYPES.add(Integer.class);
+ INLINE_TYPES.add(long.class);
+ INLINE_TYPES.add(Long.class);
+ INLINE_TYPES.add(float.class);
+ INLINE_TYPES.add(Float.class);
+ INLINE_TYPES.add(double.class);
+ INLINE_TYPES.add(Double.class);
+ INLINE_TYPES.add(boolean.class);
+ INLINE_TYPES.add(Boolean.class);
+ INLINE_TYPES.add(char.class);
+ INLINE_TYPES.add(Character.class);
+
+ INLINE_TYPES.add(String.class);
+ INLINE_TYPES.add(StringBuilder.class);
+ INLINE_TYPES.add(StringBuffer.class);
+
+ INLINE_TYPES.add(File.class);
+ INLINE_TYPES.add(URI.class);
+ INLINE_TYPES.add(URL.class);
+ INLINE_TYPES.add(Date.class);
+ }
+}

Back to the top