blob: ddcf78ad2371168ea8ae6f9e98fb5d433944c29c [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2008-2010 Sonatype, 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:
* Sonatype, Inc. - initial API and implementation
*******************************************************************************/
package org.eclipse.m2e.editor.xml;
import java.io.File;
import java.io.IOException;
import java.nio.file.FileVisitOption;
import java.nio.file.FileVisitResult;
import java.nio.file.FileVisitor;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.EnumSet;
import java.util.HashSet;
import java.util.LinkedHashSet;
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 org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IWorkspaceRoot;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.text.templates.Template;
import org.eclipse.osgi.util.NLS;
import org.eclipse.swt.graphics.Image;
import org.eclipse.ui.ISharedImages;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.model.IWorkbenchAdapter;
import org.codehaus.plexus.interpolation.InterpolationException;
import org.codehaus.plexus.interpolation.PrefixedObjectValueSource;
import org.codehaus.plexus.interpolation.PropertiesBasedValueSource;
import org.codehaus.plexus.interpolation.RegexBasedInterpolator;
import org.apache.maven.model.Dependency;
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.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.MojoParameterMetadataProvider;
import org.eclipse.m2e.editor.xml.mojo.IMojoParameterMetadataProvider;
import org.eclipse.m2e.editor.xml.mojo.MojoParameter;
/**
* Context types.
*
* @author Lukas Krecan
* @author Eugene Kuleshov
*/
@SuppressWarnings("restriction")
public enum PomTemplateContext {
UNKNOWN("unknown"), // //$NON-NLS-1$
DOCUMENT("#document"), // //$NON-NLS-1$
PROJECT("project"), // //$NON-NLS-1$
BUILD("build"), // //$NON-NLS-1$
PARENT("parent"), // //$NON-NLS-1$
RELATIVE_PATH("relativePath"), // //$NON-NLS-1$
DEPENDENCIES("dependencies"), // //$NON-NLS-1$
DEPENDENCY_MANAGEMENT("dependencyManagement"), // //$NON-NLS-1$
EXCLUSIONS("exclusions"), // //$NON-NLS-1$
PLUGINS("plugins"), // //$NON-NLS-1$
PLUGIN("plugin"), // //$NON-NLS-1$
PLUGIN_MANAGEMENT("pluginManagement"), // //$NON-NLS-1$
EXECUTIONS("executions"), // //$NON-NLS-1$
PROFILES("profiles"), // //$NON-NLS-1$
PROFILE("profile"), // //$NON-NLS-1$
REPOSITORIES("repositories"), // //$NON-NLS-1$
PROPERTIES("properties") { // //$NON-NLS-1$
protected void addTemplates(MavenProject project, IProject eclipsePrj, Collection<Template> templates,
Node currentNode, String prefix) {
// propose overridable properties
Set<String> currentNodeProps = new HashSet<>();
NodeList nodes = currentNode.getChildNodes();
for(int i = 0; i < nodes.getLength(); i++ ) {
currentNodeProps.add(nodes.item(i).getNodeName());
}
if(project != null) {
Properties props = project.getProperties();
if(props != null) {
for(Map.Entry<Object, Object> e : props.entrySet()) {
String name = e.getKey().toString();
String value = e.getValue().toString();
if(currentNodeProps.contains(name))
continue;
if(!name.startsWith(prefix))
continue;
String template = "<" + name + ">${cursor}" + value + "</" + name + ">"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
String desc = NLS.bind(Messages.PomTemplateContext_property_override, value);
templates.add(new PomTemplate(name, desc, getContextTypeId(), template, false).image(MvnImages.IMG_PROPERTY)
.relevance(2000));
}
}
}
}
},
CONFIGURATION("configuration") { //$NON-NLS-1$
public boolean handlesSubtree() {
return true;
}
@Override
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>();
String configImpl = null;
Element configNode = (Element) node;
while(configNode != null && !getNodeName().equals(configNode.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();
}
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(pluginSubNode);
String version = extractVersion(project, eclipseprj, getVersion(pluginSubNode), groupId, artifactId,
EXTRACT_STRATEGY_PLUGIN | EXTRACT_STRATEGY_SEARCH);
if(version == null) {
return;
}
// 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);
}
}
}
}
}
ArtifactKey pluginKey = new ArtifactKey(groupId, artifactId, version, null);
IMojoParameterMetadataProvider prov = new MojoParameterMetadataProvider();
MojoParameter result;
if(configImpl != null) {
result = prov.getClassConfiguration(pluginKey, configImpl);
} else if(usedMojos.isEmpty()) {
result = prov.getMojoConfiguration(pluginKey);
} else {
result = prov.getMojoConfiguration(pluginKey, usedMojos);
}
MojoParameter param = result.getContainer(configPath);
if(param != null) {
List<MojoParameter> nestedParameters = param.getNestedParameters();
for(MojoParameter parameter : nestedParameters) {
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 PomTemplate(name, text, getContextTypeId(), //
"<" + name + ">${cursor}</" + name + ">", false) //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
.image(MvnImages.IMG_PARAMETER).relevance(1900));
}
}
if(param.isMap()) {
if(prefix != null && !prefix.trim().isEmpty()) {
proposals.add(new PomTemplate(NLS.bind(Messages.PomTemplateContext_insertParameter, prefix), "", //$NON-NLS-1$
getContextTypeId(), "<" + prefix + ">${cursor}</" + prefix + ">", true) //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
.image(MvnImages.IMG_PARAMETER).relevance(1500));
}
}
if(nestedParameters.size() == 1) {
MojoParameter nestedParam = nestedParameters.get(0);
if(nestedParam.isMultiple()) {
boolean containsFiles = File.class.getSimpleName().equals(nestedParam.getType())
|| PomTemplateContext.fromNodeName(nestedParam.getName()).handlesFiles();
if(containsFiles) {
addFileTemplates(project, eclipseprj, proposals, node, prefix,
param.getName().toLowerCase().endsWith("directory"), nestedParam.getName());
}
}
}
boolean containsFiles = File.class.getSimpleName().equals(param.getType())
|| PomTemplateContext.fromNodeName(param.getName()).handlesFiles();
if(containsFiles) {
addFileTemplates(project, eclipseprj, proposals, node, prefix,
param.getName().toLowerCase().endsWith("directory"), null);
}
}
}
},
GROUP_ID("groupId") { //$NON-NLS-1$
@Override
public void addTemplates(MavenProject project, IProject eclipseprj, Collection<Template> proposals, Node node,
String prefix) throws CoreException {
for(String groupId : getSearchEngine(eclipseprj).findGroupIds(prefix, getPackaging(node),
getContainingArtifact(node))) {
checkAndAdd(proposals, prefix, groupId);
}
}
},
ARTIFACT_ID("artifactId") { //$NON-NLS-1$
@Override
public void addTemplates(MavenProject project, IProject eclipseprj, Collection<Template> proposals, Node node,
String prefix) throws CoreException {
String groupId = getGroupId(node);
//#MNGECLIPSE-1832
if((groupId == null || groupId.trim().length() == 0) && "plugin".equals(node.getParentNode().getNodeName())) {
groupId = "org.apache.maven.plugins"; //$NON-NLS-1$
}
if(groupId != null) {
for(String artifactId : getSearchEngine(eclipseprj).findArtifactIds(groupId, prefix, getPackaging(node),
getContainingArtifact(node))) {
checkAndAdd(proposals, prefix, artifactId, groupId + ":" + artifactId);
}
}
}
},
VERSION("version") { //$NON-NLS-1$
@Override
public void addTemplates(MavenProject project, IProject eclipseprj, Collection<Template> proposals, Node node,
String prefix) throws CoreException {
String groupId = getGroupId(node);
//#MNGECLIPSE-1832
if((groupId == null || groupId.trim().length() == 0) && "plugin".equals(node.getParentNode().getNodeName())) {
groupId = "org.apache.maven.plugins"; //$NON-NLS-1$
}
String artifactId = getArtifactId(node);
if(groupId != null && artifactId != null) {
for(String version : getSearchEngine(eclipseprj).findVersions(groupId, artifactId, prefix,
getPackaging(node))) {
checkAndAdd(proposals, prefix, version, groupId + ":" + artifactId + ":" + version);
}
}
//mkleint: this concept that all versions out there are equal is questionable..
if("dependency".equals(node.getParentNode().getNodeName())) { //$NON-NLS-1$
//see if we can complete the properties ending with .version
List<String> keys = new ArrayList<String>();
String contextTypeId = getContextTypeId();
MavenProject mvn = project;
if(mvn != null) {
//if groupid is the same, suggest ${project.version}
if(groupId != null && groupId.equals(mvn.getGroupId())) {
proposals.add(new Template("${project.version}", Messages.PomTemplateContext_project_version_hint, //$NON-NLS-1$
contextTypeId, "$${project.version}", false)); //$NON-NLS-3$
}
Properties props = mvn.getProperties();
if(props != null) {
for(Object key : props.keySet()) {
//only add the properties following the .version convention
if(key.toString().endsWith(".version") || key.toString().endsWith("Version")) { //$NON-NLS-1$ //$NON-NLS-2$
keys.add(key.toString());
}
}
//sort just properties
Collections.sort(keys);
if(keys.size() > 0) {
for(String key : keys) {
String expr = "${" + key + "}"; //$NON-NLS-1$ //$NON-NLS-2$
proposals.add(new Template(expr, Messages.PomTemplateContext_expression_description, contextTypeId,
"$" + expr, false)); //$NON-NLS-2$ //$NON-NLS-1$
}
}
}
} else {
//if we don't have the maven facade, it means the pom is probably broken.
//all we can do is to try guess the groupid and come up with the project.version proposal eventually
Element root = node.getOwnerDocument().getDocumentElement();
if(root != null && "project".equals(root.getNodeName())) {//$NON-NLS-1$
String currentgroupid = XmlUtils.getTextValue(XmlUtils.findChild(root, "groupId"));//$NON-NLS-1$
if(currentgroupid == null) {
Element parEl = XmlUtils.findChild(root, "parent");//$NON-NLS-1$
if(parEl != null) {
currentgroupid = XmlUtils.getTextValue(XmlUtils.findChild(parEl, "groupId"));//$NON-NLS-1$
}
}
if(groupId != null && groupId.equals(currentgroupid)) {
proposals.add(new Template("${project.version}", Messages.PomTemplateContext_project_version_hint, //$NON-NLS-1$
contextTypeId, "$${project.version}", false)); //$NON-NLS-3$
}
}
}
}
}
},
CLASSIFIER("classifier") { //$NON-NLS-1$
@Override
public void addTemplates(MavenProject project, IProject eclipseprj, Collection<Template> proposals, Node node,
String prefix) throws CoreException {
String groupId = getGroupId(node);
String artifactId = getArtifactId(node);
String version = getVersion(node);
if(groupId != null && artifactId != null && version != null) {
for(String classifier : getSearchEngine(eclipseprj).findClassifiers(groupId, artifactId, version, prefix,
getPackaging(node))) {
checkAndAdd(proposals, prefix, classifier, groupId + ":" + artifactId + ":" + version + ":" + classifier);
}
}
}
},
TYPE("type") { //$NON-NLS-1$
@Override
public void addTemplates(MavenProject project, IProject eclipseprj, Collection<Template> proposals, Node node,
String prefix) throws CoreException {
String groupId = getGroupId(node);
String artifactId = getArtifactId(node);
String version = getVersion(node);
if(groupId != null && artifactId != null && version != null) {
for(String type : getSearchEngine(eclipseprj).findTypes(groupId, artifactId, version, prefix,
getPackaging(node))) {
checkAndAdd(proposals, prefix, type, groupId + ":" + artifactId + ":" + version + ":" + type);
}
}
}
},
PACKAGING("packaging") { //$NON-NLS-1$
@Override
public void addTemplates(MavenProject project, IProject eclipseprj, Collection<Template> proposals, Node node,
String prefix) {
// TODO only show "pom" packaging in root section
checkAndAdd(proposals, prefix, "pom"); //$NON-NLS-1$
checkAndAdd(proposals, prefix, "jar"); //$NON-NLS-1$
checkAndAdd(proposals, prefix, "war"); //$NON-NLS-1$
checkAndAdd(proposals, prefix, "ear"); //$NON-NLS-1$
checkAndAdd(proposals, prefix, "ejb"); //$NON-NLS-1$
// checkAndAdd(proposals, prefix, "eclipse-plugin"); //$NON-NLS-1$
// checkAndAdd(proposals, prefix, "eclipse-feature"); //$NON-NLS-1$
// checkAndAdd(proposals, prefix, "eclipse-update-site"); //$NON-NLS-1$
checkAndAdd(proposals, prefix, "maven-plugin"); //$NON-NLS-1$
checkAndAdd(proposals, prefix, "maven-archetype"); //$NON-NLS-1$
}
},
SCOPE("scope") { //$NON-NLS-1$
@Override
public void addTemplates(MavenProject project, IProject eclipseprj, Collection<Template> proposals, Node node,
String prefix) {
checkAndAdd(proposals, prefix, "compile"); //$NON-NLS-1$
checkAndAdd(proposals, prefix, "test"); //$NON-NLS-1$
checkAndAdd(proposals, prefix, "provided"); //$NON-NLS-1$
checkAndAdd(proposals, prefix, "runtime"); //$NON-NLS-1$
checkAndAdd(proposals, prefix, "system"); //$NON-NLS-1$
if(getAncestor(node, "dependency", "dependencies", "dependencyManagement") != null) {// $NON-NLS-1$ $NON-NLS-2$ $NON-NLS-3$
checkAndAdd(proposals, prefix, "import"); //$NON-NLS-1$
}
}
},
SYSTEM_PATH("systemPath"), //$NON-NLS-1$
PHASE("phase") { //$NON-NLS-1$
@Override
public void addTemplates(MavenProject project, IProject eclipseprj, Collection<Template> proposals, Node node,
String prefix) {
// TODO the following list should be derived from the packaging handler (the actual lifecycle)
// Clean Lifecycle
checkAndAdd(proposals, prefix, "pre-clean", Messages.PomTemplateContext_preclean); //$NON-NLS-1$
checkAndAdd(proposals, prefix, "clean", Messages.PomTemplateContext_clean); //$NON-NLS-1$
checkAndAdd(proposals, prefix, "post-clean", Messages.PomTemplateContext_postclean); //$NON-NLS-1$
// Default Lifecycle
checkAndAdd(proposals, prefix, "validate", Messages.PomTemplateContext_validate); //$NON-NLS-1$
checkAndAdd(proposals, prefix, "generate-sources", Messages.PomTemplateContext_generatesources); //$NON-NLS-1$
checkAndAdd(proposals, prefix, "process-sources", Messages.PomTemplateContext_processsources); //$NON-NLS-1$
checkAndAdd(proposals, prefix, "generate-resources", Messages.PomTemplateContext_generateresources); //$NON-NLS-1$
checkAndAdd(proposals, prefix, "process-resources", Messages.PomTemplateContext_processresources); //$NON-NLS-1$
checkAndAdd(proposals, prefix, "compile", Messages.PomTemplateContext_compile); //$NON-NLS-1$
checkAndAdd(proposals, prefix, "process-classes", Messages.PomTemplateContext_processclasses); //$NON-NLS-1$
checkAndAdd(proposals, prefix, "generate-test-sources", Messages.PomTemplateContext_generatetestsources); //$NON-NLS-1$
checkAndAdd(proposals, prefix, "process-test-sources", Messages.PomTemplateContext_processtestsources); //$NON-NLS-1$
checkAndAdd(proposals, prefix, "generate-test-resources", Messages.PomTemplateContext_generatetestresources); //$NON-NLS-1$
checkAndAdd(proposals, prefix, "process-test-resources", Messages.PomTemplateContext_processtestresources); //$NON-NLS-1$
checkAndAdd(proposals, prefix, "test-compile", Messages.PomTemplateContext_testcompile); //$NON-NLS-1$
checkAndAdd(proposals, prefix, "process-test-classes", Messages.PomTemplateContext_processtestclasses); //$NON-NLS-1$
checkAndAdd(proposals, prefix, "test", Messages.PomTemplateContext_test); //$NON-NLS-1$
checkAndAdd(proposals, prefix, "prepare-package", Messages.PomTemplateContext_preparepackage); //$NON-NLS-1$
checkAndAdd(proposals, prefix, "package", Messages.PomTemplateContext_package); //$NON-NLS-1$
checkAndAdd(proposals, prefix, "pre-integration-test", Messages.PomTemplateContext_preintegrationtest); //$NON-NLS-1$
checkAndAdd(proposals, prefix, "integration-test", Messages.PomTemplateContext_integrationtest); //$NON-NLS-1$
checkAndAdd(proposals, prefix, "post-integration-test", Messages.PomTemplateContext_postintegrationtest); //$NON-NLS-1$
checkAndAdd(proposals, prefix, "verify", Messages.PomTemplateContext_verify); //$NON-NLS-1$
checkAndAdd(proposals, prefix, "install", Messages.PomTemplateContext_install); //$NON-NLS-1$
checkAndAdd(proposals, prefix, "deploy", Messages.PomTemplateContext_deploy); //$NON-NLS-1$
// Site Lifecycle
checkAndAdd(proposals, prefix, "pre-site", Messages.PomTemplateContext_presite); //$NON-NLS-1$
checkAndAdd(proposals, prefix, "site", Messages.PomTemplateContext_site); //$NON-NLS-1$
checkAndAdd(proposals, prefix, "post-site", Messages.PomTemplateContext_postsite); //$NON-NLS-1$
checkAndAdd(proposals, prefix, "site-deploy", Messages.PomTemplateContext_sitedeploy); //$NON-NLS-1$
}
},
GOAL("goal") { //$NON-NLS-1$
@Override
public void addTemplates(MavenProject project, IProject eclipseprj, Collection<Template> proposals, Node node,
String prefix) throws CoreException {
if(!"goals".equals(node.getParentNode().getNodeName())) { //$NON-NLS-1$
return;
}
node = node.getParentNode();
if(!"execution".equals(node.getParentNode().getNodeName())) { //$NON-NLS-1$
return;
}
node = node.getParentNode();
if(!"executions".equals(node.getParentNode().getNodeName())) { //$NON-NLS-1$
return;
}
node = node.getParentNode();
String groupId = getGroupId(node);
if(groupId == null) {
groupId = "org.apache.maven.plugins"; //$NON-NLS-1$
}
String artifactId = getArtifactId(node);
String version = extractVersion(project, eclipseprj, getVersion(node), 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();
if(mojos != null) {
for(MojoDescriptor mojo : mojos) {
checkAndAdd(proposals, prefix, mojo.getGoal(), mojo.getDescription());
}
}
}
}
},
MODULES("modules") { //$NON-NLS-1$
@Override
public void addTemplates(MavenProject project, IProject eclipseprj, Collection<Template> proposals, Node node,
String prefix) {
addModuleTemplates(project, eclipseprj, proposals, node, prefix, true);
}
},
MODULE("module") { //$NON-NLS-1$
@Override
public void addTemplates(MavenProject project, IProject eclipseprj, Collection<Template> proposals, Node node,
String prefix) {
addModuleTemplates(project, eclipseprj, proposals, node, prefix, false);
}
},
SOURCEDIRECTORY("sourceDirectory", "file"), //$NON-NLS-1$ //$NON-NLS-2$
SCRIPTSOURCEDIRECTORY("scriptSourceDirectory", "file"), //$NON-NLS-1$ //$NON-NLS-2$
TESTSOURCEDIRECTORY("testSourceDirectory", "file"), //$NON-NLS-1$ //$NON-NLS-2$
OUTPUTDIRECTORY("outputDirectory", "file"), //$NON-NLS-1$ //$NON-NLS-2$
TESTOUTPUTDIRECTORY("testOutputDirectory", "file"), //$NON-NLS-1$ //$NON-NLS-2$
DIRECTORY("directory", "file"), //$NON-NLS-1$ //$NON-NLS-2$
FILTER("filter", "file"), //$NON-NLS-1$ //$NON-NLS-2$
LICENSES("licenses"); //$NON-NLS-1$
private static final Logger log = LoggerFactory.getLogger(PomTemplateContext.class);
private static final String PREFIX = MvnIndexPlugin.PLUGIN_ID + ".templates.contextType."; //$NON-NLS-1$
private final String nodeName;
private final String contextSuffix;
private PomTemplateContext(String nodeName) {
this(nodeName, nodeName);
}
private PomTemplateContext(String nodeName, String contextSuffix) {
this.nodeName = nodeName;
this.contextSuffix = contextSuffix;
}
public boolean handlesSubtree() {
return false;
}
public boolean handlesFiles() {
return "file".equals(contextSuffix); //$NON-NLS-1$
}
/**
* Return templates depending on the context type.
*/
public Template[] getTemplates(MavenProject project, IProject eclipsePrj, Node node, String prefix) {
Collection<Template> templates = new ArrayList<Template>();
try {
addTemplates(project, eclipsePrj, templates, node, prefix);
} catch(CoreException e) {
log.error(e.getMessage(), e);
}
return templates.toArray(new Template[templates.size()]);
}
/**
* @param project
* @param eclipsePrj only here because getSearchEngine() requires it as parameter.
* @param templates
* @param currentNode
* @param prefix
* @throws CoreException
*/
protected void addTemplates(MavenProject project, IProject eclipsePrj, Collection<Template> templates,
Node currentNode, String prefix) throws CoreException {
if(handlesFiles()) {
addFileTemplates(project, eclipsePrj, templates, currentNode, prefix, name().toLowerCase().endsWith("directory"),
null);
}
}
protected FileProposalContext getFileProposalContext(MavenProject project, IProject eclipsePrj, String prefix) {
if(project == null && eclipsePrj == null) {
return null;
}
File projectDir;
if(project != null) {
projectDir = project.getFile().getParentFile();
} else {
projectDir = new File(eclipsePrj.getLocationURI());
}
String parentPath;
String prefixPath;
int lastSep = prefix.lastIndexOf('/');
if(lastSep != -1) {
prefixPath = prefix.substring(0, lastSep) + '/';
prefix = prefix.substring(lastSep + 1);
} else {
prefixPath = "";
}
String interpolated = simpleInterpolate(project, prefixPath);
parentPath = interpolated == null ? prefixPath : interpolated;
File parentDir;
if(!new File(parentPath).isAbsolute()) {
parentDir = new File(projectDir, parentPath);
} else {
parentDir = new File(parentPath);
}
if(!parentDir.isDirectory()) {
return null;
}
return new FileProposalContext(projectDir, parentDir, prefixPath, prefix);
}
protected void addFileTemplates(MavenProject project, IProject eclipsePrj, Collection<Template> templates,
Node currentNode, String prefix, boolean dirsOnly, String wrapperNode) {
FileProposalContext pctx = getFileProposalContext(project, eclipsePrj, prefix);
if(pctx == null) {
return;
}
List<File> files = Arrays.asList(pctx.parentDir.listFiles());
Collections.sort(files, Comparator.<File, Integer> comparing(r -> r.isDirectory() ? 0 : 1)
.thenComparing(Comparator.comparing(r -> r.getName())));
int rel = 4000;
for(File f : files) {
if(f.getName().startsWith(pctx.prefix)) {
String value = pctx.prefixPath + f.getName();
String template = value;
boolean retrigger = false;
if(f.isDirectory()) {
if(hasContents(f, dirsOnly)) {
template += '/';
retrigger = true;
}
} else if(dirsOnly) {
continue;
}
template = template.replace("$", "$$");
if(wrapperNode != null) {
template = '<' + wrapperNode + '>' + template + "${cursor}</" + wrapperNode + '>';
}
templates.add(new PomTemplate(f.getName(), "", getContextTypeId(), template, false).image(getFileIcon(f))
.matchValue(value).relevance(rel-- ).retriggerOnApply(retrigger));
}
}
}
private boolean hasContents(File f, boolean dirsOnly) {
// using nio is faster for large dirs compared to File#listFiles()
boolean[] res = new boolean[] {false};
Path thisPath = f.toPath();
try {
Files.walkFileTree(thisPath, new SimpleFileVisitor<Path>() {
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) {
if(thisPath.equals(dir)) {
return FileVisitResult.CONTINUE;
}
res[0] = true;
return FileVisitResult.TERMINATE;
}
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {
if(dirsOnly) {
return FileVisitResult.CONTINUE;
}
res[0] = true;
return FileVisitResult.TERMINATE;
}
});
} catch(IOException ex) {
}
return res[0];
}
protected void addModuleTemplates(MavenProject project, IProject eclipseprj, Collection<Template> proposals,
Node node, String prefix, boolean wrap) {
if(project == null) {
//shall not happen just double check.
return;
}
FileProposalContext pctx = getFileProposalContext(project, eclipseprj, prefix);
if(pctx == null) {
return;
}
//MNGECLIPSE-2204 collect the existing values from the surrounding xml content only..
// also, if it's a profile modules list, consider main modules as well
Set<String> existings = new HashSet<>();
Node moduleNode = node;
if(moduleNode != null) {
Node modulesNode;
if(moduleNode.getLocalName().equals("modules")) {
modulesNode = moduleNode;
} else {
modulesNode = moduleNode.getParentNode();
}
while(modulesNode != null) {
for(Element el : XmlUtils.findChilds((Element) modulesNode, "module")) {
if(el != moduleNode) {
String val = XmlUtils.getTextValue(el);
if(val != null) {
existings.add(val);
}
}
}
Node profileProjectNode = getAncestor(modulesNode, "profile", "profiles", "project");
if(profileProjectNode != null) {
modulesNode = getChildWithName(profileProjectNode, "modules");
} else {
modulesNode = null;
}
}
}
Set<String> subProjects = new LinkedHashSet<>();
try {
Path projectPath = pctx.projectDir.toPath().toRealPath();
Path parentPath = pctx.parentDir.toPath().toRealPath();
FileVisitor<Path> visitor = new SimpleFileVisitor<Path>() {
boolean submodulesSearch;
boolean submodulesFound;
Path submodulesSearchBase;
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) {
if(submodulesSearch && submodulesFound) {
// drop out quickly if we were searching for submodules and at least one was already found
return FileVisitResult.SKIP_SIBLINGS;
}
String name = dir.getFileName().toString();
if(name.startsWith(".")) {
// skip dotfiles
return FileVisitResult.SKIP_SUBTREE;
}
if(parentPath.equals(dir)) {
// don't propose the dir we are looking under
return FileVisitResult.CONTINUE;
}
if(projectPath.equals(dir) && pctx.prefixPath.startsWith("../")) {
// skip this project's dir entirely when looking for modules in parent dir
return FileVisitResult.SKIP_SUBTREE;
}
if(projectPath.startsWith(dir)) {
// skip ancestors of current dir
return FileVisitResult.CONTINUE;
}
//TODO polyglot?
if(Arrays.asList("src", "target", "bin").contains(name) && dir.resolve("../pom.xml").toFile().exists()) {
// skip recursing into certain the project dirs
return FileVisitResult.SKIP_SUBTREE;
}
if(dir.resolve("pom.xml").toFile().exists()) {
if(submodulesSearch) {
// we were looking for submodules and found at least one
submodulesFound = true;
return FileVisitResult.SKIP_SIBLINGS;
}
// found a candidate
String path = projectPath.relativize(dir).toString().replace('\\', '/');
if(!existings.contains(path)) {
subProjects.add(path);
}
// now we need to check for submodules, and if there are any, add a <path>/ proposal
submodulesSearch = true;
submodulesSearchBase = dir;
submodulesFound = false;
}
return FileVisitResult.CONTINUE;
}
public FileVisitResult postVisitDirectory(Path dir, IOException e) {
if(submodulesSearch && dir.equals(submodulesSearchBase)) {
// finish search for submodules
if(submodulesFound) {
String path = projectPath.relativize(dir).toString().replace('\\', '/');
subProjects.add(path + '/');
}
submodulesSearch = false;
submodulesSearchBase = null;
submodulesFound = false;
}
return FileVisitResult.CONTINUE;
}
};
Files.walkFileTree(parentPath, EnumSet.of(FileVisitOption.FOLLOW_LINKS), 5, visitor);
} catch(IOException e) {
}
subProjects.removeAll(existings);
int moduleRel = 8000;
int submoduleRel = 4000;
for(String path : subProjects) {
if(path.startsWith(prefix)) {
String value = path;
String template = value;
template = template.replace("$", "$$");
Image image;
String description;
int rel;
boolean retrigger = false;
if(path.endsWith("/")) {
image = MvnImages.IMG_DISCOVERY;
description = NLS.bind(Messages.PomTemplateContext_submodules, path);
rel = submoduleRel-- ;
retrigger = true;
} else {
image = getFileIcon(new File(pctx.projectDir, path));
description = NLS.bind(Messages.PomTemplateContext_module, path);
rel = moduleRel-- ;
}
if(wrap) {
template = "<module>" + template + "${cursor}</module>";
}
proposals.add(new PomTemplate(value, description, getContextTypeId(), template, false).image(image)
.relevance(rel).retriggerOnApply(retrigger));
}
}
}
protected static Image getFileIcon(File f) {
IWorkspaceRoot wroot = ResourcesPlugin.getWorkspace().getRoot();
IResource[] resources;
if(f.isDirectory()) {
resources = wroot.findContainersForLocationURI(f.toURI());
} else {
resources = wroot.findFilesForLocationURI(f.toURI());
}
IResource res = resources.length > 0 ? resources[0] : null;
if(res != null) {
IWorkbenchAdapter wbAdapter = res.getAdapter(IWorkbenchAdapter.class);
if(wbAdapter == null) {
return null;
}
ImageDescriptor id = wbAdapter.getImageDescriptor(res);
return id != null ? MvnImages.getImage(id) : null;
}
if(f.isDirectory()) {
return PlatformUI.getWorkbench().getSharedImages().getImage(ISharedImages.IMG_OBJ_FOLDER);
}
return PlatformUI.getWorkbench().getSharedImages().getImage(ISharedImages.IMG_OBJ_FILE);
}
protected String getNodeName() {
return nodeName;
}
public String getContextTypeId() {
return PREFIX + contextSuffix;
}
public static PomTemplateContext fromId(String contextTypeId) {
for(PomTemplateContext context : values()) {
if(context.getContextTypeId().equals(contextTypeId)) {
return context;
}
}
return UNKNOWN;
}
public static PomTemplateContext fromNodeName(String idSuffix) {
for(PomTemplateContext context : values()) {
if(context.getNodeName().equals(idSuffix)) {
return context;
}
}
return UNKNOWN;
}
public static PomTemplateContext fromNode(Node node) {
PomTemplateContext context = PomTemplateContext.fromNodeName(node.getNodeName());
// 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;
}
return context;
}
protected static SearchEngine getSearchEngine(IProject project) throws CoreException {
if(searchEngineForTests != null) {
return searchEngineForTests;
}
return M2EUIPluginActivator.getDefault().getSearchEngine(project);
}
private static SearchEngine searchEngineForTests;
public static void setSearchEngineForTests(SearchEngine _searchEngineForTests) {
searchEngineForTests = _searchEngineForTests;
}
/**
* Returns containing artifactInfo for exclusions. Otherwise returns null.
*/
protected ArtifactInfo getContainingArtifact(Node currentNode) {
if(isExclusion(currentNode)) {
Node node = currentNode.getParentNode().getParentNode();
return getArtifactInfo(node);
}
return null;
}
/**
* Returns artifact info from siblings of given node.
*/
private ArtifactInfo getArtifactInfo(Node node) {
return new ArtifactInfo(getGroupId(node), getArtifactId(node), getVersion(node), //
getSiblingTextValue(node, "classifier"), getSiblingTextValue(node, "type")); //$NON-NLS-1$ //$NON-NLS-2$
}
/**
* Returns required packaging.
*/
protected Packaging getPackaging(Node currentNode) {
if(isPlugin(currentNode)) {
return Packaging.PLUGIN;
} else if(isParent(currentNode)) {
return Packaging.POM;
}
return Packaging.ALL;
}
/**
* Returns true if user is editing plugin dependency.
*/
private boolean isPlugin(Node currentNode) {
return "plugin".equals(currentNode.getParentNode().getNodeName()); //$NON-NLS-1$
}
/**
* Returns true if user is editing plugin dependency exclusion.
*/
private boolean isExclusion(Node currentNode) {
return "exclusion".equals(currentNode.getParentNode().getNodeName()); //$NON-NLS-1$
}
/**
* Returns true if user is editing parent dependency.
*/
private boolean isParent(Node currentNode) {
return "parent".equals(currentNode.getParentNode().getNodeName()); //$NON-NLS-1$
}
protected String getGroupId(Node currentNode) {
return getSiblingTextValue(currentNode, "groupId"); //$NON-NLS-1$
}
protected void checkAndAdd(Collection<Template> proposals, String prefix, String name) {
checkAndAdd(proposals, prefix, name, name);
}
protected void checkAndAdd(Collection<Template> proposals, String prefix, String name, String description) {
checkAndAdd(proposals, prefix, name, name, -1);
}
protected void checkAndAdd(Collection<Template> proposals, String prefix, String name, String description, int rel) {
if(name.startsWith(prefix)) {
proposals.add(new PomTemplate(name, description, getContextTypeId(), name, false).relevance(rel));
}
}
/**
* @param project
* @param version
* @param groupId
* @param artifactId
* @return
* @throws CoreException
*/
static int EXTRACT_STRATEGY_PLUGIN = 1;
static int EXTRACT_STRATEGY_DEPENDENCY = 2;
static int EXTRACT_STRATEGY_SEARCH = 4;
static String extractVersion(MavenProject mp, IProject project, String version, String groupId, String artifactId,
int strategy) throws CoreException {
assert mp != null;
version = simpleInterpolate(mp, version);
if(version == null) {
Packaging pack = Packaging.ALL;
if((strategy & EXTRACT_STRATEGY_PLUGIN) != 0) {
version = searchPM(mp, groupId, artifactId);
pack = Packaging.PLUGIN;
}
if((strategy & EXTRACT_STRATEGY_DEPENDENCY) != 0) {
version = searchDM(mp, groupId, artifactId);
}
if(version == null && (strategy & EXTRACT_STRATEGY_SEARCH) != 0) {
Collection<String> versions = getSearchEngine(project).findVersions(groupId, artifactId, "", pack); //$NON-NLS-1$
if(versions.isEmpty()) {
return null;
}
version = versions.iterator().next();
}
}
return version;
}
// TODO copy of this resides in FormUtils
static String simpleInterpolate(MavenProject project, String text) {
if(text != null && text.contains("${")) { //$NON-NLS-1$
//when expression is in the version but no project instance around
// just give up.
if(project == null) {
return null;
}
Properties props = project.getProperties();
RegexBasedInterpolator inter = new RegexBasedInterpolator();
if(props != null) {
inter.addValueSource(new PropertiesBasedValueSource(props));
}
inter.addValueSource(
new PrefixedObjectValueSource(Arrays.asList(new String[] {"pom.", "project."}), project.getModel(), false)); //$NON-NLS-1$ //$NON-NLS-2$
try {
text = inter.interpolate(text);
} catch(InterpolationException e) {
text = null;
}
}
return text;
}
static String searchPM(MavenProject project, String groupId, String artifactId) {
if(project == null) {
return null;
}
String version = null;
String id = Plugin.constructKey(groupId, artifactId);
PluginManagement pm = project.getPluginManagement();
if(pm != null) {
for(Plugin pl : pm.getPlugins()) {
if(id.equals(pl.getKey())) {
version = pl.getVersion();
break;
}
}
}
return version;
}
static String searchDM(MavenProject project, String groupId, String artifactId) {
if(project == null) {
return null;
}
String version = null;
//see if we can find the dependency is in dependency management of resolved project.
String id = groupId + ":" + artifactId + ":";
DependencyManagement dm = project.getDependencyManagement();
if(dm != null) {
for(Dependency dep : dm.getDependencies()) {
if(dep.getManagementKey().startsWith(id)) {
version = dep.getVersion();
break;
}
}
}
return version;
}
protected static String getArtifactId(Node currentNode) {
return getSiblingTextValue(currentNode, "artifactId"); //$NON-NLS-1$
}
protected static String getVersion(Node currentNode) {
return getSiblingTextValue(currentNode, "version"); //$NON-NLS-1$
}
private static String getSiblingTextValue(Node sibling, String name) {
Node node = getSiblingWithName(sibling, name);
return XmlUtils.getTextValue(node);
}
/**
* Returns sibling with given name.
*/
private static Node getSiblingWithName(Node node, String name) {
return getChildWithName(node.getParentNode(), name);
}
/**
* Returns child with given name
*/
protected 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);
}
}
return null;
}
protected static Node getAncestor(Node node, String... names) {
int i = 0;
for(; i < names.length; i++ ) {
Node parent = node.getParentNode();
if(parent == null || !names[i].equals(parent.getNodeName()))
return null;
node = parent;
}
return i == names.length ? node : null;
}
private static class FileProposalContext {
final File projectDir;
final File parentDir;
final String prefixPath;
final String prefix;
FileProposalContext(File projectDir, File parentDir, String prefixPath, String prefix) {
this.projectDir = projectDir;
this.parentDir = parentDir;
this.prefixPath = prefixPath;
this.prefix = prefix;
}
}
}