diff options
author | Anton Tanasenko | 2015-05-24 12:16:57 +0000 |
---|---|---|
committer | Anton Tanasenko | 2015-05-25 16:19:13 +0000 |
commit | 3e7111c55ca2b5c5ef62d4655dcbac7723ae842d (patch) | |
tree | 9f0986c404b5612aa429ebacb6d1c98d1aeddeaa | |
parent | d9a3f3c9d6954372ba5b7899cfcdee45fa0b43da (diff) | |
download | m2e-core-3e7111c55ca2b5c5ef62d4655dcbac7723ae842d.tar.gz m2e-core-3e7111c55ca2b5c5ef62d4655dcbac7723ae842d.tar.xz m2e-core-3e7111c55ca2b5c5ef62d4655dcbac7723ae842d.zip |
468133 Extension point for pom mojo configuration content assist
Change-Id: Ifd2e3bad27314880e98998a97ed7abd26d648d30
Signed-off-by: Anton Tanasenko <atg.sleepless@gmail.com>
12 files changed, 916 insertions, 566 deletions
diff --git a/org.eclipse.m2e.editor.xml/META-INF/MANIFEST.MF b/org.eclipse.m2e.editor.xml/META-INF/MANIFEST.MF index 1f18d4c3..b2c5d70c 100644 --- a/org.eclipse.m2e.editor.xml/META-INF/MANIFEST.MF +++ b/org.eclipse.m2e.editor.xml/META-INF/MANIFEST.MF @@ -27,6 +27,7 @@ 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.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/plugin.properties b/org.eclipse.m2e.editor.xml/plugin.properties new file mode 100644 index 00000000..1a903c03 --- /dev/null +++ b/org.eclipse.m2e.editor.xml/plugin.properties @@ -0,0 +1 @@ +extension-point.mojoParameterMetadata.name=Mojo Parameter Metadata diff --git a/org.eclipse.m2e.editor.xml/plugin.xml b/org.eclipse.m2e.editor.xml/plugin.xml index 17ecab89..bb01872b 100644 --- a/org.eclipse.m2e.editor.xml/plugin.xml +++ b/org.eclipse.m2e.editor.xml/plugin.xml @@ -1,6 +1,7 @@ <?xml version="1.0" encoding="UTF-8"?> <?eclipse version="3.2"?> <plugin> + <extension-point id="mojoParameterMetadata" name="%extension-point.mojoParameterMetadata.name" schema="schema/mojoParameterMetadata.exsd"/> <extension point="org.eclipse.wst.sse.ui.editorConfiguration"> <sourceViewerConfiguration diff --git a/org.eclipse.m2e.editor.xml/schema/.gitignore b/org.eclipse.m2e.editor.xml/schema/.gitignore deleted file mode 100644 index e69de29b..00000000 --- a/org.eclipse.m2e.editor.xml/schema/.gitignore +++ /dev/null diff --git a/org.eclipse.m2e.editor.xml/schema/mojoParameterMetadata.exsd b/org.eclipse.m2e.editor.xml/schema/mojoParameterMetadata.exsd new file mode 100644 index 00000000..f10eba03 --- /dev/null +++ b/org.eclipse.m2e.editor.xml/schema/mojoParameterMetadata.exsd @@ -0,0 +1,121 @@ +<?xml version='1.0' encoding='UTF-8'?>
+<!-- Schema file written by PDE -->
+<schema targetNamespace="org.eclipse.m2e.editor.xml" xmlns="http://www.w3.org/2001/XMLSchema">
+<annotation>
+ <appInfo>
+ <meta.schema plugin="org.eclipse.m2e.editor.xml" id="mojoParameterMetadata" name="%extension-point.mojoParameterMetadata.name"/>
+ </appInfo>
+ <documentation>
+ Mojo parameter metadata sources provide plugin configuration options that mirror the corresponding component configurator
+ </documentation>
+ </annotation>
+
+ <element name="extension">
+ <annotation>
+ <appInfo>
+ <meta.element />
+ </appInfo>
+ </annotation>
+ <complexType>
+ <sequence minOccurs="1" maxOccurs="unbounded">
+ <element ref="mojoParameterMetadata"/>
+ </sequence>
+ <attribute name="point" type="string" use="required">
+ <annotation>
+ <documentation>
+
+ </documentation>
+ </annotation>
+ </attribute>
+ <attribute name="id" type="string">
+ <annotation>
+ <documentation>
+
+ </documentation>
+ </annotation>
+ </attribute>
+ <attribute name="name" type="string">
+ <annotation>
+ <documentation>
+
+ </documentation>
+ <appInfo>
+ <meta.attribute translatable="true"/>
+ </appInfo>
+ </annotation>
+ </attribute>
+ </complexType>
+ </element>
+
+ <element name="mojoParameterMetadata">
+ <complexType>
+ <attribute name="configurator" type="string" use="required">
+ <annotation>
+ <documentation>
+
+ </documentation>
+ </annotation>
+ </attribute>
+ <attribute name="class" type="string" use="required">
+ <annotation>
+ <documentation>
+
+ </documentation>
+ <appInfo>
+ <meta.attribute kind="java" basedOn=":org.eclipse.m2e.editor.xml.mojo.IMojoParameterMetadata"/>
+ </appInfo>
+ </annotation>
+ </attribute>
+ </complexType>
+ </element>
+
+ <annotation>
+ <appInfo>
+ <meta.section type="since"/>
+ </appInfo>
+ <documentation>
+ 1.6
+ </documentation>
+ </annotation>
+
+ <annotation>
+ <appInfo>
+ <meta.section type="examples"/>
+ </appInfo>
+ <documentation>
+ [Enter extension point usage example here.]
+ </documentation>
+ </annotation>
+
+ <annotation>
+ <appInfo>
+ <meta.section type="apiinfo"/>
+ </appInfo>
+ <documentation>
+ [Enter API information here.]
+ </documentation>
+ </annotation>
+
+ <annotation>
+ <appInfo>
+ <meta.section type="implementation"/>
+ </appInfo>
+ <documentation>
+ [Enter information about supplied implementation of this extension point.]
+ </documentation>
+ </annotation>
+
+ <annotation>
+ <appInfo>
+ <meta.section type="copyright"/>
+ </appInfo>
+ <documentation>
+ 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
+ </documentation>
+ </annotation>
+
+</schema>
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 891bb878..14ff2315 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 @@ -54,14 +54,16 @@ import org.apache.maven.plugin.descriptor.MojoDescriptor; import org.apache.maven.plugin.descriptor.PluginDescriptor; import org.apache.maven.project.MavenProject; +import org.eclipse.m2e.core.embedder.ArtifactKey; import org.eclipse.m2e.core.ui.internal.M2EUIPluginActivator; import org.eclipse.m2e.core.ui.internal.search.util.ArtifactInfo; 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; +import org.eclipse.m2e.editor.xml.mojo.IMojoParameterMetadataProvider; +import org.eclipse.m2e.editor.xml.mojo.MojoParameter; /** @@ -115,8 +117,8 @@ public enum PomTemplateContext { } @Override - protected void addTemplates(MavenProject project, IProject eclipseprj, Collection<Template> proposals, Node node, - String prefix) throws CoreException { + protected void addTemplates(final MavenProject project, IProject eclipseprj, Collection<Template> proposals, + Node node, String prefix) throws CoreException { // find configuration ancestor List<String> pathElements = new ArrayList<String>(); @@ -181,16 +183,12 @@ public enum PomTemplateContext { } } - Plugin plugin = new Plugin(); - plugin.setGroupId(groupId); - plugin.setArtifactId(artifactId); - plugin.setVersion(version); - + final ArtifactKey pluginKey = new ArtifactKey(groupId, artifactId, version, null); final String fConfigImpl = configImpl; final MojoParameter[] parameterMetadata = new MojoParameter[1]; final CoreException[] innerException = new CoreException[1]; - final MojoParameterMetadataProvider prov = new MojoParameterMetadataProvider(project, plugin); + final IMojoParameterMetadataProvider prov = new MojoParameterMetadataProvider(); try { PlatformUI.getWorkbench().getActiveWorkbenchWindow().run(true, true, new IRunnableWithProgress() { public void run(IProgressMonitor monitor) { @@ -198,11 +196,11 @@ public enum PomTemplateContext { try { MojoParameter parameter; if(fConfigImpl != null) { - parameter = prov.getParameterRoot(fConfigImpl, monitor); + parameter = prov.getClassConfiguration(pluginKey, fConfigImpl, monitor); } else if(usedMojos.isEmpty()) { - parameter = prov.getMojoParameterRoot(monitor); + parameter = prov.getMojoConfiguration(pluginKey, monitor); } else { - parameter = prov.getMojoParameterRoot(usedMojos, monitor); + parameter = prov.getMojoConfiguration(pluginKey, usedMojos, monitor); } if(monitor.isCanceled()) diff --git a/org.eclipse.m2e.editor.xml/src/main/java/org/eclipse/m2e/editor/xml/internal/mojo/DefaultMojoParameterMetadata.java b/org.eclipse.m2e.editor.xml/src/main/java/org/eclipse/m2e/editor/xml/internal/mojo/DefaultMojoParameterMetadata.java new file mode 100644 index 00000000..fb229b37 --- /dev/null +++ b/org.eclipse.m2e.editor.xml/src/main/java/org/eclipse/m2e/editor/xml/internal/mojo/DefaultMojoParameterMetadata.java @@ -0,0 +1,87 @@ +/******************************************************************************* + * 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.lang.reflect.Type; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; + +import org.apache.maven.plugin.descriptor.MojoDescriptor; +import org.apache.maven.plugin.descriptor.Parameter; +import org.apache.maven.plugin.descriptor.PluginDescriptor; + +import org.eclipse.m2e.editor.xml.mojo.IMojoParameterMetadata; +import org.eclipse.m2e.editor.xml.mojo.MojoParameter; +import org.eclipse.m2e.editor.xml.mojo.PlexusConfigHelper; + + +/** + * DefaultMojoParameterMetadataSource + * + * @author sleepless + */ +public class DefaultMojoParameterMetadata implements IMojoParameterMetadata { + private final Logger log = LoggerFactory.getLogger(getClass()); + + public List<MojoParameter> loadMojoParameters(PluginDescriptor desc, MojoDescriptor mojo, PlexusConfigHelper helper, + IProgressMonitor monitor) throws CoreException { + + if(monitor.isCanceled()) { + return Collections.emptyList(); + } + Class<?> clazz; + try { + clazz = mojo.getImplementationClass(); + if(clazz == null) { + clazz = desc.getClassRealm().loadClass(mojo.getImplementation()); + } + } catch(ClassNotFoundException | TypeNotPresentException ex) { + log.warn(ex.getMessage()); + return Collections.emptyList(); + } + + List<Parameter> ps = mojo.getParameters(); + Map<String, Type> properties = helper.getClassProperties(clazz); + + List<MojoParameter> parameters = new ArrayList<>(); + + if(ps != null) { + for(Parameter p : ps) { + if(monitor.isCanceled()) { + return Collections.emptyList(); + } + if(!p.isEditable()) { + continue; + } + + Type type = properties.get(p.getName()); + if(type == null) { + continue; + } + + helper.addParameter(desc.getClassRealm(), clazz, type, p.getName(), p.getAlias(), parameters, p.isRequired(), + p.getExpression(), p.getDescription(), p.getDefaultValue(), monitor); + } + } + + return parameters; + } + +} 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 index 07f6b4a4..d3a6b7b1 100644 --- 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 @@ -11,39 +11,33 @@ package org.eclipse.m2e.editor.xml.internal.mojo; -import java.io.File; -import java.io.IOException; -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.Arrays; 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 java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import com.google.common.cache.Cache; +import com.google.common.cache.CacheBuilder; import com.google.common.collect.ImmutableMap; -import com.google.common.collect.ImmutableSet; -import com.google.common.reflect.ClassPath; -import com.google.common.reflect.ClassPath.ClassInfo; import org.eclipse.aether.repository.RemoteRepository; import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IConfigurationElement; +import org.eclipse.core.runtime.IExtension; +import org.eclipse.core.runtime.IExtensionPoint; +import org.eclipse.core.runtime.IExtensionRegistry; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Platform; import org.eclipse.core.runtime.Status; import org.codehaus.plexus.component.repository.exception.ComponentLookupException; @@ -55,96 +49,91 @@ import org.apache.maven.model.building.UrlModelSource; import org.apache.maven.plugin.BuildPluginManager; import org.apache.maven.plugin.MavenPluginManager; 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.ArtifactKey; 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; +import org.eclipse.m2e.editor.xml.mojo.IMojoParameterMetadata; +import org.eclipse.m2e.editor.xml.mojo.IMojoParameterMetadataProvider; +import org.eclipse.m2e.editor.xml.mojo.MojoParameter; +import org.eclipse.m2e.editor.xml.mojo.PlexusConfigHelper; /** * @author atanasenko */ @SuppressWarnings("restriction") -public class MojoParameterMetadataProvider { +public class MojoParameterMetadataProvider implements IMojoParameterMetadataProvider { private static final Logger log = LoggerFactory.getLogger(MojoParameterMetadataProvider.class); - private final MavenProject project; - - private final Plugin plugin; + private static final Cache<String, MojoParameter> cache = CacheBuilder.newBuilder().maximumSize(100).softValues() + .build(); protected final MavenImpl maven; - private final Map<String, List<MojoParameter>> parameters; - - public MojoParameterMetadataProvider(MavenProject project, Plugin plugin) { - this.project = project; - this.plugin = plugin; + public MojoParameterMetadataProvider() { 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; - - execute(new ICallable<Void>() { - public Void call(IMavenExecutionContext context, IProgressMonitor monitor) throws CoreException { - PluginDescriptor pd = getPluginDescriptor(context, monitor); + public MojoParameter getClassConfiguration(final ArtifactKey pluginKey, final String className, + final IProgressMonitor monitor) throws CoreException { - if(pd != null) { - Class<?> clazz; - try { - clazz = pd.getClassRealm().loadClass(className); - } catch(ClassNotFoundException ex) { + try { + String key = pluginKey.toPortableString() + "/" + className; + + return cache.get(key, new Callable<MojoParameter>() { + public MojoParameter call() throws Exception { + + return execute(pluginKey, new ICallable<MojoParameter>() { + public MojoParameter call(IMavenExecutionContext context, IProgressMonitor monitor) throws CoreException { + PluginDescriptor pd = getPluginDescriptor(pluginKey, context, monitor); + + if(pd != null) { + Class<?> clazz; + try { + clazz = pd.getClassRealm().loadClass(className); + } catch(ClassNotFoundException ex) { + return null; + } + + return new MojoParameter("", className, //$NON-NLS-1$ + new PlexusConfigHelper().loadParameters(pd.getClassRealm(), clazz, monitor)); + } 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); + } catch(ExecutionException e) { + Throwable t = e.getCause(); + if(t instanceof CoreException) { + throw (CoreException) t; + } + if(t instanceof RuntimeException) { + throw (RuntimeException) t; + } + throw new CoreException(new Status(IStatus.ERROR, IMavenConstants.PLUGIN_ID, -1, e.getMessage(), e)); } - - return plist; } - public MojoParameter getMojoParameterRoot(final Collection<String> mojos, IProgressMonitor monitor) - throws CoreException { + public MojoParameter getMojoConfiguration(ArtifactKey pluginKey, 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); + MojoParameter md = getMojoConfiguration(pluginKey, mojo, monitor); for(MojoParameter p : md.getNestedParameters()) { if(!collected.add(p.getName())) continue; @@ -154,50 +143,78 @@ public class MojoParameterMetadataProvider { 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 getMojoConfiguration(ArtifactKey pluginKey, IProgressMonitor monitor) throws CoreException { + return getMojoConfiguration(pluginKey, "*", monitor); //$NON-NLS-1$ } - public MojoParameter getMojoParameterRoot(final String mojo, IProgressMonitor monitor) throws CoreException { + public MojoParameter getMojoConfiguration(final ArtifactKey pluginKey, final String mojo, + final IProgressMonitor monitor) throws CoreException { - MojoParameter predefParameters = getPredefined(); + MojoParameter predefParameters = getPredefined(pluginKey); if(predefParameters != null) { return predefParameters; } - String key = "mojo:" + (mojo == null ? "*" : mojo); //$NON-NLS-1$ //$NON-NLS-2$ + String key = pluginKey.toPortableString() + "/mojo/" + (mojo == null ? "*" : mojo); //$NON-NLS-1$ //$NON-NLS-2$ - List<MojoParameter> plist = parameters.get(key); + try { + return cache.get(key, new Callable<MojoParameter>() { + public MojoParameter call() throws Exception { + + return execute(pluginKey, new ICallable<MojoParameter>() { + public MojoParameter call(IMavenExecutionContext context, IProgressMonitor monitor) throws CoreException { + PluginDescriptor pd = getPluginDescriptor(pluginKey, context, monitor); + if(pd != null) { + return new MojoParameter("", mojo, loadMojoParameters(pd, mojo, monitor)); //$NON-NLS-1$ + } + return null; + } + }, monitor); + } + }); - if(plist == null) { + } catch(ExecutionException e) { + Throwable t = e.getCause(); + if(t instanceof CoreException) { + throw (CoreException) t; + } + if(t instanceof RuntimeException) { + throw (RuntimeException) t; + } + throw new CoreException(new Status(IStatus.ERROR, IMavenConstants.PLUGIN_ID, -1, e.getMessage(), e)); + } + } + + List<MojoParameter> loadMojoParameters(PluginDescriptor desc, String goal, IProgressMonitor monitor) + throws CoreException { - final List<MojoParameter> parameters = new ArrayList<>(); - plist = parameters; - this.parameters.put(key, plist); + PlexusConfigHelper helper = new PlexusConfigHelper(); - execute(new ICallable<Void>() { - public Void call(IMavenExecutionContext context, IProgressMonitor monitor) throws CoreException { - PluginDescriptor pd = getPluginDescriptor(context, monitor); - if(pd != null) { - loadMojoParameters(pd, mojo, parameters, monitor); + if(goal.equals("*")) { //$NON-NLS-1$ + List<MojoParameter> parameters = new ArrayList<>(); + Set<String> collected = new HashSet<>(); + for(MojoDescriptor mojo : desc.getMojos()) { + for(MojoParameter p : loadMojoParameters(desc, mojo, helper, monitor)) { + if(collected.add(p.getName())) { + parameters.add(p); } - return null; } - }, monitor); + } + return parameters; } - - return new MojoParameter("", mojo, plist); //$NON-NLS-1$ + return loadMojoParameters(desc, desc.getMojo(goal), helper, monitor); } - private MojoParameter getPredefined() { - return PREDEF.get(plugin.getGroupId() + ":" + plugin.getArtifactId() + ":" + plugin.getVersion()); + private MojoParameter getPredefined(ArtifactKey pluginKey) { + return PREDEF.get(pluginKey.getGroupId() + ":" + pluginKey.getArtifactId() + ":" + pluginKey.getVersion()); } - private void execute(final ICallable<Void> callable, IProgressMonitor monitor) throws CoreException { + <T> T execute(final ArtifactKey pluginKey, final ICallable<T> callable, IProgressMonitor monitor) + throws CoreException { 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(new ICallable<T>() { + public T call(IMavenExecutionContext context, IProgressMonitor monitor) throws CoreException { MavenProject mp = getProject(context); if(mp != null) { return context.execute(mp, callable, monitor); @@ -207,84 +224,7 @@ public class MojoParameterMetadataProvider { }, monitor); } - 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) { - log.warn(ex.getMessage()); - return; - } - - List<Parameter> ps = mojo.getParameters(); - Map<String, Type> properties = getClassProperties(clazz); - - if(ps != null) { - for(Parameter p : ps) { - if(monitor.isCanceled()) { - return; - } - if(!p.isEditable()) { - continue; - } - - Type type = properties.get(p.getName()); - if(type == null) { - continue; - } - - if(collected.add(p.getName())) { - addParameter(desc, clazz, type, 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; - } - - Map<String, Type> properties = getClassProperties(clazz); - - for(Map.Entry<String, Type> e : properties.entrySet()) { - if(monitor.isCanceled()) { - return; - } - addParameter(desc, clazz, e.getValue(), e.getKey(), null, parameters, false, null, null, null, monitor); - } - } - - protected MavenProject getProject(IMavenExecutionContext context) { - if(project != null) { - return project; - } + MavenProject getProject(IMavenExecutionContext context) { ModelSource modelSource = new UrlModelSource(DefaultMaven.class.getResource("project/standalone.xml")); //$NON-NLS-1$ try { @@ -295,9 +235,15 @@ public class MojoParameterMetadataProvider { } } - PluginDescriptor getPluginDescriptor(IMavenExecutionContext context, IProgressMonitor monitor) { + PluginDescriptor getPluginDescriptor(ArtifactKey pluginKey, IMavenExecutionContext context, + IProgressMonitor monitor) { PluginDescriptor desc; + Plugin plugin = new Plugin(); + plugin.setGroupId(pluginKey.getGroupId()); + plugin.setArtifactId(pluginKey.getArtifactId()); + plugin.setVersion(pluginKey.getVersion()); + List<RemoteRepository> remoteRepos = context.getSession().getCurrentProject().getRemotePluginRepositories(); try { desc = lookup(MavenPluginManager.class).getPluginDescriptor(plugin, remoteRepos, context.getRepositorySession()); @@ -314,403 +260,55 @@ public class MojoParameterMetadataProvider { 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)); + throw new CoreException( + new Status(IStatus.ERROR, IMavenConstants.PLUGIN_ID, -1, Messages.MavenImpl_error_lookup, ex)); } } - private void addParameter(PluginDescriptor desc, Class<?> enclosingClass, Type paramType, String name, String alias, - List<MojoParameter> parameters, boolean required, String expression, String description, String defaultValue, + List<MojoParameter> loadMojoParameters(PluginDescriptor desc, MojoDescriptor mojo, PlexusConfigHelper helper, IProgressMonitor monitor) throws CoreException { - Class<?> paramClass = getRawType(paramType); - if(paramClass == null) { - return; + IMojoParameterMetadata metadata = null; + String mojoConfigurator = mojo.getComponentConfigurator(); + if(mojoConfigurator != null) { + metadata = readMojoParameterMetadata(mojoConfigurator); } - - // inline - if(isInline(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)) { - // 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; - } - - // properties - if(Properties.class.isAssignableFrom(paramClass)) { - - MojoParameter inner = new MojoParameter("property", "property", Arrays.asList( - new MojoParameter("name", "String"), new MojoParameter("value", "String"))); - - parameters.add(configure(new MojoParameter(name, getTypeDisplayName(paramType), inner), required, expression, - description, defaultValue)); - if(alias != null) { - parameters.add(configure(new MojoParameter(alias, getTypeDisplayName(paramType), inner), required, expression, - description, defaultValue)); - } - } - - // collection/array - Type itemType = getItemType(paramType); - - if(itemType != null) { - - List<MojoParameter> itemParameters = getItemParameters(desc, enclosingClass, name, itemType, monitor); - - parameters.add(configure(new MojoParameter(name, getTypeDisplayName(paramType), itemParameters), required, - expression, description, defaultValue)); - - if(alias != null) { - itemParameters = getItemParameters(desc, enclosingClass, alias, itemType, monitor); - parameters.add(configure(new MojoParameter(alias, getTypeDisplayName(paramType), itemParameters), required, - expression, description, defaultValue)); - } - - return; - } - - // pojo - // skip classes without no-arg constructors - try { - paramClass.getConstructor(new Class[0]); - } catch(NoSuchMethodException ex) { - return; - } - - 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)); + if(metadata == null) { + metadata = new DefaultMojoParameterMetadata(); } + return metadata.loadMojoParameters(desc, mojo, helper, monitor); } - private List<MojoParameter> getItemParameters(PluginDescriptor desc, Class<?> enclosingClass, String name, - Type paramType, IProgressMonitor monitor) throws CoreException { - - Class<?> paramClass = getRawType(paramType); + public static final String EXTENSION_MOJO_PARAMETER_METADATA = MvnIndexPlugin.PLUGIN_ID + ".mojoParameterMetadata"; //$NON-NLS-1$ - if(paramClass == null || isInline(paramClass)) { - MojoParameter container = new MojoParameter(toSingular(name), getTypeDisplayName(paramType)).multiple(); - return Collections.singletonList(container); - } - - if(Map.class.isAssignableFrom(paramClass) || Properties.class.isAssignableFrom(paramClass)) { - MojoParameter container = new MojoParameter(toSingular(name), getTypeDisplayName(paramType)).multiple().map(); - return Collections.singletonList(container); - } - - Type itemType = getItemType(paramType); - - if(itemType != null) { - MojoParameter container = new MojoParameter(toSingular(name), getTypeDisplayName(paramType)).multiple(); - container.setNestedParameters(getItemParameters(desc, enclosingClass, name, itemType, monitor)); - return Collections.singletonList(container); - } + private static IMojoParameterMetadata readMojoParameterMetadata(String mojoConfigurator) { - @SuppressWarnings("rawtypes") - List<Class> parameterClasses = getCandidateClasses(desc, enclosingClass, paramClass); - - List<MojoParameter> parameters = new ArrayList<>(); - for(Class<?> clazz : parameterClasses) { - - String paramName; - if(clazz.equals(paramClass)) { - paramName = toSingular(name); - } else { - paramName = clazz.getSimpleName(); - paramName = Character.toLowerCase(paramName.charAt(0)) + paramName.substring(1); - } - - MojoParameter container = new MojoParameter(paramName, getTypeDisplayName(clazz)).multiple(); - container.setNestedParameters(getParameters(desc, clazz, monitor)); - parameters.add(container); - } - - return parameters; - } - - 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 != null && paramClass.isArray()) { - return paramClass.getComponentType(); - } - if(!Collection.class.isAssignableFrom(paramClass)) { - return null; - } - - if(paramType instanceof ParameterizedType) { - ParameterizedType pt = (ParameterizedType) paramType; - - Type[] args = pt.getActualTypeArguments(); - if(args.length > 0) { - return args[0]; - } - } - - return null; - } - - private static Map<String, Type> getClassProperties(Class<?> clazz) { - Map<String, Type> props = new HashMap<>(); - - Method[] methods; - try { - methods = clazz.getMethods(); - } catch(Throwable e) { - log.debug("Cannot get methods of " + clazz.getName(), e); - methods = null; - } - - if(methods != null) { - for(Method m : methods) { - if((m.getModifiers() & Modifier.STATIC) != 0) { - continue; - } - - String name = m.getName(); - - if((name.startsWith("add") || name.startsWith("set")) && m.getParameterTypes().length == 1) { //$NON-NLS-1$ //$NON-NLS-2$ - String prop = name.substring(3); - if(!prop.isEmpty()) { - prop = Character.toLowerCase(prop.charAt(0)) + prop.substring(1); - if(!props.containsKey(prop)) { - props.put(prop, m.getGenericParameterTypes()[0]); + IExtensionRegistry registry = Platform.getExtensionRegistry(); + IExtensionPoint mappingsExtensionPoint = registry.getExtensionPoint(EXTENSION_MOJO_PARAMETER_METADATA); + if(mappingsExtensionPoint != null) { + IExtension[] mappingsExtensions = mappingsExtensionPoint.getExtensions(); + for(IExtension extension : mappingsExtensions) { + IConfigurationElement[] elements = extension.getConfigurationElements(); + for(IConfigurationElement element : elements) { + if(element.getName().equals("mojoParameterMetadata") //$NON-NLS-1$ + && mojoConfigurator.equals(element.getAttribute("configurator"))) { //$NON-NLS-1$ + try { + return (IMojoParameterMetadata) element.createExecutableExtension("class"); //$NON-NLS-1$ + } catch(CoreException ex) { + log.error(ex.getMessage(), ex); } } } } } - - Class<?> pClazz = clazz; - while(pClazz != null && !pClazz.equals(Object.class)) { - - Field[] fields; - try { - fields = pClazz.getDeclaredFields(); - } catch(Throwable e) { - log.debug("Cannot get declared fields of " + pClazz.getName(), e); - fields = null; - } - - if(fields != null) { - for(Field f : fields) { - if((f.getModifiers() & (Modifier.STATIC | Modifier.FINAL)) != 0) { - continue; - } - - String prop = f.getName(); - - if(!props.containsKey(prop)) { - - props.put(prop, f.getGenericType()); - - } - } - } - pClazz = pClazz.getSuperclass(); - - } - - return props; - } - - @SuppressWarnings({"rawtypes", "unchecked"}) - private static List<Class> getCandidateClasses(PluginDescriptor desc, Class enclosingClass, Class paramClass) { - - String name = enclosingClass.getName(); - int dot = name.lastIndexOf('.'); - if(dot > 0) { - String pkg = name.substring(0, dot); - - List<Class> candidateClasses = null; - - ClassPath cp; - try { - cp = ClassPath.from(desc.getClassRealm()); - } catch(IOException e) { - log.error(e.getMessage()); - return Collections.singletonList(enclosingClass); - } - - for(ClassInfo ci : cp.getTopLevelClasses(pkg)) { - Class clazz; - try { - clazz = desc.getClassRealm().loadClass(ci.getName()); - } catch(ClassNotFoundException e) { - log.error(e.getMessage(), e); - continue; - } - - if((clazz.getModifiers() & (Modifier.ABSTRACT)) != 0) { - continue; - } - - if(!paramClass.isAssignableFrom(clazz)) { - continue; - } - - // skip classes without no-arg constructors - try { - clazz.getConstructor(new Class[0]); - } catch(NoSuchMethodException ex) { - continue; - } - - if(candidateClasses == null) { - candidateClasses = new ArrayList<Class>(); - } - candidateClasses.add(clazz); - - } - - if(candidateClasses != null) { - return candidateClasses; - } - } - - return Collections.singletonList(paramClass); - } - - private static boolean isInline(Class<?> paramClass) { - return INLINE_TYPES.contains(paramClass.getName()) || paramClass.isEnum(); - } - - 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("<"); //$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(">").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; + return null; } - private static final Set<String> INLINE_TYPES; - private static final Map<String, MojoParameter> PREDEF; static { // @formatter:off - INLINE_TYPES = ImmutableSet.<String>of( - byte.class.getName(), - Byte.class.getName(), - short.class.getName(), - Short.class.getName(), - int.class.getName(), - Integer.class.getName(), - long.class.getName(), - Long.class.getName(), - float.class.getName(), - Float.class.getName(), - double.class.getName(), - Double.class.getName(), - boolean.class.getName(), - Boolean.class.getName(), - char.class.getName(), - Character.class.getName(), - - String.class.getName(), - StringBuilder.class.getName(), - StringBuffer.class.getName(), - - File.class.getName(), - URI.class.getName(), - URL.class.getName(), - Date.class.getName(), - - "org.codehaus.plexus.configuration.PlexusConfiguration" - ); - // @formatter:on - } - - static { - // @formatter:off PREDEF = ImmutableMap.<String, MojoParameter>of( "org.eclipse.m2e:lifecycle-mapping:1.0.0", new MojoParameter("", "", Collections.singletonList( diff --git a/org.eclipse.m2e.editor.xml/src/main/java/org/eclipse/m2e/editor/xml/mojo/IMojoParameterMetadata.java b/org.eclipse.m2e.editor.xml/src/main/java/org/eclipse/m2e/editor/xml/mojo/IMojoParameterMetadata.java new file mode 100644 index 00000000..7e8b7c3b --- /dev/null +++ b/org.eclipse.m2e.editor.xml/src/main/java/org/eclipse/m2e/editor/xml/mojo/IMojoParameterMetadata.java @@ -0,0 +1,34 @@ +/******************************************************************************* + * 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.mojo; + +import java.util.List; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; + +import org.apache.maven.plugin.descriptor.MojoDescriptor; +import org.apache.maven.plugin.descriptor.PluginDescriptor; + + +/** + * @since 1.6 + */ +public interface IMojoParameterMetadata { + + /** + * Returns a list of parameters that are applicable to a specified plugin mojo + */ + List<MojoParameter> loadMojoParameters(PluginDescriptor desc, MojoDescriptor mojo, PlexusConfigHelper helper, + IProgressMonitor monitor) throws CoreException; + +} diff --git a/org.eclipse.m2e.editor.xml/src/main/java/org/eclipse/m2e/editor/xml/mojo/IMojoParameterMetadataProvider.java b/org.eclipse.m2e.editor.xml/src/main/java/org/eclipse/m2e/editor/xml/mojo/IMojoParameterMetadataProvider.java new file mode 100644 index 00000000..5ee6f609 --- /dev/null +++ b/org.eclipse.m2e.editor.xml/src/main/java/org/eclipse/m2e/editor/xml/mojo/IMojoParameterMetadataProvider.java @@ -0,0 +1,53 @@ +/******************************************************************************* + * 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.mojo; + +import java.util.Collection; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; + +import org.eclipse.m2e.core.embedder.ArtifactKey; + + +/** + * Provides available configuration options for maven plugin executions + * + * @author atanasenko + * @since 1.6 + */ +public interface IMojoParameterMetadataProvider { + + /** + * Calculates available configuration of one specific mojo. + */ + public MojoParameter getMojoConfiguration(ArtifactKey pluginKey, String mojo, IProgressMonitor monitor) + throws CoreException; + + /** + * Calculates available configuration of a number of mojos. + */ + public MojoParameter getMojoConfiguration(ArtifactKey pluginKey, Collection<String> mojos, IProgressMonitor monitor) + throws CoreException; + + /** + * Calculates available configuration of all mojos provided by specified plugin. + */ + public MojoParameter getMojoConfiguration(ArtifactKey pluginKey, IProgressMonitor monitor) throws CoreException; + + /** + * Calculates available configuration of a single plugin class. + */ + public MojoParameter getClassConfiguration(ArtifactKey pluginKey, String className, IProgressMonitor monitor) + throws CoreException; + +} 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/mojo/MojoParameter.java index 57814148..2b2702e9 100644 --- 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/mojo/MojoParameter.java @@ -9,14 +9,14 @@ * Anton Tanasenko. - initial API and implementation *******************************************************************************/ -package org.eclipse.m2e.editor.xml.internal.mojo; +package org.eclipse.m2e.editor.xml.mojo; import java.util.Collections; import java.util.List; /** - * @author atanasenko + * @since 1.6 */ public class MojoParameter { @@ -48,16 +48,16 @@ public class MojoParameter { this(name, type, Collections.singletonList(parameter)); } - protected MojoParameter(String name, String type) { + public MojoParameter(String name, String type) { this(name, type, Collections.<MojoParameter> emptyList()); } - MojoParameter multiple() { + public MojoParameter multiple() { this.multiple = true; return this; } - MojoParameter map() { + public MojoParameter map() { this.map = true; return this; } @@ -71,11 +71,7 @@ public class MojoParameter { } public List<MojoParameter> getNestedParameters() { - return Collections.unmodifiableList(nested); - } - - void setNestedParameters(List<MojoParameter> nested) { - this.nested = nested; + return nested == null ? Collections.<MojoParameter> emptyList() : Collections.unmodifiableList(nested); } public String getName() { diff --git a/org.eclipse.m2e.editor.xml/src/main/java/org/eclipse/m2e/editor/xml/mojo/PlexusConfigHelper.java b/org.eclipse.m2e.editor.xml/src/main/java/org/eclipse/m2e/editor/xml/mojo/PlexusConfigHelper.java new file mode 100644 index 00000000..b5287132 --- /dev/null +++ b/org.eclipse.m2e.editor.xml/src/main/java/org/eclipse/m2e/editor/xml/mojo/PlexusConfigHelper.java @@ -0,0 +1,460 @@ +/*******************************************************************************
+ * 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.mojo;
+
+import java.io.File;
+import java.io.IOException;
+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.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.collect.ImmutableSet;
+import com.google.common.reflect.ClassPath;
+import com.google.common.reflect.ClassPath.ClassInfo;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+
+import org.codehaus.plexus.classworlds.realm.ClassRealm;
+
+
+/**
+ * Mirrors logic implemented in default maven mojo configurator with regards to discovering how a PlexusConfiguration
+ * can be applied to an arbitrary object tree.
+ *
+ * @see org.codehaus.plexus.component.configurator.BasicComponentConfigurator
+ * @see org.codehaus.plexus.component.configurator.converters.lookup.DefaultConverterLookup
+ * @see org.codehaus.plexus.component.configurator.converters.composite.ObjectWithFieldsConverter
+ * @since 1.6
+ */
+public class PlexusConfigHelper {
+
+ private static final Logger log = LoggerFactory.getLogger(PlexusConfigHelper.class);
+
+ private Map<Class<?>, List<MojoParameter>> processedClasses;
+
+ public PlexusConfigHelper() {
+ processedClasses = new HashMap<>();
+ }
+
+ public List<MojoParameter> loadParameters(ClassRealm realm, Class<?> paramClass, IProgressMonitor monitor)
+ throws CoreException {
+
+ if(monitor.isCanceled()) {
+ return Collections.emptyList();
+ }
+
+ List<MojoParameter> parameters = processedClasses.get(paramClass);
+ if(parameters == null) {
+ parameters = new ArrayList<>();
+ processedClasses.put(paramClass, parameters);
+
+ log.debug("Loading properties of {}", paramClass.getName());
+ Map<String, Type> properties = getClassProperties(paramClass);
+
+ for(Map.Entry<String, Type> e : properties.entrySet()) {
+ if(!monitor.isCanceled()) {
+ addParameter(realm, paramClass, e.getValue(), e.getKey(), null, parameters, false, null, null, null, monitor);
+ }
+ }
+ }
+
+ return parameters;
+ }
+
+ public void addParameter(ClassRealm realm, Class<?> enclosingClass, 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);
+ if(paramClass == null) {
+ return;
+ }
+
+ // inline
+ if(isInline(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)) {
+ // 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;
+ }
+
+ // properties
+ if(Properties.class.isAssignableFrom(paramClass)) {
+
+ MojoParameter nested = new MojoParameter("property", "property",
+ Arrays.asList(new MojoParameter("name", "String"), new MojoParameter("value", "String")));
+
+ parameters.add(configure(new MojoParameter(name, getTypeDisplayName(paramType), nested), required, expression,
+ description, defaultValue));
+ if(alias != null) {
+ parameters.add(configure(new MojoParameter(alias, getTypeDisplayName(paramType), nested), required, expression,
+ description, defaultValue));
+ }
+ }
+
+ // collection/array
+ Type itemType = getItemType(paramType);
+
+ if(itemType != null) {
+
+ List<MojoParameter> nested = getItemParameters(realm, enclosingClass, name, itemType, monitor);
+
+ parameters.add(configure(new MojoParameter(name, getTypeDisplayName(paramType), nested), required, expression,
+ description, defaultValue));
+
+ if(alias != null) {
+ nested = getItemParameters(realm, enclosingClass, alias, itemType, monitor);
+ parameters.add(configure(new MojoParameter(alias, getTypeDisplayName(paramType), nested), required, expression,
+ description, defaultValue));
+ }
+
+ return;
+ }
+
+ // pojo
+ // skip classes without no-arg constructors
+ try {
+ paramClass.getConstructor(new Class[0]);
+ } catch(NoSuchMethodException ex) {
+ return;
+ }
+
+ List<MojoParameter> nested = loadParameters(realm, paramClass, monitor);
+ parameters.add(configure(new MojoParameter(name, getTypeDisplayName(paramType), nested), required, expression,
+ description, defaultValue));
+ if(alias != null) {
+ parameters.add(configure(new MojoParameter(alias, getTypeDisplayName(paramType), nested), required, expression,
+ description, defaultValue));
+ }
+
+ }
+
+ public List<MojoParameter> getItemParameters(ClassRealm realm, Class<?> enclosingClass, String name, Type paramType,
+ IProgressMonitor monitor) throws CoreException {
+
+ Class<?> paramClass = getRawType(paramType);
+
+ if(paramClass == null || isInline(paramClass)) {
+ MojoParameter container = new MojoParameter(toSingularName(name), getTypeDisplayName(paramType)).multiple();
+ return Collections.singletonList(container);
+ }
+
+ if(Map.class.isAssignableFrom(paramClass) || Properties.class.isAssignableFrom(paramClass)) {
+ MojoParameter container = new MojoParameter(toSingularName(name), getTypeDisplayName(paramType)).multiple().map();
+ return Collections.singletonList(container);
+ }
+
+ Type itemType = getItemType(paramType);
+
+ if(itemType != null) {
+ List<MojoParameter> nested = getItemParameters(realm, enclosingClass, name, itemType, monitor);
+ MojoParameter container = new MojoParameter(toSingularName(name), getTypeDisplayName(paramType), nested)
+ .multiple();
+ return Collections.singletonList(container);
+ }
+
+ @SuppressWarnings("rawtypes")
+ List<Class> parameterClasses = getCandidateClasses(realm, enclosingClass, paramClass);
+
+ List<MojoParameter> parameters = new ArrayList<>();
+ for(Class<?> clazz : parameterClasses) {
+
+ String paramName;
+ if(clazz.equals(paramClass)) {
+ paramName = toSingularName(name);
+ } else {
+ paramName = clazz.getSimpleName();
+ paramName = Character.toLowerCase(paramName.charAt(0)) + paramName.substring(1);
+ }
+
+ List<MojoParameter> nested = loadParameters(realm, paramClass, monitor);
+ MojoParameter container = new MojoParameter(paramName, getTypeDisplayName(clazz), nested).multiple();
+ parameters.add(container);
+ }
+
+ return parameters;
+ }
+
+ public 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;
+ }
+
+ public static Class<?> getRawType(Type type) {
+ if(type instanceof Class) {
+ return (Class<?>) type;
+ }
+ if(type instanceof ParameterizedType) {
+ return (Class<?>) ((ParameterizedType) type).getRawType();
+ }
+ return null;
+ }
+
+ public static Type getItemType(Type paramType) {
+
+ Class<?> paramClass = getRawType(paramType);
+
+ if(paramClass != null && paramClass.isArray()) {
+ return paramClass.getComponentType();
+ }
+ if(!Collection.class.isAssignableFrom(paramClass)) {
+ return null;
+ }
+
+ if(paramType instanceof ParameterizedType) {
+ ParameterizedType pt = (ParameterizedType) paramType;
+
+ Type[] args = pt.getActualTypeArguments();
+ if(args.length > 0) {
+ return args[0];
+ }
+ }
+
+ return null;
+ }
+
+ public Map<String, Type> getClassProperties(Class<?> clazz) {
+ Map<String, Type> props = new HashMap<>();
+
+ for(Method m : clazz.getMethods()) {
+ if((m.getModifiers() & Modifier.STATIC) != 0) {
+ continue;
+ }
+
+ String name = m.getName();
+
+ if((name.startsWith("add") || name.startsWith("set")) && m.getParameterTypes().length == 1) { //$NON-NLS-1$ //$NON-NLS-2$
+ String prop = name.substring(3);
+ if(!prop.isEmpty()) {
+ prop = Character.toLowerCase(prop.charAt(0)) + prop.substring(1);
+ if(!props.containsKey(prop)) {
+ props.put(prop, m.getGenericParameterTypes()[0]);
+ }
+ }
+ }
+ }
+
+ Class<?> pClazz = clazz;
+ while(pClazz != null && !pClazz.equals(Object.class)) {
+
+ for(Field f : pClazz.getDeclaredFields()) {
+ if((f.getModifiers() & (Modifier.STATIC | Modifier.FINAL)) != 0) {
+ continue;
+ }
+
+ String prop = f.getName();
+
+ if(!props.containsKey(prop)) {
+
+ props.put(prop, f.getGenericType());
+
+ }
+ }
+ pClazz = pClazz.getSuperclass();
+
+ }
+
+ return props;
+ }
+
+ @SuppressWarnings({"rawtypes", "unchecked"})
+ public List<Class> getCandidateClasses(ClassRealm realm, Class enclosingClass, Class paramClass) {
+
+ String name = enclosingClass.getName();
+ int dot = name.lastIndexOf('.');
+ if(dot > 0) {
+ String pkg = name.substring(0, dot);
+
+ List<Class> candidateClasses = null;
+
+ ClassPath cp;
+ try {
+ cp = ClassPath.from(realm);
+ } catch(IOException e) {
+ log.error(e.getMessage());
+ return Collections.singletonList(enclosingClass);
+ }
+
+ for(ClassInfo ci : cp.getTopLevelClasses(pkg)) {
+ Class clazz;
+ try {
+ clazz = realm.loadClass(ci.getName());
+ } catch(ClassNotFoundException e) {
+ log.error(e.getMessage(), e);
+ continue;
+ }
+
+ if((clazz.getModifiers() & (Modifier.ABSTRACT)) != 0) {
+ continue;
+ }
+
+ if(!paramClass.isAssignableFrom(clazz)) {
+ continue;
+ }
+
+ // skip classes without no-arg constructors
+ try {
+ clazz.getConstructor(new Class[0]);
+ } catch(NoSuchMethodException ex) {
+ continue;
+ }
+
+ if(candidateClasses == null) {
+ candidateClasses = new ArrayList<Class>();
+ }
+ candidateClasses.add(clazz);
+
+ }
+
+ if(candidateClasses != null) {
+ return candidateClasses;
+ }
+ }
+
+ return Collections.singletonList(paramClass);
+ }
+
+ public static boolean isInline(Class<?> paramClass) {
+ return INLINE_TYPES.contains(paramClass.getName()) || paramClass.isEnum();
+ }
+
+ public 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("<"); //$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(">").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;
+ }
+
+ public String toSingularName(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<String> INLINE_TYPES;
+
+ static {
+ // @formatter:off
+ INLINE_TYPES = ImmutableSet.<String>of(
+ byte.class.getName(),
+ Byte.class.getName(),
+ short.class.getName(),
+ Short.class.getName(),
+ int.class.getName(),
+ Integer.class.getName(),
+ long.class.getName(),
+ Long.class.getName(),
+ float.class.getName(),
+ Float.class.getName(),
+ double.class.getName(),
+ Double.class.getName(),
+ boolean.class.getName(),
+ Boolean.class.getName(),
+ char.class.getName(),
+ Character.class.getName(),
+
+ String.class.getName(),
+ StringBuilder.class.getName(),
+ StringBuffer.class.getName(),
+
+ File.class.getName(),
+ URI.class.getName(),
+ URL.class.getName(),
+ Date.class.getName(),
+
+ "org.codehaus.plexus.configuration.PlexusConfiguration"
+ );
+ // @formatter:on
+ }
+
+}
|