blob: 9d478fb01312b2fa6579378b2497bf0a59db24d4 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2016 Anton Tanasenko.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Anton Tanasenko - initial API and implementation
*******************************************************************************/
package org.eclipse.m2e.core.internal.lifecyclemapping;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Deque;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import com.google.common.base.CharMatcher;
import com.google.common.base.Splitter;
import com.google.common.collect.ImmutableMap;
import org.codehaus.plexus.util.ReaderFactory;
import org.codehaus.plexus.util.xml.Xpp3Dom;
import org.codehaus.plexus.util.xml.pull.MXParser;
import org.codehaus.plexus.util.xml.pull.XmlPullParser;
import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
import org.apache.maven.model.InputLocation;
import org.apache.maven.model.InputLocationTracker;
import org.apache.maven.model.InputSource;
import org.apache.maven.model.Plugin;
import org.apache.maven.model.PluginContainer;
import org.apache.maven.model.PluginExecution;
import org.apache.maven.project.MavenProject;
import org.eclipse.m2e.core.internal.Messages;
import org.eclipse.m2e.core.internal.lifecyclemapping.model.LifecycleMappingMetadata;
import org.eclipse.m2e.core.internal.lifecyclemapping.model.LifecycleMappingMetadataSource;
import org.eclipse.m2e.core.internal.lifecyclemapping.model.PluginExecutionFilter;
import org.eclipse.m2e.core.internal.lifecyclemapping.model.PluginExecutionMetadata;
import org.eclipse.m2e.core.internal.markers.SourceLocation;
import org.eclipse.m2e.core.lifecyclemapping.model.PluginExecutionAction;
import org.eclipse.m2e.core.project.configurator.MojoExecutionKey;
/**
* AnnotationMappingMetadataSource
*
* @author atgsl
*/
public class AnnotationMappingMetadataSource implements MappingMetadataSource {
private static final String SELF = ""; //$NON-NLS-1$
private final MavenProject project;
private final String projectId;
private final List<PI> pis;
public static AnnotationMappingMetadataSource get(MavenProject project) {
List<PI> pis = parsePIs(project);
if(!pis.isEmpty()) {
return new AnnotationMappingMetadataSource(project, pis);
}
return null;
}
private AnnotationMappingMetadataSource(MavenProject project, List<PI> pis) {
this.project = project;
this.pis = pis;
projectId = project.getModel().getLocation(SELF).getSource().getModelId();
}
public List<PluginExecutionMetadata> getPluginExecutionMetadata(MojoExecutionKey execution) {
Xpp3Dom action = getAction(execution);
if(action != null) {
return Collections.singletonList(createMetadata(action));
}
return Collections.emptyList();
}
private Xpp3Dom getAction(MojoExecutionKey execution) {
String key = Plugin.constructKey(execution.getGroupId(), execution.getArtifactId());
Plugin plugin = getPlugin(project.getBuild(), key);
Plugin mplugin = getPlugin(project.getPluginManagement(), key);
String executionId = execution.getExecutionId();
if(executionId != null) {
// find pi for the executionId
if(plugin != null) {
PluginExecution pluginExecution = plugin.getExecutionsAsMap().get(executionId);
if(pluginExecution != null) {
Xpp3Dom action = getAction(pluginExecution);
if(action != null)
return action;
}
}
if(mplugin != null) {
PluginExecution pluginExecution = mplugin.getExecutionsAsMap().get(executionId);
if(pluginExecution != null) {
Xpp3Dom action = getAction(pluginExecution);
if(action != null)
return action;
}
}
// find pi for the whole plugin
if(plugin != null) {
Xpp3Dom action = getAction(plugin);
if(action != null)
return action;
}
if(mplugin != null) {
Xpp3Dom action = getAction(mplugin);
if(action != null)
return action;
}
}
return null;
}
private Plugin getPlugin(PluginContainer plugins, String key) {
return plugins == null ? null : plugins.getPluginsAsMap().get(key);
}
public LifecycleMappingMetadata getLifecycleMappingMetadata(String packagingType) throws DuplicateMappingException {
return null;
}
private Xpp3Dom getAction(InputLocationTracker tracker) {
InputLocation location = tracker.getLocation(SELF);
if(location != null && location.getSource() != null && projectId.equals(location.getSource().getModelId())) {
int l = location.getLineNumber();
int c = location.getColumnNumber();
for(PI pi : pis) {
if(pi.l == l && pi.c == c) {
return pi.action;
}
}
}
return null;
}
private PluginExecutionMetadata createMetadata(Xpp3Dom action) {
Xpp3Dom actionDom = new Xpp3Dom("action"); //$NON-NLS-1$
actionDom.addChild(action);
PluginExecutionMetadata md = new PluginExecutionMetadata();
md.setActionDom(actionDom);
LifecycleMappingMetadataSource source = new LifecycleMappingMetadataSource();
source.setSource(project);
md.setSource(source);
md.setFilter(new PluginExecutionFilter());
return md;
}
private static List<PI> parsePIs(MavenProject project) {
File pom = project.getFile();
InputSource source = project.getModel().getLocation(SELF).getSource();
List<PI> pis = new ArrayList<>();
XmlPullParser parser = new MXParser();
try (InputStream in = new FileInputStream(pom)) {
parser.setInput(ReaderFactory.newXmlReader(in));
Deque<State> stack = new LinkedList<>();
int eventType = parser.getEventType();
while(eventType != XmlPullParser.END_DOCUMENT) {
if(eventType == XmlPullParser.START_TAG) {
stack.push(new State(parser.getLineNumber(), parser.getColumnNumber()));
} else if(eventType == XmlPullParser.END_TAG) {
stack.pop();
} else if(eventType == XmlPullParser.PROCESSING_INSTRUCTION && !stack.isEmpty()) {
String text = parser.getText();
if(text.startsWith("m2e ")) { //$NON-NLS-1$
// found it
Xpp3Dom dom = parse(text.substring(4));
if(dom == null) {
SourceLocation location = new SourceLocation(source.getLocation(), source.getModelId(),
parser.getLineNumber(), parser.getColumnNumber(), text.length() + 4);
throw new LifecycleMappingConfigurationException(Messages.AnnotationMappingMetadataSource_UnsupportedInstructionFormat, location);
}
State s = stack.peek();
PI pi = new PI(s.l, s.c, dom);
pis.add(pi);
}
}
eventType = parser.nextToken();
}
} catch(XmlPullParserException | IOException ex) {
SourceLocation location = new SourceLocation(source.getLocation(), source.getModelId(), parser.getLineNumber(),
parser.getColumnNumber(), 1);
throw new LifecycleMappingConfigurationException(Messages.AnnotationMappingMetadataSource_ErrorParsingInstruction, location);
}
return pis;
}
private static final Splitter PI_SPLITTER = Splitter.on(CharMatcher.whitespace()).omitEmptyStrings().limit(2);
private static final Splitter EXECUTE_SPLITTER = Splitter.on(',').omitEmptyStrings();
private static final Map<String, String> EXECUTE_OPTIONS = new ImmutableMap.Builder<String, String>()
.put("onConfiguration", LifecycleMappingFactory.ELEMENT_RUN_ON_CONFIGURATION) //$NON-NLS-1$
.put("onIncremental", LifecycleMappingFactory.ELEMENT_RUN_ON_INCREMENTAL).build(); //$NON-NLS-1$
private static Xpp3Dom parse(String pi) {
List<String> split = PI_SPLITTER.splitToList(pi);
PluginExecutionAction a = getAction(split.get(0));
if(a == null) {
return null;
}
switch(a) {
case ignore:
return new Xpp3Dom("ignore"); //$NON-NLS-1$
case configurator:
if(split.size() != 2) {
return null;
}
Xpp3Dom conf = new Xpp3Dom("configurator"); //$NON-NLS-1$
Xpp3Dom id = new Xpp3Dom("id"); //$NON-NLS-1$
id.setValue(split.get(1));
conf.addChild(id);
return conf;
case execute:
Xpp3Dom exec = new Xpp3Dom("execute"); //$NON-NLS-1$
if(split.size() > 1) {
for(String option : EXECUTE_SPLITTER.split(split.get(1))) {
String value = EXECUTE_OPTIONS.get(option);
if(value == null) {
return null;
}
Xpp3Dom opt = new Xpp3Dom(value);
opt.setValue("true"); //$NON-NLS-1$
exec.addChild(opt);
}
}
return exec;
default:
return null;
}
}
private static PluginExecutionAction getAction(String value) {
for(PluginExecutionAction a : PluginExecutionAction.values()) {
if(value.toLowerCase().equals(a.name())) {
return a;
}
}
return null;
}
private static class State {
final int l;
final int c;
State(int l, int c) {
this.l = l;
this.c = c;
}
}
private static class PI {
final int l;
final int c;
final Xpp3Dom action;
PI(int l, int c, Xpp3Dom action) {
this.l = l;
this.c = c;
this.action = action;
}
}
}