diff options
Diffstat (limited to 'org.eclipse.m2e.editor.xml/src')
22 files changed, 4720 insertions, 0 deletions
diff --git a/org.eclipse.m2e.editor.xml/src/main/java/org/eclipse/m2e/editor/xml/InsertArtifactProposal.java b/org.eclipse.m2e.editor.xml/src/main/java/org/eclipse/m2e/editor/xml/InsertArtifactProposal.java new file mode 100644 index 00000000..27ddffb9 --- /dev/null +++ b/org.eclipse.m2e.editor.xml/src/main/java/org/eclipse/m2e/editor/xml/InsertArtifactProposal.java @@ -0,0 +1,340 @@ +package org.eclipse.m2e.editor.xml; + +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + +import org.apache.maven.model.Plugin; +import org.apache.maven.model.PluginManagement; +import org.apache.maven.project.MavenProject; +import org.w3c.dom.Element; +import org.w3c.dom.Node; + +import org.eclipse.core.resources.IProject; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.jface.text.BadLocationException; +import org.eclipse.jface.text.IDocument; +import org.eclipse.jface.text.Region; +import org.eclipse.jface.text.contentassist.ICompletionProposal; +import org.eclipse.jface.text.contentassist.ICompletionProposalExtension4; +import org.eclipse.jface.text.contentassist.ICompletionProposalExtension5; +import org.eclipse.jface.text.contentassist.IContextInformation; +import org.eclipse.jface.text.formatter.IContentFormatter; +import org.eclipse.jface.text.formatter.IContentFormatterExtension; +import org.eclipse.jface.text.source.ISourceViewer; +import org.eclipse.jface.window.Window; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.graphics.Point; +import org.eclipse.wst.sse.core.internal.provisional.IndexedRegion; + +import org.eclipse.m2e.core.MavenPlugin; +import org.eclipse.m2e.core.core.MavenLogger; +import org.eclipse.m2e.core.embedder.ArtifactKey; +import org.eclipse.m2e.core.index.IIndex; +import org.eclipse.m2e.core.index.IndexedArtifactFile; +import org.eclipse.m2e.core.internal.project.MavenMarkerManager; +import org.eclipse.m2e.core.project.IMavenProjectFacade; +import org.eclipse.m2e.core.ui.dialogs.MavenRepositorySearchDialog; +import org.eclipse.m2e.editor.xml.InsertArtifactProposal.Configuration; +import org.eclipse.m2e.editor.xml.internal.Messages; + +public class InsertArtifactProposal implements ICompletionProposal, ICompletionProposalExtension4, ICompletionProposalExtension5 { + + private ISourceViewer sourceViewer; + private Region region; + private int generatedLength = 0; + private int generatedOffset; + private Configuration config; + private PomStructuredTextViewConfiguration textConfig; + + public InsertArtifactProposal(ISourceViewer sourceViewer, Region region, Configuration config, PomStructuredTextViewConfiguration config2) { + this.sourceViewer = sourceViewer; + this.region = region; + generatedOffset = region.getOffset(); + this.config = config; + this.textConfig = config2; + assert config.getType() != null; + } + + public void apply(IDocument document) { + IProject prj = PomContentAssistProcessor.extractProject(sourceViewer); + Set<ArtifactKey> managedKeys = new HashSet<ArtifactKey>(); + Set<ArtifactKey> usedKeys = new HashSet<ArtifactKey>(); + if (prj != null) { + IMavenProjectFacade facade = MavenPlugin.getDefault().getMavenProjectManager().getProject(prj); + if (facade != null) { + MavenProject mp = facade.getMavenProject(); + if (mp != null) { + PluginManagement pm = mp.getPluginManagement(); + if (pm != null && pm.getPlugins() != null) { + for (Plugin plug : pm.getPlugins()) { + managedKeys.add(new ArtifactKey(plug.getGroupId(), plug.getArtifactId(), plug.getVersion(), null)); + } + } + } + } + } + //TODO also collect the used plugin's list + + MavenRepositorySearchDialog dialog = new MavenRepositorySearchDialog(sourceViewer.getTextWidget().getShell(), + config.getType().getWindowTitle(), config.getType().getIIndexType(), + usedKeys, managedKeys, false); + if (config.getInitiaSearchString() != null) { + dialog.setQuery(config.getInitiaSearchString()); + } + if(dialog.open() == Window.OK) { + String lineDelim = document.getLegalLineDelimiters()[0];//do we care? or just append \n always? + IndexedArtifactFile af = (IndexedArtifactFile) dialog.getFirstResult(); + int offset = region.getOffset(); + if(af != null) { + if (config.getType() == SearchType.PARENT) { + try { + StringBuffer buffer = new StringBuffer(); + buffer.append("<parent>").append(lineDelim); //$NON-NLS-1$ + buffer.append("<groupId>").append(af.group).append("</groupId>").append(lineDelim); //$NON-NLS-1$ //$NON-NLS-2$ + buffer.append("<artifactId>").append(af.artifact).append("</artifactId>").append(lineDelim); //$NON-NLS-1$ //$NON-NLS-2$ + buffer.append("<version>").append(af.version).append("</version>").append(lineDelim); //$NON-NLS-1$ //$NON-NLS-2$ + String relativePath = PomContentAssistProcessor.findRelativePath(sourceViewer, af.group, af.artifact, af.version); + if (relativePath != null) { + buffer.append("<relativePath>").append(relativePath).append("</relativePath>").append(lineDelim); //$NON-NLS-1$ //$NON-NLS-2$ + } + buffer.append("</parent>").append(lineDelim); //$NON-NLS-1$ + generatedLength = buffer.toString().length(); + document.replace(offset, region.getLength(), buffer.toString()); + + IContentFormatter formatter = textConfig.getContentFormatter(sourceViewer); + Region resRegion = format(formatter, document, generatedOffset, generatedLength); + generatedOffset = resRegion.getOffset(); + generatedLength = resRegion.getLength(); + } catch(BadLocationException e) { + MavenLogger.log("Failed inserting parent element", e); //$NON-NLS-1$ + } + } + if (config.getType() == SearchType.PLUGIN) { + Node current = config.getCurrentNode(); + if ("project".equals(current.getNodeName())) { //$NON-NLS-1$ + //in project section go with build/plugins. + Element build = MavenMarkerManager.findChildElement((Element)current, "build"); //$NON-NLS-1$ + if (build == null) { + try { + StringBuffer buffer = new StringBuffer(); + generateBuild(buffer, lineDelim, af, skipVersion(current, af, managedKeys)); + generatedLength = buffer.toString().length(); + document.replace(offset, 0, buffer.toString()); + + IContentFormatter formatter = textConfig.getContentFormatter(sourceViewer); + Region resRegion = format(formatter, document, generatedOffset, generatedLength); + generatedOffset = resRegion.getOffset(); + generatedLength = resRegion.getLength(); + } catch (BadLocationException e) { + MavenLogger.log("Failed inserting build element", e); //$NON-NLS-1$ + } + return; + } else { + current = build; + IndexedRegion reg = (IndexedRegion)current; + //we need to update the offset to where we found the existing build element.. + offset = reg.getEndOffset() - "</build>".length(); //$NON-NLS-1$ + } + } + if ("build".equals(current.getNodeName()) || "pluginManagement".equals(current.getNodeName())) { //$NON-NLS-1$ //$NON-NLS-2$ + Element plugins = MavenMarkerManager.findChildElement((Element)current, "plugins"); //$NON-NLS-1$ + if (plugins == null) { + //we need to create it. + try { + StringBuffer buffer = new StringBuffer(); + generatePlugins(buffer, lineDelim, af, skipVersion(current, af, managedKeys)); + generatedLength = buffer.toString().length(); + document.replace(offset, 0, buffer.toString()); + + IContentFormatter formatter = textConfig.getContentFormatter(sourceViewer); + Region resRegion = format(formatter, document, offset, generatedLength); + generatedOffset = resRegion.getOffset(); + generatedLength = resRegion.getLength(); + } catch (BadLocationException e) { + MavenLogger.log("Failed inserting plugins element", e); //$NON-NLS-1$ + } + return; + } else { + current = plugins; + IndexedRegion reg = (IndexedRegion)current; + //we need to update the offset to where we found the existing plugins element.. + offset = reg.getEndOffset() - "</plugins>".length(); //$NON-NLS-1$ + } + } + if ("plugins".equals(current.getNodeName())) { //$NON-NLS-1$ + //simple, just add the plugin here.. + //TODO we might want to look if the plugin is already defined in this section or not.. + try { + StringBuffer buffer = new StringBuffer(); + generatePlugin(buffer, lineDelim, af, skipVersion(current, af, managedKeys)); + generatedLength = buffer.toString().length(); + document.replace(offset, 0, buffer.toString()); + IContentFormatter formatter = textConfig.getContentFormatter(sourceViewer); + Region resRegion = format(formatter, document, offset, generatedLength); + generatedOffset = resRegion.getOffset(); + generatedLength = resRegion.getLength(); + } catch (BadLocationException e) { + MavenLogger.log("Failed inserting plugin element", e); //$NON-NLS-1$ + } + } + } + } + } + } + + /** + * decide if we want to generate the version element or not.. + * @param currentNode + * @param af + * @param managedList + * @return + */ + private boolean skipVersion(Node currentNode, IndexedArtifactFile af, Set<ArtifactKey> managedList) { + if ("pluginManagement".equals(currentNode.getNodeName()) || "pluginManagement".equals(currentNode.getParentNode().getNodeName())) { + return false; + } + ArtifactKey key = new ArtifactKey(af.group, af.artifact, af.version, null); + return managedList.contains(key); + } + + private void generatePlugin(StringBuffer buffer, String lineDelim, IndexedArtifactFile af, boolean skipVersion) { + buffer.append("<plugin>").append(lineDelim); //$NON-NLS-1$ + buffer.append("<groupId>").append(af.group).append("</groupId>").append(lineDelim); //$NON-NLS-1$ //$NON-NLS-2$ + buffer.append("<artifactId>").append(af.artifact).append("</artifactId>").append(lineDelim); //$NON-NLS-1$ //$NON-NLS-2$ + //for managed plugins (if version matches only?), don't add the version element + if (!skipVersion) { + buffer.append("<version>").append(af.version).append("</version>").append(lineDelim); //$NON-NLS-1$ //$NON-NLS-2$ + } + buffer.append("</plugin>").append(lineDelim); //$NON-NLS-1$ + } + + private void generatePlugins(StringBuffer buffer, String lineDelim, IndexedArtifactFile af, boolean skipVersion) { + buffer.append("<plugins>").append(lineDelim); //$NON-NLS-1$ + generatePlugin(buffer, lineDelim, af, skipVersion); + buffer.append("</plugins>").append(lineDelim); //$NON-NLS-1$ + } + + private void generateBuild(StringBuffer buffer, String lineDelim, IndexedArtifactFile af, boolean skipVersion) { + buffer.append("<build>").append(lineDelim); //$NON-NLS-1$ + generatePlugins(buffer, lineDelim, af, skipVersion); + buffer.append("</build>").append(lineDelim); //$NON-NLS-1$ + } + + /** + * take the document and format the region specified by the supplied formatter. + * operates on whole line (determined by the region specified) + * returns the new region encompassing the original region after formatting + */ + public static Region format(IContentFormatter formatter, IDocument document, int offset, int length) throws BadLocationException { + int startLine = document.getLineOfOffset(offset); + int endLine = document.getLineOfOffset(offset + length - 1); // -1 to make sure to be before the end of line char + int startLineOffset = document.getLineOffset(startLine); + formatter.format(document, new Region(startLineOffset, (document.getLineOffset(endLine) + document.getLineLength(endLine)) - startLineOffset)); + startLineOffset = document.getLineOffset(startLine); //should be same, just being paranoid + return new Region (startLineOffset, (document.getLineOffset(endLine) + document.getLineLength(endLine)) - startLineOffset); + } + + public Point getSelection(IDocument document) { + return new Point(generatedOffset, generatedLength); + } + + public String getAdditionalProposalInfo() { + return null; //not to be used anymore + } + + public String getDisplayString() { + return config.getType().getDisplayName(); + } + + public Image getImage() { + return config.getType().getImage(); + } + + public IContextInformation getContextInformation() { + // TODO Auto-generated method stub + return null; + } + + public boolean isAutoInsertable() { + return false; + } + + public Object getAdditionalProposalInfo(IProgressMonitor monitor) { + return config.getType().getAdditionalInfo(); + } + + /** + * supported search types + * @author mkleint + * + */ + public static enum SearchType { + + PARENT(IIndex.SEARCH_PARENTS, Messages.InsertArtifactProposal_searchDialog_title, Messages.InsertArtifactProposal_display_name, MvnImages.IMG_OPEN_POM, Messages.InsertArtifactProposal_additionals), + PLUGIN(IIndex.SEARCH_PLUGIN, Messages.InsertArtifactProposal_insert_plugin_title, Messages.InsertArtifactProposal_insert_plugin_display_name, MvnImages.IMG_OPEN_POM, Messages.InsertArtifactProposal_insert_plugin_description); + + private final String type; + private final String windowTitle; + private final String displayName; + private final Image image; + private final String additionalInfo; + private SearchType(String type, String windowTitle, String dn, Image img, String addInfo) { + this.type = type; + this.windowTitle = windowTitle; + this.displayName = dn; + this.image = img; + this.additionalInfo = addInfo; + } + + String getIIndexType() { + return type; + } + + public String getWindowTitle() { + return windowTitle; + } + + public String getDisplayName() { + return displayName; + } + + public Image getImage() { + return image; + } + + public String getAdditionalInfo() { + return additionalInfo; + } + + } + + public static class Configuration { + private final SearchType type; + private String initiaSearchString; + private Node node; + + public Configuration(SearchType type) { + this.type = type; + } + + public void setInitiaSearchString(String initiaSearchString) { + this.initiaSearchString = initiaSearchString; + } + public String getInitiaSearchString() { + return initiaSearchString; + } + public SearchType getType() { + return type; + } + + public void setCurrentNode(Node node) { + this.node = node; + } + + public Node getCurrentNode() { + return node; + } + } + +} diff --git a/org.eclipse.m2e.editor.xml/src/main/java/org/eclipse/m2e/editor/xml/InsertExpressionProposal.java b/org.eclipse.m2e.editor.xml/src/main/java/org/eclipse/m2e/editor/xml/InsertExpressionProposal.java new file mode 100644 index 00000000..1424e7b6 --- /dev/null +++ b/org.eclipse.m2e.editor.xml/src/main/java/org/eclipse/m2e/editor/xml/InsertExpressionProposal.java @@ -0,0 +1,110 @@ +package org.eclipse.m2e.editor.xml; + +import org.apache.maven.model.InputLocation; +import org.apache.maven.model.InputSource; +import org.apache.maven.model.Model; +import org.apache.maven.project.MavenProject; + +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.jface.text.BadLocationException; +import org.eclipse.jface.text.IDocument; +import org.eclipse.jface.text.Region; +import org.eclipse.jface.text.contentassist.ICompletionProposal; +import org.eclipse.jface.text.contentassist.ICompletionProposalExtension5; +import org.eclipse.jface.text.contentassist.IContextInformation; +import org.eclipse.jface.text.source.ISourceViewer; +import org.eclipse.osgi.util.NLS; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.graphics.Point; + +import org.eclipse.m2e.core.project.IMavenProjectFacade; +import org.eclipse.m2e.editor.xml.internal.Messages; +/** + * insertion proposal for ${ expressions + * @author mkleint + * + */ +public class InsertExpressionProposal implements ICompletionProposal, ICompletionProposalExtension5 { + + private IMavenProjectFacade project; + private String key; + private Region region; + private ISourceViewer sourceViewer; + private int len = 0; + + public InsertExpressionProposal(ISourceViewer sourceViewer, Region region, String key, IMavenProjectFacade mvnproject) { + assert project != null; + this.sourceViewer = sourceViewer; + this.region = region; + this.key = key; + this.project = mvnproject; + } + + public Object getAdditionalProposalInfo(IProgressMonitor monitor) { + String value = PomTemplateContext.simpleInterpolate(project.getProject(), "${" + key + "}"); //$NON-NLS-1$ //$NON-NLS-2$ + MavenProject mavprj = project.getMavenProject(); + String loc = null; + if (mavprj != null) { + Model mdl = mavprj.getModel(); + if (mdl.getProperties() != null && mdl.getProperties().containsKey(key)) { + if (mdl.getLocation("properties") != null) { + InputLocation location = mdl.getLocation("properties").getLocation(key); //$NON-NLS-1$ + if (location != null) { + //MNGECLIPSE-2539 apparently you can have an InputLocation with null input source. + // check! + InputSource source = location.getSource(); + if (source != null) { + loc = source.getModelId(); + } + } + } + } + } + StringBuffer buff = new StringBuffer(); + buff.append("<html>"); //$NON-NLS-1$ + if (value != null) { + buff.append(NLS.bind(Messages.InsertExpressionProposal_hint1, value)); + } + if (loc != null) { + buff.append(NLS.bind(Messages.InsertExpressionProposal_hint2, loc)); + } + buff.append("</html>"); //$NON-NLS-1$ + return buff.toString(); + } + + public void apply(IDocument document) { + int offset = region.getOffset(); + String replace = "${" + key + "}"; //$NON-NLS-1$ //$NON-NLS-2$ + try { + document.replace(offset, region.getLength(), replace); + len = replace.length(); + } catch(BadLocationException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + } + + public Point getSelection(IDocument document) { + return new Point(region.getOffset() + len, 0); + } + + public String getAdditionalProposalInfo() { + //not used anymore + return null; + } + + public String getDisplayString() { + return "${" + key + "}"; //$NON-NLS-1$ //$NON-NLS-2$ + } + + public Image getImage() { + // TODO what kind of icon to use? + return null; + } + + public IContextInformation getContextInformation() { + return null; + } + +} diff --git a/org.eclipse.m2e.editor.xml/src/main/java/org/eclipse/m2e/editor/xml/MavenMarkerResolutionGenerator.java b/org.eclipse.m2e.editor.xml/src/main/java/org/eclipse/m2e/editor/xml/MavenMarkerResolutionGenerator.java new file mode 100644 index 00000000..351e8ec5 --- /dev/null +++ b/org.eclipse.m2e.editor.xml/src/main/java/org/eclipse/m2e/editor/xml/MavenMarkerResolutionGenerator.java @@ -0,0 +1,65 @@ +/******************************************************************************* + * 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 org.eclipse.core.resources.IMarker; +import org.eclipse.ui.IMarkerResolution; +import org.eclipse.ui.IMarkerResolutionGenerator; +import org.eclipse.ui.IMarkerResolutionGenerator2; + +import org.eclipse.m2e.core.core.IMavenConstants; + + +/** + * MavenMarkerResolutionGenerator + * + * @author dyocum + */ +public class MavenMarkerResolutionGenerator implements IMarkerResolutionGenerator, IMarkerResolutionGenerator2 { + + /* (non-Javadoc) + * @see org.eclipse.ui.IMarkerResolutionGenerator#getResolutions(org.eclipse.core.resources.IMarker) + */ + public IMarkerResolution[] getResolutions(IMarker marker) { + String hint = marker.getAttribute(IMavenConstants.MARKER_ATTR_EDITOR_HINT, null); + if(hint != null) { + //only provide a quickfix for the schema marker + if(IMavenConstants.EDITOR_HINT_MISSING_SCHEMA.equals(hint)) { + return new IMarkerResolution[] {new XMLSchemaMarkerResolution()}; + } + if(IMavenConstants.EDITOR_HINT_PARENT_VERSION.equals(hint)) { + return new IMarkerResolution[] {new PomQuickAssistProcessor.IdPartRemovalProposal(marker, true) }; + } + if(IMavenConstants.EDITOR_HINT_PARENT_GROUP_ID.equals(hint)) { + return new IMarkerResolution[] {new PomQuickAssistProcessor.IdPartRemovalProposal(marker, false) }; + } + if(hint.equals(IMavenConstants.EDITOR_HINT_MANAGED_DEPENDENCY_OVERRIDE)) { + return new IMarkerResolution[] { + new PomQuickAssistProcessor.ManagedVersionRemovalProposal(marker, true), + new PomQuickAssistProcessor.IgnoreWarningProposal(marker, IMavenConstants.MARKER_IGNORE_MANAGED) + }; + } + if(hint.equals(IMavenConstants.EDITOR_HINT_MANAGED_PLUGIN_OVERRIDE)) { + return new IMarkerResolution[] { + new PomQuickAssistProcessor.ManagedVersionRemovalProposal(marker, false), + new PomQuickAssistProcessor.IgnoreWarningProposal(marker, IMavenConstants.MARKER_IGNORE_MANAGED) + }; + } + } + return new IMarkerResolution[0]; + } + + public boolean hasResolutions(IMarker marker) { + String hint = marker.getAttribute(IMavenConstants.MARKER_ATTR_EDITOR_HINT, null); //$NON-NLS-1$ + return !(hint == null); + } +} diff --git a/org.eclipse.m2e.editor.xml/src/main/java/org/eclipse/m2e/editor/xml/MvnImages.java b/org.eclipse.m2e.editor.xml/src/main/java/org/eclipse/m2e/editor/xml/MvnImages.java new file mode 100644 index 00000000..90ff8203 --- /dev/null +++ b/org.eclipse.m2e.editor.xml/src/main/java/org/eclipse/m2e/editor/xml/MvnImages.java @@ -0,0 +1,107 @@ +/******************************************************************************* + * 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 org.eclipse.jface.resource.ImageDescriptor; +import org.eclipse.jface.resource.ImageRegistry; +import org.eclipse.swt.graphics.Image; +import org.eclipse.ui.plugin.AbstractUIPlugin; + +import org.eclipse.m2e.core.core.MavenLogger; + +/** + * @author Eugene Kuleshov + */ +public class MvnImages { + + // object images + + public static final Image IMG_JAR = createImage("jar_obj.gif"); //$NON-NLS-1$ + + public static final Image IMG_JARS = createImage("jars_obj.gif"); //$NON-NLS-1$ + + public static final Image IMG_REPOSITORY = createImage("repository_obj.gif"); //$NON-NLS-1$ + + public static final Image IMG_PLUGIN = createImage("plugin_obj.gif"); //$NON-NLS-1$ + + public static final Image IMG_PLUGINS = createImage("plugins_obj.gif"); //$NON-NLS-1$ + + public static final Image IMG_EXECUTION = createImage("execution_obj.gif"); //$NON-NLS-1$ + + public static final Image IMG_GOAL = createImage("goal_obj.gif"); //$NON-NLS-1$ + + public static final Image IMG_FILTER = createImage("filter_obj.gif"); //$NON-NLS-1$ + + public static final Image IMG_RESOURCE = createImage("resource_obj.gif"); //$NON-NLS-1$ + + public static final Image IMG_RESOURCES = createImage("resources_obj.gif"); //$NON-NLS-1$ + + public static final Image IMG_INCLUDE = createImage("include_obj.gif"); //$NON-NLS-1$ + + public static final Image IMG_EXCLUDE = createImage("exclude_obj.gif"); //$NON-NLS-1$ + + public static final Image IMG_PERSON = createImage("person_obj.gif"); //$NON-NLS-1$ + + public static final Image IMG_ROLE = createImage("role_obj.gif"); //$NON-NLS-1$ + + public static final Image IMG_PROPERTY = createImage("property_obj.gif"); //$NON-NLS-1$ + + public static final Image IMG_PROPERTIES = createImage("properties_obj.gif"); //$NON-NLS-1$ + + public static final Image IMG_REPORT = createImage("report_obj.gif"); //$NON-NLS-1$ + + public static final Image IMG_PROFILE = createImage("profile_obj.gif"); //$NON-NLS-1$ + + public static final Image IMG_PROFILES = createImage("profiles_obj.gif"); //$NON-NLS-1$ + + public static final Image IMG_PARAMETER = createImage("parameter_obj.gif"); //$NON-NLS-1$ + + public static final Image IMG_BUILD = createImage("build_obj.gif"); //$NON-NLS-1$ + + public static final Image IMG_ELEMENT = createImage("element_obj.gif"); //$NON-NLS-1$ + + public static final Image IMG_USER_TEMPLATE = createImage("template_obj.gif"); //$NON-NLS-1$ + + public static final Image IMG_OPEN_POM = createImage("open_pom.gif"); //$NON-NLS-1$ + + public static final Image IMG_CLOSE = createImage("close.gif"); //$NON-NLS-1$ + + private static ImageDescriptor create(String key) { + try { + ImageDescriptor imageDescriptor = createDescriptor(key); + ImageRegistry imageRegistry = getImageRegistry(); + if(imageRegistry!=null) { + imageRegistry.put(key, imageDescriptor); + } + return imageDescriptor; + } catch (Exception ex) { + MavenLogger.log(key, ex); + return null; + } + } + + private static Image createImage(String key) { + create(key); + ImageRegistry imageRegistry = getImageRegistry(); + return imageRegistry==null ? null : imageRegistry.get(key); + } + + private static ImageRegistry getImageRegistry() { + MvnIndexPlugin plugin = MvnIndexPlugin.getDefault(); + return plugin==null ? null : plugin.getImageRegistry(); + } + + private static ImageDescriptor createDescriptor(String image) { + return AbstractUIPlugin.imageDescriptorFromPlugin(MvnIndexPlugin.PLUGIN_ID, "icons/" + image); //$NON-NLS-1$ + } + +} diff --git a/org.eclipse.m2e.editor.xml/src/main/java/org/eclipse/m2e/editor/xml/MvnIndexPlugin.java b/org.eclipse.m2e.editor.xml/src/main/java/org/eclipse/m2e/editor/xml/MvnIndexPlugin.java new file mode 100644 index 00000000..5e5812df --- /dev/null +++ b/org.eclipse.m2e.editor.xml/src/main/java/org/eclipse/m2e/editor/xml/MvnIndexPlugin.java @@ -0,0 +1,99 @@ +/******************************************************************************* + * 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.IOException; + +import org.osgi.framework.BundleContext; + +import org.eclipse.jface.text.templates.ContextTypeRegistry; +import org.eclipse.jface.text.templates.persistence.TemplateStore; +import org.eclipse.ui.editors.text.templates.ContributionContextTypeRegistry; +import org.eclipse.ui.editors.text.templates.ContributionTemplateStore; +import org.eclipse.ui.plugin.AbstractUIPlugin; + +import org.eclipse.m2e.core.core.MavenLogger; + + +/** + * @author Lukas Krecan + */ +public class MvnIndexPlugin extends AbstractUIPlugin { + public static final String PLUGIN_ID = "org.eclipse.m2e.editor.xml"; //$NON-NLS-1$ + + private static final String TEMPLATES_KEY = PLUGIN_ID + ".templates"; //$NON-NLS-1$ + + private static MvnIndexPlugin defaultInstance; + + private TemplateStore templateStore; + + private ContributionContextTypeRegistry contextTypeRegistry; + + @Override + public void start(BundleContext context) throws Exception { + super.start(context); + defaultInstance = this; + } + + @Override + public void stop(BundleContext context) throws Exception { + super.stop(context); + defaultInstance = null; + } + + public static MvnIndexPlugin getDefault() { + return defaultInstance; + } + + /** + * Returns the template store. + * + * @return the template store. + */ + public TemplateStore getTemplateStore() { + if(templateStore == null) { + templateStore = new ContributionTemplateStore(getTemplateContextRegistry(), getPreferenceStore(), TEMPLATES_KEY); + try { + templateStore.load(); + } catch(IOException ex) { + MavenLogger.log("Unable to load pom templates", ex); //$NON-NLS-1$ + } + } + return templateStore; + } + + /** + * Returns the template context type registry. + * + * @return the template context type registry + */ + public ContextTypeRegistry getTemplateContextRegistry() { + if(contextTypeRegistry == null) { + ContributionContextTypeRegistry registry = new ContributionContextTypeRegistry(); + for(PomTemplateContext contextType : PomTemplateContext.values()) { + registry.addContextType(contextType.getContextTypeId()); + } + contextTypeRegistry = registry; + } + return contextTypeRegistry; + } + + public ContextTypeRegistry getContextTypeRegistry() { + if(contextTypeRegistry == null) { + contextTypeRegistry = new ContributionContextTypeRegistry(); + // TemplateContextType contextType = new TemplateContextType(CONTEXT_TYPE, "POM XML Editor"); + PomTemplateContextType contextType = new PomTemplateContextType(); + contextTypeRegistry.addContextType(contextType); + } + return contextTypeRegistry; + } +} diff --git a/org.eclipse.m2e.editor.xml/src/main/java/org/eclipse/m2e/editor/xml/PomContentAssistProcessor.java b/org.eclipse.m2e.editor.xml/src/main/java/org/eclipse/m2e/editor/xml/PomContentAssistProcessor.java new file mode 100644 index 00000000..6b010671 --- /dev/null +++ b/org.eclipse.m2e.editor.xml/src/main/java/org/eclipse/m2e/editor/xml/PomContentAssistProcessor.java @@ -0,0 +1,579 @@ +/******************************************************************************* + * 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.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import java.util.Properties; +import java.util.Set; +import java.util.Stack; +import java.util.TreeSet; + +import org.apache.maven.project.MavenProject; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.Text; + +import org.eclipse.core.filebuffers.FileBuffers; +import org.eclipse.core.filebuffers.ITextFileBuffer; +import org.eclipse.core.filesystem.IFileStore; +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.IResource; +import org.eclipse.core.resources.ResourcesPlugin; +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.Path; +import org.eclipse.jface.text.BadLocationException; +import org.eclipse.jface.text.IDocument; +import org.eclipse.jface.text.IRegion; +import org.eclipse.jface.text.ITextSelection; +import org.eclipse.jface.text.ITextViewer; +import org.eclipse.jface.text.Region; +import org.eclipse.jface.text.contentassist.CompletionProposal; +import org.eclipse.jface.text.contentassist.ICompletionProposal; +import org.eclipse.jface.text.source.ISourceViewer; +import org.eclipse.jface.text.templates.ContextTypeRegistry; +import org.eclipse.jface.text.templates.DocumentTemplateContext; +import org.eclipse.jface.text.templates.Template; +import org.eclipse.jface.text.templates.TemplateContext; +import org.eclipse.jface.text.templates.TemplateContextType; +import org.eclipse.jface.text.templates.TemplateException; +import org.eclipse.jface.text.templates.TemplateProposal; +import org.eclipse.jface.text.templates.persistence.TemplateStore; +import org.eclipse.osgi.util.NLS; +import org.eclipse.swt.graphics.Image; +import org.eclipse.ui.internal.WorkbenchPlugin; +import org.eclipse.wst.sse.core.internal.provisional.IndexedRegion; +import org.eclipse.wst.sse.core.utils.StringUtils; +import org.eclipse.wst.xml.ui.internal.contentassist.ContentAssistRequest; +import org.eclipse.wst.xml.ui.internal.contentassist.XMLContentAssistProcessor; + +import org.eclipse.m2e.core.MavenPlugin; +import org.eclipse.m2e.core.internal.project.MavenMarkerManager; +import org.eclipse.m2e.core.project.IMavenProjectFacade; +import org.eclipse.m2e.editor.xml.internal.Messages; + +/** + * @author Lukas Krecan + * @author Eugene Kuleshov + */ +@SuppressWarnings("restriction") +public class PomContentAssistProcessor extends XMLContentAssistProcessor { + + private static final ProposalComparator PROPOSAL_COMPARATOR = new ProposalComparator(); + + private ISourceViewer sourceViewer; + + private PomStructuredTextViewConfiguration textConfig; + + public PomContentAssistProcessor(ISourceViewer sourceViewer, PomStructuredTextViewConfiguration pomStructuredTextViewConfiguration) { + this.sourceViewer = sourceViewer; + textConfig = pomStructuredTextViewConfiguration; + } + + //broken + + protected void addTagNameProposals(ContentAssistRequest contentAssistRequest, int childPosition) { + String currentNodeName = getCurrentNode(contentAssistRequest).getNodeName(); + PomTemplateContext context = PomTemplateContext.fromNodeName(currentNodeName); + if(PomTemplateContext.CONFIGURATION == context) + { + //this is sort of hack that makes sure the config proposals appear even + // when you type <prefix + // the downside is that additional typing hides the proposals popup + // there has to be a better way though. the xml element completions seems to be coping with it fine.. + contentAssistRequest.setReplacementBeginPosition(contentAssistRequest.getReplacementBeginPosition() - 1); + contentAssistRequest.setReplacementLength(contentAssistRequest.getReplacementLength() + 1); + addProposals(contentAssistRequest, context, getCurrentNode(contentAssistRequest), contentAssistRequest.getMatchString()); + } + if(PomTemplateContext.UNKNOWN == context) + { + context = PomTemplateContext.fromNodeName(getCurrentNode(contentAssistRequest).getParentNode().getNodeName()); + if(PomTemplateContext.CONFIGURATION == context) + { + addProposals(contentAssistRequest, context, getCurrentNode(contentAssistRequest).getParentNode(), contentAssistRequest.getMatchString()); + } + } + super.addTagNameProposals(contentAssistRequest, childPosition); + } + + + @Override + protected void addTagInsertionProposals(ContentAssistRequest contentAssistRequest, int childPosition) { + String currentNodeName = getCurrentNode(contentAssistRequest).getNodeName(); + + addProposals(contentAssistRequest, PomTemplateContext.fromNodeName(currentNodeName)); + super.addTagInsertionProposals(contentAssistRequest, childPosition); + } + + private Node getCurrentNode(ContentAssistRequest contentAssistRequest) { + Node currentNode = contentAssistRequest.getNode(); + if(currentNode instanceof Text) { + currentNode = currentNode.getParentNode(); + } + return currentNode; + } + + private void addProposals(ContentAssistRequest request, PomTemplateContext context) { + ITextSelection selection = (ITextSelection) sourceViewer.getSelectionProvider().getSelection(); + int offset = request.getReplacementBeginPosition(); + // adjust offset to end of normalized selection + if(selection.getOffset() == offset) { + offset = selection.getOffset() + selection.getLength(); + } + + String prefix = extractPrefix(sourceViewer, offset); + + addExpressionProposal(request, context, getCurrentNode(request), prefix); + + addGenerateProposals(request, context, getCurrentNode(request), prefix); + + addProposals(request, context, getCurrentNode(request), prefix); + } + /** + * this is a proposal method for adding expressions when ${ is typed.. + * @param request + * @param context + * @param currentNode + * @param prefix + */ + private void addExpressionProposal(ContentAssistRequest request, PomTemplateContext context, Node currentNode, + String prefix) { + int exprStart = prefix.lastIndexOf("${"); //$NON-NLS-1$ + if (exprStart != -1) { + //the regular prefix is separated by whitespace and <> brackets only, we need to cut the last portion + String realExpressionPrefix = prefix.substring(exprStart); + if (realExpressionPrefix.contains("}")) { //$NON-NLS-1$ + //the expression is not opened.. + return; + } + if (expressionproposalContexts.contains(context)) { + //add all effective pom expressions + IProject prj = extractProject(sourceViewer); + Region region = new Region(request.getReplacementBeginPosition() - realExpressionPrefix.length(), realExpressionPrefix.length()); + if (prj != null) { + IMavenProjectFacade mvnproject = MavenPlugin.getDefault().getMavenProjectManager().getProject(prj); + Set<String> collect = new TreeSet<String>(); + if (mvnproject != null) { + MavenProject mp = mvnproject.getMavenProject(); + if (mp != null) { + Properties props = mp.getProperties(); + if (props != null) { + for (Object key : props.keySet()) { + String keyString = key.toString(); + if (("${" + keyString).startsWith(realExpressionPrefix)) { //$NON-NLS-1$ + collect.add(keyString); + } + } + } + } + //add a few hardwired values as well + if ("${basedir}".startsWith(realExpressionPrefix)) { //$NON-NLS-1$ + collect.add("basedir"); //$NON-NLS-1$ + } + if ("${project.version}".startsWith(realExpressionPrefix)) { //$NON-NLS-1$ + collect.add("project.version"); //$NON-NLS-1$ + } + if ("${project.groupId}".startsWith(realExpressionPrefix)) { //$NON-NLS-1$ + collect.add("project.groupId"); //$NON-NLS-1$ + } + if ("${project.artifactId}".startsWith(realExpressionPrefix)) { //$NON-NLS-1$ + collect.add("project.artifactId"); //$NON-NLS-1$ + } + if ("${project.build.directory}".startsWith(realExpressionPrefix)) { //$NON-NLS-1$ + collect.add("project.build.directory"); //$NON-NLS-1$ + } + for (String key : collect) { + ICompletionProposal proposal = new InsertExpressionProposal(sourceViewer, region, key, mvnproject); + if(request.shouldSeparate()) { + request.addMacro(proposal); + } else { + request.addProposal(proposal); + } + } + } + } + } + } + } + + private static List<PomTemplateContext> expressionproposalContexts = Arrays.asList(new PomTemplateContext[] { + PomTemplateContext.ARTIFACT_ID, + PomTemplateContext.CLASSIFIER, +// PomTemplateContext.CONFIGURATION, + PomTemplateContext.GOAL, + PomTemplateContext.GROUP_ID, + PomTemplateContext.MODULE, + PomTemplateContext.PACKAGING, + PomTemplateContext.PHASE, + PomTemplateContext.PROPERTIES, //?? + PomTemplateContext.SCOPE, + PomTemplateContext.SYSTEM_PATH, + PomTemplateContext.TYPE, +// PomTemplateContext.VERSION, version is intentionally not included as we have specialized handling there.. + PomTemplateContext.UNKNOWN //this one is both important and troubling.. but having a context for everything is weird. + }); + + private void addGenerateProposals(ContentAssistRequest request, PomTemplateContext context, Node node, String prefix) { + if (prefix.trim().length() != 0) { + //only provide these generate proposals when there is no prefix. + return; + } + if (context == PomTemplateContext.PROJECT) { + //check if we have a parent defined.. + Node project = node; + if (project != null && project instanceof Element) { + Element parent = MavenMarkerManager.findChildElement((Element)project, "parent"); //$NON-NLS-1$ + if (parent == null) { + //now add the proposal for parent inclusion + Region region = new Region(request.getReplacementBeginPosition(), 0); + Element groupId = MavenMarkerManager.findChildElement((Element)project, "groupId"); //$NON-NLS-1$ + String groupString = null; + if (groupId != null) { + groupString = MavenMarkerManager.getElementTextValue(groupId); + } + InsertArtifactProposal.Configuration config = new InsertArtifactProposal.Configuration(InsertArtifactProposal.SearchType.PARENT); + config.setInitiaSearchString(groupString); + ICompletionProposal proposal = new InsertArtifactProposal(sourceViewer, region, config, this.textConfig); + if(request.shouldSeparate()) { + request.addMacro(proposal); + } else { + request.addProposal(proposal); + } + } + } + } + if (context == PomTemplateContext.PARENT && node.getNodeName().equals("parent")) { //$NON-NLS-1$ + Element parent = (Element)node; + Element relPath = MavenMarkerManager.findChildElement(parent, "relativePath"); //$NON-NLS-1$ + if (relPath == null) { + //only show when no relpath already defined.. + String relative = findRelativePath(sourceViewer, parent); + if (relative != null) { + Region region = new Region(request.getReplacementBeginPosition(), 0); + ICompletionProposal proposal = new CompletionProposal("<relativePath>" + relative + "</relativePath>", //$NON-NLS-1$ //$NON-NLS-2$ + region.getOffset(), region.getLength(), 0, + WorkbenchPlugin.getDefault().getImageRegistry().get(org.eclipse.ui.internal.SharedImages.IMG_OBJ_ADD), + NLS.bind(Messages.PomContentAssistProcessor_insert_relPath_title, relative), null, null); + if (request.shouldSeparate()) { + request.addMacro(proposal); + } else { + request.addProposal(proposal); + } + } + } + } + if (context == PomTemplateContext.RELATIVE_PATH) { + //completion in the text portion of relative path + Element parent = (Element) node.getParentNode(); + if (parent != null && "parent".equals(parent.getNodeName())) { //$NON-NLS-1$ + String relative = findRelativePath(sourceViewer, parent); + String textContent = MavenMarkerManager.getElementTextValue(node); + if (relative != null && !relative.equals(textContent)) { + Region region = new Region(request.getReplacementBeginPosition() - prefix.length(), prefix.length()); + if (request.getNode() instanceof IndexedRegion && request.getNode() instanceof Text) { + //for <relativePath>|</relativePath> the current node is the element node and not the text node + //only replace the text node content.. + IndexedRegion index = (IndexedRegion)request.getNode(); + region = new Region(index.getStartOffset(), index.getEndOffset() - index.getStartOffset()); + } + ICompletionProposal proposal = new CompletionProposal(relative, + region.getOffset(), region.getLength(), 0, + WorkbenchPlugin.getDefault().getImageRegistry().get(org.eclipse.ui.internal.SharedImages.IMG_OBJ_ADD), + NLS.bind(Messages.PomContentAssistProcessor_set_relPath_title, relative), null, null); + if (request.shouldSeparate()) { + request.addMacro(proposal); + } else { + request.addProposal(proposal); + } + } + } + } + if (context == PomTemplateContext.PLUGINS || context == PomTemplateContext.BUILD + || context == PomTemplateContext.PLUGIN_MANAGEMENT || context == PomTemplateContext.PROJECT) { + //now add the proposal for plugin inclusion + Region region = new Region(request.getReplacementBeginPosition(), 0); + InsertArtifactProposal.Configuration config = new InsertArtifactProposal.Configuration(InsertArtifactProposal.SearchType.PLUGIN); + config.setCurrentNode(node); + + ICompletionProposal proposal = new InsertArtifactProposal(sourceViewer, region, config, this.textConfig); + if(request.shouldSeparate()) { + request.addMacro(proposal); + } else { + request.addProposal(proposal); + } + + } + + } + /* + * calculates the path of the node up in the hierarchy, example of result is project/build/plugins/plugin + * level parameter designates the number of parents to climb eg. for level 2 the result would be plugins/plugin + * level -1 means all the way to the top. + */ + static String pathUp(Node node, int level) { + StringBuffer buf = new StringBuffer(); + int current = level; + while (node != null && level > 0) { + if (node instanceof Element) { + if (buf.length() > 0) { + buf.insert(0, "/"); + } + buf.insert(0, node.getNodeName()); + current = current -1; + } + node = node.getParentNode(); + } + return buf.toString(); + } + + private static String findRelativePath(ISourceViewer viewer, Element parent) { + String groupId = MavenMarkerManager.getElementTextValue(MavenMarkerManager.findChildElement(parent, "groupId")); //$NON-NLS-1$ + String artifactId = MavenMarkerManager.getElementTextValue(MavenMarkerManager.findChildElement(parent, "artifactId")); //$NON-NLS-1$ + String version = MavenMarkerManager.getElementTextValue(MavenMarkerManager.findChildElement(parent, "version")); //$NON-NLS-1$ + return findRelativePath(viewer, groupId, artifactId, version); + } + + public static String findRelativePath(ISourceViewer viewer, String groupId, String artifactId, String version) { + if (groupId != null && artifactId != null && version != null) { + IMavenProjectFacade facade = MavenPlugin.getDefault().getMavenProjectManager().getMavenProject(groupId, artifactId, version); + if (facade != null) { + //now add the proposal for relativePath + IFile parentPomFile = facade.getPom(); + IPath path = parentPomFile.getLocation(); + IProject prj = extractProject(viewer); + if (prj != null && path != null) { + IPath path2 = prj.getLocation(); + IPath relative = path.makeRelativeTo(path2); + if (relative != path) { + return relative.toString(); + } + } + } + } + return null; + } + + + + private void addProposals(ContentAssistRequest request, PomTemplateContext context, Node currentNode, String prefix) { + if(request != null) { + IProject prj = extractProject(sourceViewer); + + ICompletionProposal[] templateProposals = getTemplateProposals(prj, sourceViewer, + request.getReplacementBeginPosition(), context.getContextTypeId(), currentNode, prefix); + for(ICompletionProposal proposal : templateProposals) { + if(request.shouldSeparate()) { + request.addMacro(proposal); + } else { + request.addProposal(proposal); + } + } + } + } + + /** + * what is this method supposed to do? for the sourceViewer find the associated file on disk and for + * that one find the IProject it belongs to. The required condition for the IProject instance is that + * project relative path of the file shall only be pom.xml (thus no nested, unopened maven pom). + * So that when MavenPlugin.getDefault().getMavenProjectManager().getProject(prj); is called later on + * the instance, it actually returns the maven model facade for the pom.xml backing the sourceViewer. + * @param sourceViewer + * @return + */ + public static IProject extractProject(ITextViewer sourceViewer) { + ITextFileBuffer buf = FileBuffers.getTextFileBufferManager().getTextFileBuffer(sourceViewer.getDocument()); + IFileStore folder = buf.getFileStore(); + File file = new File(folder.toURI()); + IPath path = Path.fromOSString(file.getAbsolutePath()); + Stack<IResource> stack = new Stack<IResource>(); + //here we need to find the most inner project to the path. + //we do so by shortening the path and remembering all the resources identified. + // at the end we pick the last one from the stack. is there a catch to it? + IResource ifile = ResourcesPlugin.getWorkspace().getRoot().getFileForLocation(path); + if (ifile != null) { + stack.push(ifile); + } else { + while(path.segmentCount() > 1) { + ResourcesPlugin.getWorkspace().getRoot().findMember(path); + if(ifile != null) { + stack.push(ifile); + } + path = path.removeFirstSegments(1); + } + } + IResource res = stack.empty() ? null : stack.pop(); + if (res != null) { + IProject prj = res.getProject(); + //the project returned is in a way unrelated to nested child poms that don't have an opened project, + //in that case we pass along a wrong parent/aggregator + if (res.getProjectRelativePath().segmentCount() != 1) { + //if the project were the pom's project, the relative path would be just "pom.xml", if it's not just throw it out of the window.. + prj = null; + } + return prj; + } + return null; + } + + private ICompletionProposal[] getTemplateProposals(IProject project, ITextViewer viewer, int offset, String contextTypeId, Node currentNode, String prefix) { + ITextSelection selection = (ITextSelection) viewer.getSelectionProvider().getSelection(); + + // adjust offset to end of normalized selection + if(selection.getOffset() == offset) { + offset = selection.getOffset() + selection.getLength(); + } + +// String prefix = extractPrefix(viewer, offset); + Region region = new Region(offset - prefix.length(), prefix.length()); + TemplateContext context = createContext(viewer, region, contextTypeId); + if(context == null) { + return new ICompletionProposal[0]; + } + + // name of the selection variables {line, word}_selection + context.setVariable("selection", selection.getText()); //$NON-NLS-1$ + + PomTemplateContext templateContext = PomTemplateContext.fromId(contextTypeId); + + // add the user defined templates - separate them from the rest of the templates + // so that we know what they are and can assign proper icon to them. + Image image = MvnImages.IMG_USER_TEMPLATE; + List<TemplateProposal> matches = new ArrayList<TemplateProposal>(); + TemplateStore store = MvnIndexPlugin.getDefault().getTemplateStore(); + if(store != null) { + Template[] templates = store.getTemplates(contextTypeId); + for(Template template : templates) { + TemplateProposal proposal = createProposalForTemplate(prefix, region, context, image, template, true); + if (proposal != null) { + matches.add(proposal); + } + } + } + if (templateContext == PomTemplateContext.CONFIGURATION) { + image = MvnImages.IMG_PARAMETER; + } else { + //other suggestions from the templatecontext are to be text inside the element, not actual + //elements.. + image = null; + } + + Template[] templates = templateContext.getTemplates(project, currentNode, prefix); + for(Template template : templates) { + TemplateProposal proposal = createProposalForTemplate(prefix, region, context, image, template, false); + if (proposal != null) { + matches.add(proposal); + } + } + + + if (templateContext!=PomTemplateContext.VERSION) { + // versions are already sorted with o.a.m.artifact.versioning.ComparableVersion + Collections.sort(matches, PROPOSAL_COMPARATOR); + } + + return (ICompletionProposal[]) matches.toArray(new ICompletionProposal[matches.size()]); + + } + + private TemplateProposal createProposalForTemplate(String prefix, Region region, TemplateContext context, Image image, + final Template template, boolean isUserTemplate) { + try { + context.getContextType().validate(template.getPattern()); + if(template.matches(prefix, context.getContextType().getId())) { + if (isUserTemplate) { + //for templates defined by users, preserve the default behaviour.. + return new TemplateProposal(template, context, region, image, getRelevance(template, prefix)) { + public String getAdditionalProposalInfo() { + return StringUtils.convertToHTMLContent(super.getAdditionalProposalInfo()); + } + }; + } else { + return new TemplateProposal(template, context, region, image, getRelevance(template, prefix)) { + public String getAdditionalProposalInfo() { + return getTemplate().getDescription(); + } + + public String getDisplayString() { + return template.getName(); + } + }; + } + } + } catch(TemplateException e) { + // ignore + } + + return null; + } + + protected TemplateContext createContext(ITextViewer viewer, IRegion region, String contextTypeId) { + TemplateContextType contextType= getContextType(viewer, region, contextTypeId); + if (contextType != null) { + IDocument document= viewer.getDocument(); + return new DocumentTemplateContext(contextType, document, region.getOffset(), region.getLength()); + } + return null; + } + + //TODO we should have different relevance for user defined templates and generated proposals.. + protected int getRelevance(Template template, String prefix) { + if (template.getName().startsWith(prefix)) + return 90; + return 0; + } + + protected TemplateContextType getContextType(ITextViewer viewer, IRegion region, String contextTypeId) { + ContextTypeRegistry registry = MvnIndexPlugin.getDefault().getTemplateContextRegistry(); + if(registry != null) { + return registry.getContextType(contextTypeId); + } + return null; + } + + public static final String extractPrefix(ITextViewer viewer, int offset) { + int i = offset; + IDocument document = viewer.getDocument(); + if(i > document.getLength()) { + return ""; //$NON-NLS-1$ + } + + try { + while(i > 0) { + char ch = document.getChar(i - 1); + if(ch == '>' || ch == '<' || ch == ' ' || ch == '\n' || ch == '\t') { + break; + } + i-- ; + } + return document.get(i, offset - i); + } catch(BadLocationException e) { + return ""; //$NON-NLS-1$ + } + } + + + static final class ProposalComparator implements Comparator<TemplateProposal> { + public int compare(TemplateProposal o1, TemplateProposal o2) { + int res = o2.getRelevance() - o1.getRelevance(); + if(res == 0) { + res = o1.getDisplayString().compareTo(o2.getDisplayString()); + } + return res; + } + } + +} diff --git a/org.eclipse.m2e.editor.xml/src/main/java/org/eclipse/m2e/editor/xml/PomContentOutlineConfiguration.java b/org.eclipse.m2e.editor.xml/src/main/java/org/eclipse/m2e/editor/xml/PomContentOutlineConfiguration.java new file mode 100644 index 00000000..60b4b27a --- /dev/null +++ b/org.eclipse.m2e.editor.xml/src/main/java/org/eclipse/m2e/editor/xml/PomContentOutlineConfiguration.java @@ -0,0 +1,358 @@ +/******************************************************************************* + * 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 org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +import org.eclipse.jface.dialogs.Dialog; +import org.eclipse.jface.viewers.ILabelProvider; +import org.eclipse.jface.viewers.ILabelProviderListener; +import org.eclipse.jface.viewers.TreeViewer; +import org.eclipse.swt.graphics.Image; +import org.eclipse.wst.xml.ui.views.contentoutline.XMLContentOutlineConfiguration; + + +/** + * @author Eugene Kuleshov + */ +public class PomContentOutlineConfiguration extends XMLContentOutlineConfiguration { + + public ILabelProvider getLabelProvider(TreeViewer viewer) { + return new PomLabelProvider(super.getLabelProvider(viewer)); + } + + /** + * POM label provider + */ + private final class PomLabelProvider implements ILabelProvider { + + private static final String TARGET_PATH = "targetPath"; //$NON-NLS-1$ + + private static final String DIRECTORY = "directory"; //$NON-NLS-1$ + + private static final String REPORT_SET = "reportSet"; //$NON-NLS-1$ + + private static final String PROPERTIES = "properties"; //$NON-NLS-1$ + + private static final String REPORTING = "reporting"; //$NON-NLS-1$ + + private static final String BUILD = "build"; //$NON-NLS-1$ + + private static final String EXCLUDE = "exclude"; //$NON-NLS-1$ + + private static final String INCLUDE = "include"; //$NON-NLS-1$ + + private static final String FILTER = "filter"; //$NON-NLS-1$ + + private static final String TEST_RESOURCE = "testResource"; //$NON-NLS-1$ + + private static final String RESOURCE = "resource"; //$NON-NLS-1$ + + private static final String TEST_RESOURCES = "testResources"; //$NON-NLS-1$ + + private static final String RESOURCES = "resources"; //$NON-NLS-1$ + + private static final String GOAL = "goal"; //$NON-NLS-1$ + + private static final String EXECUTION = "execution"; //$NON-NLS-1$ + + private static final String PLUGIN = "plugin"; //$NON-NLS-1$ + + private static final String PLUGINS = "plugins"; //$NON-NLS-1$ + + private static final String SNAPSHOT_REPOSITORY = "snapshotRepository"; //$NON-NLS-1$ + + private static final String PLUGIN_REPOSITORY = "pluginRepository"; //$NON-NLS-1$ + + private static final String REPOSITORY = "repository"; //$NON-NLS-1$ + + private static final String SITE = "site"; //$NON-NLS-1$ + + private static final String CONTRIBUTOR = "contributor"; //$NON-NLS-1$ + + private static final String DEVELOPER = "developer"; //$NON-NLS-1$ + + private static final String PROFILE = "profile"; //$NON-NLS-1$ + + private static final String PROFILES = "profiles"; //$NON-NLS-1$ + + private static final String MODULE = "module"; //$NON-NLS-1$ + + private static final String EXTENSION = "extension"; //$NON-NLS-1$ + + private static final String EXCLUSION = "exclusion"; //$NON-NLS-1$ + + private static final String MODULES = "modules"; //$NON-NLS-1$ + + private static final String EXTENSIONS = "extensions"; //$NON-NLS-1$ + + private static final String EXCLUSIONS = "exclusions"; //$NON-NLS-1$ + + private static final String DEPENDENCIES = "dependencies"; //$NON-NLS-1$ + + private static final String PARENT = "parent"; //$NON-NLS-1$ + + private static final String SCOPE = "scope"; //$NON-NLS-1$ + + private static final String TYPE = "type"; //$NON-NLS-1$ + + private static final String CLASSIFIER = "classifier"; //$NON-NLS-1$ + + private static final String DEPENDENCY = "dependency"; //$NON-NLS-1$ + + private static final String ID = "id"; //$NON-NLS-1$ + + private static final String EMAIL = "email"; //$NON-NLS-1$ + + private static final String NAME = "name"; //$NON-NLS-1$ + + private static final String VERSION = "version"; //$NON-NLS-1$ + + private static final String GROUP_ID = "groupId"; //$NON-NLS-1$ + + private static final String ARTIFACT_ID = "artifactId"; //$NON-NLS-1$ + + private static final String NAMESPACE_POM = "http://maven.apache.org/POM/4.0.0"; //$NON-NLS-1$ + + private static final int MAX_LABEL_LENGTH = 120; + + private final ILabelProvider labelProvider; + + private PomLabelProvider(ILabelProvider labelProvider) { + this.labelProvider = labelProvider; + } + + public Image getImage(Object element) { + Node node = (Node) element; + String namespace = node.getNamespaceURI(); + String nodeName = node.getNodeName(); + + if(node.getNodeType()==Node.COMMENT_NODE) { + return labelProvider.getImage(element); + } + + if(NAMESPACE_POM.equals(namespace)) { + if(PARENT.equals(nodeName)) { + return MvnImages.IMG_JAR; + + } else if(DEPENDENCIES.equals(nodeName) // + || EXCLUSIONS.equals(nodeName) // + || EXTENSIONS.equals(nodeName) // + || MODULES.equals(nodeName)) { + return MvnImages.IMG_JARS; + + } else if(DEPENDENCY.equals(nodeName) // + || EXCLUSION.equals(nodeName) // + || EXTENSION.equals(nodeName) // + || MODULE.equals(nodeName)) { + // TODO show folder if module is in the workspace + return MvnImages.IMG_JAR; + + } else if(REPOSITORY.equals(nodeName) || PLUGIN_REPOSITORY.equals(nodeName) + || SNAPSHOT_REPOSITORY.equals(nodeName) || SITE.equals(nodeName)) { + return MvnImages.IMG_REPOSITORY; + + } else if(PROFILES.equals(nodeName)) { + return MvnImages.IMG_PROFILES; + + } else if(PROFILE.equals(nodeName)) { + return MvnImages.IMG_PROFILE; + + } else if(DEVELOPER.equals(nodeName) || CONTRIBUTOR.equals(nodeName)) { + return MvnImages.IMG_PERSON; + + } else if(PLUGINS.equals(nodeName)) { + return MvnImages.IMG_PLUGINS; + + } else if(PLUGIN.equals(nodeName)) { + return MvnImages.IMG_PLUGIN; + + } else if(EXECUTION.equals(nodeName)) { + return MvnImages.IMG_EXECUTION; + + } else if(GOAL.equals(nodeName)) { + return MvnImages.IMG_GOAL; + + } else if(RESOURCES.equals(nodeName) // + || TEST_RESOURCES.equals(nodeName)) { + return MvnImages.IMG_RESOURCES; + + } else if(RESOURCE.equals(nodeName) // + || TEST_RESOURCE.equals(nodeName)) { + return MvnImages.IMG_RESOURCE; + + } else if(FILTER.equals(nodeName)) { + return MvnImages.IMG_FILTER; + + } else if(INCLUDE.equals(nodeName)) { + return MvnImages.IMG_INCLUDE; + + } else if(EXCLUDE.equals(nodeName)) { + return MvnImages.IMG_EXCLUDE; + + } else if(BUILD.equals(nodeName)) { + return MvnImages.IMG_BUILD; + + } else if(REPORTING.equals(nodeName)) { + return MvnImages.IMG_REPORT; + + } else if(PROPERTIES.equals(nodeName)) { + return MvnImages.IMG_PROPERTIES; + + } else if(PROPERTIES.equals(node.getParentNode().getNodeName())) { + return MvnImages.IMG_PROPERTY; + + // } else if("mailingList".equals(nodeName)) { + // return MvnImages.IMG_MAIL; + + } + + return MvnImages.IMG_ELEMENT; + } + + return labelProvider.getImage(element); + } + + public String getText(Object element) { + String text = labelProvider.getText(element); + + Node node = (Node) element; + String namespace = node.getNamespaceURI(); + String nodeName = node.getNodeName(); + + if(node.getNodeType()==Node.COMMENT_NODE) { + return cleanText(node); + } + + if(NAMESPACE_POM.equals(namespace)) { + if(PARENT.equals(nodeName)) { + return getLabel(text, node, GROUP_ID, ARTIFACT_ID, VERSION); + + } else if(DEPENDENCY.equals(nodeName)) { + return getLabel(text, node, GROUP_ID, ARTIFACT_ID, VERSION, CLASSIFIER, TYPE, SCOPE); + + } else if(EXCLUSION.equals(nodeName)) { + return getLabel(text, node, GROUP_ID, ARTIFACT_ID); + + } else if(EXTENSION.equals(nodeName)) { + return getLabel(text, node, GROUP_ID, ARTIFACT_ID, VERSION); + + } else if(REPOSITORY.equals(nodeName) || PLUGIN_REPOSITORY.equals(nodeName) + || SNAPSHOT_REPOSITORY.equals(nodeName) || SITE.equals(nodeName) || PROFILE.equals(nodeName) + || EXECUTION.equals(nodeName)) { + return getLabel(text, node, ID); + + } else if("mailingList".equals(nodeName)) { //$NON-NLS-1$ + return getLabel(text, node, NAME); + + } else if(DEVELOPER.equals(nodeName)) { + return getLabel(text, node, ID, NAME, EMAIL); + + } else if(CONTRIBUTOR.equals(nodeName)) { + return getLabel(text, node, NAME, EMAIL); + + } else if(PLUGIN.equals(nodeName)) { + return getLabel(text, node, GROUP_ID, ARTIFACT_ID, VERSION); + + } else if(RESOURCE.equals(nodeName) || TEST_RESOURCE.equals(nodeName)) { + return getLabel(text, node, DIRECTORY, TARGET_PATH); + + } else if(REPORT_SET.equals(nodeName)) { + return getLabel(text, node, ID); + + } else if(EXECUTION.equals(nodeName)) { + return getLabel(text, node, ID); + + } + + NodeList childNodes = node.getChildNodes(); + if(childNodes.getLength()==1) { + Node item = childNodes.item(0); + short nodeType = item.getNodeType(); + if(nodeType==Node.TEXT_NODE || nodeType==Node.COMMENT_NODE) { + String nodeText = item.getNodeValue(); + if(nodeText.length()>0) { + return text + " " + cleanText(item); //$NON-NLS-1$ + } + } + } + } + + return text; + } + + public boolean isLabelProperty(Object element, String name) { + return labelProvider.isLabelProperty(element, name); + } + + public void addListener(ILabelProviderListener listener) { + labelProvider.addListener(listener); + } + + public void removeListener(ILabelProviderListener listener) { + labelProvider.removeListener(listener); + } + + public void dispose() { + labelProvider.dispose(); + } + + private String getLabel(String text, Node node, String... names) { + StringBuilder sb = new StringBuilder(text).append(" "); //$NON-NLS-1$ + String sep = ""; //$NON-NLS-1$ + for(String name : names) { + String value = getValue(node, name); + if(value!=null) { + sb.append(sep).append(value); + sep = " : "; //$NON-NLS-1$ + } + } + + return sb.toString(); + } + + private String getValue(Node node, String name) { + NodeList childNodes = node.getChildNodes(); + for(int i = 0; i < childNodes.getLength(); i++ ) { + Node item = childNodes.item(i); + if(item.getNodeType()==Node.ELEMENT_NODE && name.equals(item.getNodeName())) { + NodeList nodes = item.getChildNodes(); + if(nodes.getLength()==1) { + String value = nodes.item(0).getNodeValue().trim(); + if(value.length()>0) { + return value; + } + } + return null; + } + } + return null; + } + + private String cleanText(Node node) { + String value = node.getNodeValue(); + if (value==null) { + return ""; //$NON-NLS-1$ + } + + value = value.replaceAll("\\s", " ").replaceAll("(\\s){2,}", " ").trim(); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ + if (value.length() > MAX_LABEL_LENGTH) { + value = value.substring(0, 120) + Dialog.ELLIPSIS; + } + + return value; + } + } + +} + diff --git a/org.eclipse.m2e.editor.xml/src/main/java/org/eclipse/m2e/editor/xml/PomHyperlinkDetector.java b/org.eclipse.m2e.editor.xml/src/main/java/org/eclipse/m2e/editor/xml/PomHyperlinkDetector.java new file mode 100644 index 00000000..25c71c7c --- /dev/null +++ b/org.eclipse.m2e.editor.xml/src/main/java/org/eclipse/m2e/editor/xml/PomHyperlinkDetector.java @@ -0,0 +1,729 @@ +/******************************************************************************* + * 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.util.ArrayList; +import java.util.List; + +import org.apache.maven.model.Build; +import org.apache.maven.model.Dependency; +import org.apache.maven.model.DependencyManagement; +import org.apache.maven.model.InputLocation; +import org.apache.maven.model.InputSource; +import org.apache.maven.model.Model; +import org.apache.maven.model.Plugin; +import org.apache.maven.model.PluginManagement; +import org.apache.maven.project.MavenProject; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; +import org.w3c.dom.Text; + +import org.eclipse.core.filebuffers.FileBuffers; +import org.eclipse.core.filebuffers.ITextFileBuffer; +import org.eclipse.core.filesystem.EFS; +import org.eclipse.core.filesystem.IFileStore; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.core.runtime.jobs.Job; +import org.eclipse.jface.dialogs.MessageDialog; +import org.eclipse.jface.text.BadLocationException; +import org.eclipse.jface.text.IDocument; +import org.eclipse.jface.text.IRegion; +import org.eclipse.jface.text.ITextViewer; +import org.eclipse.jface.text.Region; +import org.eclipse.jface.text.hyperlink.IHyperlink; +import org.eclipse.jface.text.hyperlink.IHyperlinkDetector; +import org.eclipse.osgi.util.NLS; +import org.eclipse.swt.widgets.Display; +import org.eclipse.ui.IEditorPart; +import org.eclipse.ui.IWorkbenchPage; +import org.eclipse.ui.IWorkbenchWindow; +import org.eclipse.ui.PartInitException; +import org.eclipse.ui.PlatformUI; +import org.eclipse.ui.forms.editor.FormEditor; +import org.eclipse.ui.ide.IDE; +import org.eclipse.wst.sse.core.StructuredModelManager; +import org.eclipse.wst.sse.core.internal.provisional.IStructuredModel; +import org.eclipse.wst.sse.core.internal.provisional.IndexedRegion; +import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocument; +import org.eclipse.wst.sse.ui.StructuredTextEditor; + +import org.eclipse.m2e.core.MavenPlugin; +import org.eclipse.m2e.core.actions.OpenPomAction; +import org.eclipse.m2e.core.project.IMavenProjectFacade; +import org.eclipse.m2e.editor.xml.internal.Messages; + + +/** + * @author Eugene Kuleshov + * @author Milos Kleint + */ +public class PomHyperlinkDetector implements IHyperlinkDetector { + + private final String[] versioned = new String[] { + "dependency>", //$NON-NLS-1$ + "parent>", //$NON-NLS-1$ + "plugin>", //$NON-NLS-1$ + "reportPlugin>", //$NON-NLS-1$ + "extension>" //$NON-NLS-1$ + }; + public IHyperlink[] detectHyperlinks(ITextViewer textViewer, final IRegion region, boolean canShowMultipleHyperlinks) { + if(region == null || textViewer == null) { + return null; + } + + IDocument document = textViewer.getDocument(); + if(document == null) { + return null; + } + + IRegion lineInfo; + String line; + try { + lineInfo = document.getLineInformationOfOffset(region.getOffset()); + line = document.get(lineInfo.getOffset(), lineInfo.getLength()); + } catch(BadLocationException ex) { + return null; + } + + if(line.length() == 0) { + return null; + } + List<IHyperlink> hyperlinks = new ArrayList<IHyperlink>(); + final int offset = region.getOffset(); + final String text = document.get(); + Node current = getCurrentNode(document, offset); + //check if we have a property expression at cursor + IHyperlink link = openPropertyDefinition(current, textViewer, offset); + if (link != null) { + hyperlinks.add(link); + } + //now check if the dependency/plugin has a version element or not, if not, try searching for it in DM/PM of effective pom + link = openDPManagement(current, textViewer, offset); + if (link != null) { + hyperlinks.add(link); + } + + //first check all elements that have id (groupId+artifactId+version) combo + Fragment fragment = null; + //TODO rewrite to use Nodes + for (String el : versioned) { + fragment = getFragment(text, offset, "<" + el, "</" + el); //$NON-NLS-1$ //$NON-NLS-2$ + if (fragment != null) break; + } + + if (fragment != null) { + link = openPOMbyID(fragment, textViewer); + if (link != null) { + hyperlinks.add(link); + } + } + //check if <module> text is selected. + //TODO rewrite to use Nodes + fragment = getFragment(text, offset, "<module>", "</module>"); //$NON-NLS-1$ //$NON-NLS-2$ + if (fragment != null) { + link = openModule(fragment, textViewer); + if (link != null) { + hyperlinks.add(link); + } + } + if (hyperlinks.size() > 0) { + return hyperlinks.toArray(new IHyperlink[0]); + } + return null; + } + + static ManagedArtifactRegion findManagedArtifactRegion(Node current, ITextViewer textViewer, int offset) { + while (current != null && !( current instanceof Element)) { + current = current.getParentNode(); + } + if (current != null) { + Node artNode = null; + Node groupNode = null; + if ("artifactId".equals(current.getNodeName())) { //$NON-NLS-1$ + artNode = current; + } + if ("groupId".equals(current.getNodeName())) { //$NON-NLS-1$ + groupNode = current; + } + //only on artifactid and groupid elements.. + if (artNode == null && groupNode == null) { + return null; + } + Node root = current.getParentNode(); + boolean isDependency = false; + boolean isPlugin = false; + if (root != null) { + String name = root.getNodeName(); + if ("dependency".equals(name)) { //$NON-NLS-1$ + isDependency = true; + } + if ("plugin".equals(name)) { //$NON-NLS-1$ + isPlugin = true; + } + } else { + return null; + } + if (!isDependency && !isPlugin) { + //some kind of other identifier + return null; + } + //now see if version is missing + NodeList childs = root.getChildNodes(); + for (int i = 0; i < childs.getLength(); i++) { + Node child = childs.item(i); + if (child instanceof Element) { + Element el = (Element) child; + if ("version".equals(el.getNodeName())) { //$NON-NLS-1$ + return null; + } + if (artNode == null && "artifactId".equals(el.getNodeName())) { //$NON-NLS-1$ + artNode = el; + } + if (groupNode == null && "groupId".equals(el.getNodeName())) { //$NON-NLS-1$ + groupNode = el; + } + } + } + if (groupNode != null && artNode != null) { + assert groupNode instanceof IndexedRegion; + assert artNode instanceof IndexedRegion; + + IndexedRegion groupReg = (IndexedRegion)groupNode; + IndexedRegion artReg = (IndexedRegion)artNode; + int startOffset = Math.min(groupReg.getStartOffset(), artReg.getStartOffset()); + int length = Math.max(groupReg.getEndOffset(), artReg.getEndOffset()) - startOffset; + String groupId = getElementTextValue(groupNode); + String artifactId = getElementTextValue(artNode); + //TODO we shall rely on presence of a cached model, not project alone.. + final IProject prj = PomContentAssistProcessor.extractProject(textViewer); + if (prj != null) { + //now we can create the region I guess, + return new ManagedArtifactRegion(startOffset, length, groupId, artifactId, isDependency, isPlugin, prj); + } + } + } + return null; + } + + private IHyperlink openDPManagement(Node current, ITextViewer textViewer, int offset) { + final ManagedArtifactRegion region = findManagedArtifactRegion(current, textViewer, offset); + if (region != null) { + return new IHyperlink() { + public IRegion getHyperlinkRegion() { + return region; + } + + public String getHyperlinkText() { + return NLS.bind(Messages.PomHyperlinkDetector_link_managed, "" + region.groupId + ":" + region.artifactId); + } + + public String getTypeLabel() { + return "pom-dependency-plugin-management"; //$NON-NLS-1$ + } + + public void open() { + //see if we can find the plugin in plugin management of resolved project. + IMavenProjectFacade mvnproject = MavenPlugin.getDefault().getMavenProjectManager().getProject(region.project); + if (mvnproject != null) { + MavenProject mavprj = mvnproject.getMavenProject(); + if (mavprj != null) { + InputLocation openLocation = findLocationForManagedArtifact(region, mavprj); + if (openLocation != null) { + File file = fileForInputLocation(openLocation); + if (file != null) { + IFileStore fileStore = EFS.getLocalFileSystem().getStore(file.toURI()); + openXmlEditor(fileStore, openLocation.getLineNumber(), openLocation.getColumnNumber()); + } + } + } + } + } + }; + } + return null; + } + + + static InputLocation findLocationForManagedArtifact(final ManagedArtifactRegion region, MavenProject mavprj) { + Model mdl = mavprj.getModel(); + InputLocation openLocation = null; + if (region.isDependency) { + DependencyManagement dm = mdl.getDependencyManagement(); + if (dm != null) { + List<Dependency> list = dm.getDependencies(); + String id = region.groupId + ":" + region.artifactId + ":"; //$NON-NLS-1$ //$NON-NLS-2$ + if (list != null) { + for (Dependency dep : list) { + if (dep.getManagementKey().startsWith(id)) { + InputLocation location = dep.getLocation("artifactId"); //$NON-NLS-1$ + //when would this be null? + if (location != null) { + openLocation = location; + break; + } + } + } + } + } + } + if (region.isPlugin) { + Build build = mdl.getBuild(); + if (build != null) { + PluginManagement pm = build.getPluginManagement(); + if (pm != null) { + List<Plugin> list = pm.getPlugins(); + String id = Plugin.constructKey(region.groupId, region.artifactId); + if (list != null) { + for (Plugin plg : list) { + if (id.equals(plg.getKey())) { + InputLocation location = plg.getLocation("artifactId"); //$NON-NLS-1$ + //when would this be null? + if (location != null) { + openLocation = location; + break; + } + } + } + } + } + } + } + return openLocation; + } + + static String getElementTextValue(Node element) { + if (element == null) return null; + StringBuffer buff = new StringBuffer(); + NodeList list = element.getChildNodes(); + for (int i = 0; i < list.getLength(); i++) { + Node child = list.item(i); + if (child instanceof Text) { + Text text = (Text)child; + buff.append(text.getData()); + } + } + return buff.toString(); + } + + static ExpressionRegion findExpressionRegion(Node current, ITextViewer viewer, int offset) { + if (current != null && current instanceof Text) { + Text node = (Text)current; + String value = node.getNodeValue(); + if (value != null) { + assert node instanceof IndexedRegion; + IndexedRegion reg = (IndexedRegion)node; + int index = offset - reg.getStartOffset(); + String before = value.substring(0, Math.min (index + 1, value.length())); + String after = value.substring(Math.min (index + 1, value.length())); + int start = before.lastIndexOf("${"); //$NON-NLS-1$ + if (before.lastIndexOf("}") > start) {//$NON-NLS-1$ + //we might be in between two expressions.. + start = -1; + } + int end = after.indexOf("}"); //$NON-NLS-1$ + if (after.indexOf("${") != -1 && after.indexOf("${") < end) {//$NON-NLS-1$ + //we might be in between two expressions.. + end = -1; + } + if (start > -1 && end > -1) { + final int startOffset = reg.getStartOffset() + start; + final String expr = before.substring(start) + after.substring(0, end + 1); + final int length = expr.length(); + final String prop = before.substring(start + 2) + after.substring(0, end); +// there are often properties that start with project. eg. project.build.sourceEncoding +// if (prop.startsWith("project.") || prop.startsWith("pom.")) { //$NON-NLS-1$ //$NON-NLS-2$ +// return null; //ignore these, not in properties section. +// } + final IProject prj = PomContentAssistProcessor.extractProject(viewer); + //TODO we shall rely on presence of a cached model, not project alone.. ]MNGECLIPSE-2540 + if (prj != null) { + return new ExpressionRegion(startOffset, length, prop, prj); + } + } + } + } + return null; + } + /** + * converts an InputLocation to a file path on the local disk, null if not available. + * still the input source's model value can be used further.. + * @param location + * @return + */ + static File fileForInputLocation(InputLocation location) { + InputSource source = location.getSource(); + if (source != null) { + //MNGECLIPSE-2539 apparently if maven can't resolve the model from local storage, + //the location will be empty. not only applicable to local repo models but + //apparently also to models in workspace not reachable by relativePath + String loc = source.getLocation(); + File file = null; + if (loc != null) { + file = new File(loc); + } else { + //try to find pom by coordinates.. + String modelId = source.getModelId(); + String[] splitStrings = modelId.split(":"); + assert splitStrings.length == 3; + IMavenProjectFacade facade = MavenPlugin.getDefault().getMavenProjectManager().getMavenProject(splitStrings[0], splitStrings[1], splitStrings[2]); + if (facade != null) { + file = facade.getPomFile(); + } + } + return file; + } + return null; + } + + + private IHyperlink openPropertyDefinition(Node current, ITextViewer viewer, int offset) { + final ExpressionRegion region = findExpressionRegion(current, viewer, offset); + if (region != null) { + return new IHyperlink() { + public IRegion getHyperlinkRegion() { + return region; + } + + public String getHyperlinkText() { + return NLS.bind(Messages.PomHyperlinkDetector_open_property, region.property); + } + + public String getTypeLabel() { + return "pom-property-expression"; //$NON-NLS-1$ + } + + public void open() { + //see if we can find the plugin in plugin management of resolved project. + IMavenProjectFacade mvnproject = MavenPlugin.getDefault().getMavenProjectManager().getProject(region.project); + if(mvnproject != null) { + MavenProject mavprj = mvnproject.getMavenProject(); + if(mavprj != null) { + Model mdl = mavprj.getModel(); + if (mdl.getProperties().containsKey(region.property)) { + InputLocation location = mdl.getLocation( "properties" ).getLocation( region.property ); //$NON-NLS-1$ + if (location != null) { + File file = fileForInputLocation(location); + if (file != null) { + IFileStore fileStore = EFS.getLocalFileSystem().getStore(file.toURI()); + openXmlEditor(fileStore, location.getLineNumber(), location.getColumnNumber()); + } + } + } + } + } + } + }; + } + return null; + } + + private IHyperlink openModule(Fragment fragment, ITextViewer textViewer) { + final Fragment module = getValue(fragment, "<module>", "</module>"); //$NON-NLS-1$ //$NON-NLS-2$ + + ITextFileBuffer buf = FileBuffers.getTextFileBufferManager().getTextFileBuffer(textViewer.getDocument()); + IFileStore folder = buf.getFileStore().getParent(); + + String path = module.text; + //construct IPath for the child pom file, handle relative paths.. + while(folder != null && path.startsWith("../")) { //NOI18N //$NON-NLS-1$ + folder = folder.getParent(); + path = path.substring("../".length());//NOI18N //$NON-NLS-1$ + } + if(folder == null) { + return null; + } + IFileStore modulePom = folder.getChild(path); + if(!modulePom.getName().endsWith("xml")) {//NOI18N //$NON-NLS-1$ + modulePom = modulePom.getChild("pom.xml");//NOI18N //$NON-NLS-1$ + } + final IFileStore fileStore = modulePom; + if (!fileStore.fetchInfo().exists()) { + return null; + } + + IHyperlink pomHyperlink = new IHyperlink() { + public IRegion getHyperlinkRegion() { + return new Region(module.offset, module.length); + } + + public String getHyperlinkText() { + return NLS.bind(Messages.PomHyperlinkDetector_open_module, module.text); + } + + public String getTypeLabel() { + return "pom-module"; //$NON-NLS-1$ + } + + public void open() { + openXmlEditor(fileStore); + } + }; + + return pomHyperlink; + + } + + private IHyperlink openPOMbyID(Fragment fragment, final ITextViewer viewer) { + final Fragment groupId = getValue(fragment, "<groupId>", "</groupId>"); //$NON-NLS-1$ //$NON-NLS-2$ + final Fragment artifactId = getValue(fragment, "<artifactId>", Messages.PomHyperlinkDetector_23); //$NON-NLS-1$ + final Fragment version = getValue(fragment, "<version>", "</version>"); //$NON-NLS-1$ //$NON-NLS-2$ + final IProject prj = PomContentAssistProcessor.extractProject(viewer); + + IHyperlink pomHyperlink = new IHyperlink() { + public IRegion getHyperlinkRegion() { + //the goal here is to have the groupid/artifactid/version combo underscored by the link. + //that will prevent underscoring big portions (like plugin config) underscored and + // will also handle cases like dependencies within plugins. + int max = groupId != null ? groupId.offset + groupId.length : Integer.MIN_VALUE; + int min = groupId != null ? groupId.offset : Integer.MAX_VALUE; + max = Math.max(max, artifactId != null ? artifactId.offset + artifactId.length : Integer.MIN_VALUE); + min = Math.min(min, artifactId != null ? artifactId.offset : Integer.MAX_VALUE); + max = Math.max(max, version != null ? version.offset + version.length : Integer.MIN_VALUE); + min = Math.min(min, version != null ? version.offset : Integer.MAX_VALUE); + return new Region(min, max - min); + } + + public String getHyperlinkText() { + return NLS.bind(Messages.PomHyperlinkDetector_hyperlink_pattern, groupId, artifactId); + } + + public String getTypeLabel() { + return "pom"; //$NON-NLS-1$ + } + + public void open() { + new Job(Messages.PomHyperlinkDetector_job_name) { + protected IStatus run(IProgressMonitor monitor) { + // TODO resolve groupId if groupId==null + String gridString = groupId == null ? "org.apache.maven.plugins" : groupId.text; //$NON-NLS-1$ + String artidString = artifactId == null ? null : artifactId.text; + String versionString = version == null ? null : version.text; + if (prj != null && gridString != null && artidString != null && (version == null || version.text.contains("${"))) { //$NON-NLS-1$ + try { + //TODO how do we decide here if the hyperlink is a dependency or a plugin + // hyperlink?? + versionString = PomTemplateContext.extractVersion(prj, versionString, gridString, artidString, PomTemplateContext.EXTRACT_STRATEGY_DEPENDENCY); + + } catch(CoreException e) { + versionString = null; + } + } + if (versionString == null) { + return Status.OK_STATUS; + } + OpenPomAction.openEditor(gridString, + artidString, + versionString, monitor); + return Status.OK_STATUS; + } + }.schedule(); + } + + }; + + return pomHyperlink; + } + + /** + * fragment offset returned contains the xml elements + * while the text only includes the element text value + */ + private Fragment getValue(Fragment section, String startTag, String endTag) { + int start = section.text.indexOf(startTag); + if(start == -1) { + return null; + } + int end = section.text.indexOf(endTag); + if(end == -1) { + return null; + } + + return new Fragment(section.text.substring(start + startTag.length(), end).trim(), section.offset + start, end + endTag.length() - start); + } + + /** + * returns the text, offset and length of the xml element. text includes the xml tags. + */ + private Fragment getFragment(String text, int offset, String startTag, String endTag) { + int start = text.substring(0, offset).lastIndexOf(startTag); + if(start == -1) { + return null; + } + + int end = text.indexOf(endTag, start); + if(end == -1 || end <= offset) { + return null; + } + end = end + endTag.length(); + return new Fragment(text.substring(start, end), start, end - start); + } + + private static class Fragment { + final int length; + final int offset; + final String text; + + Fragment(String text, int start, int len) { + this.text = text; + this.offset = start; + + this.length = len; + + } + + @Override + public String toString() { + return text; + } + } + + + /** + * copied from org.eclipse.wst.xml.ui.internal.hyperlink.XMLHyperlinkDetector + * Returns the node the cursor is currently on in the document. null if no + * node is selected + * + * returned value is also an instance of IndexedRegion + * + * @param offset + * @return Node either element, doctype, text, or null + */ + static Node getCurrentNode(IDocument document, int offset) { + // get the current node at the offset (returns either: element, + // doctype, text) + IndexedRegion inode = null; + IStructuredModel sModel = null; + try { + sModel = StructuredModelManager.getModelManager().getExistingModelForRead(document); + if (sModel != null) { + inode = sModel.getIndexedRegion(offset); + if (inode == null) { + inode = sModel.getIndexedRegion(offset - 1); + } + } + } + finally { + if (sModel != null) { + sModel.releaseFromRead(); + } + } + + if (inode instanceof Node) { + return (Node) inode; + } + return null; + } + + private void openXmlEditor(final IFileStore fileStore) { + openXmlEditor(fileStore, -1, -1); + } + + private void openXmlEditor(final IFileStore fileStore, int line, int column) { + IWorkbenchWindow window = PlatformUI.getWorkbench().getActiveWorkbenchWindow(); + if(window != null) { + IWorkbenchPage page = window.getActivePage(); + if(page != null) { + try { + IEditorPart part = IDE.openEditorOnFileStore(page, fileStore); + if(part instanceof FormEditor) { + FormEditor ed = (FormEditor) part; + ed.setActivePage(null); //null means source, always or just in the case of MavenPomEditor? + if(line != -1) { + if(ed.getActiveEditor() instanceof StructuredTextEditor) { + StructuredTextEditor structured = (StructuredTextEditor) ed.getActiveEditor(); + // convert the line and Column numbers to an offset: + IDocument doc = structured.getTextViewer().getDocument(); + if (doc instanceof IStructuredDocument) { + IStructuredDocument document = (IStructuredDocument) doc; + try { + int offset = document.getLineOffset(line - 1); + structured.selectAndReveal(offset + column - 1, 0); + } catch(BadLocationException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + } + } + } + } catch(PartInitException e) { + MessageDialog.openInformation( + Display.getDefault().getActiveShell(), // + Messages.PomHyperlinkDetector_error_title, + NLS.bind(Messages.PomHyperlinkDetector_error_message, fileStore, e.toString())); + + } + } + } + } + + + static class ExpressionRegion implements IRegion { + + final String property; + private int length; + private int offset; + final IProject project; + + public ExpressionRegion(int startOffset, int length, String prop, IProject project) { + this.offset = startOffset; + this.length = length; + this.property = prop; + this.project = project; + } + + public int getLength() { + return length; + } + + public int getOffset() { + return offset; + } + } + + static class ManagedArtifactRegion implements IRegion { + + private int length; + private int offset; + final IProject project; + final String groupId; + final String artifactId; + final boolean isPlugin; + final boolean isDependency; + + public ManagedArtifactRegion(int startOffset, int length, String groupId, String artifactId, boolean isDependency, boolean isPlugin, IProject project) { + this.offset = startOffset; + this.length = length; + this.project = project; + this.artifactId = artifactId; + this.groupId = groupId; + this.isDependency = isDependency; + this.isPlugin = isPlugin; + } + + public int getLength() { + return length; + } + + public int getOffset() { + return offset; + } + } + + +} diff --git a/org.eclipse.m2e.editor.xml/src/main/java/org/eclipse/m2e/editor/xml/PomModelHandler.java b/org.eclipse.m2e.editor.xml/src/main/java/org/eclipse/m2e/editor/xml/PomModelHandler.java new file mode 100644 index 00000000..cd68f514 --- /dev/null +++ b/org.eclipse.m2e.editor.xml/src/main/java/org/eclipse/m2e/editor/xml/PomModelHandler.java @@ -0,0 +1,133 @@ +/******************************************************************************* + * 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.util.ArrayList; +import java.util.List; + +import org.w3c.dom.Element; + +import org.eclipse.wst.common.uriresolver.internal.provisional.URIResolver; +import org.eclipse.wst.sse.core.internal.provisional.IModelLoader; +import org.eclipse.wst.sse.core.internal.provisional.INodeAdapter; +import org.eclipse.wst.sse.core.internal.provisional.INodeAdapterFactory; +import org.eclipse.wst.sse.core.internal.provisional.INodeNotifier; +import org.eclipse.wst.xml.core.internal.contentmodel.CMDocument; +import org.eclipse.wst.xml.core.internal.contentmodel.CMElementDeclaration; +import org.eclipse.wst.xml.core.internal.contentmodel.modelquery.CMDocumentManager; +import org.eclipse.wst.xml.core.internal.contentmodel.modelqueryimpl.ModelQueryImpl; +import org.eclipse.wst.xml.core.internal.contentmodel.util.CMDocumentCache; +import org.eclipse.wst.xml.core.internal.contentmodel.util.NamespaceTable; +import org.eclipse.wst.xml.core.internal.modelhandler.ModelHandlerForXML; +import org.eclipse.wst.xml.core.internal.modelhandler.XMLModelLoader; +import org.eclipse.wst.xml.core.internal.modelquery.ModelQueryAdapterFactoryForXML; +import org.eclipse.wst.xml.core.internal.modelquery.XMLModelQueryAssociationProvider; +import org.eclipse.wst.xml.core.internal.ssemodelquery.ModelQueryAdapter; +import org.eclipse.wst.xml.core.internal.ssemodelquery.ModelQueryAdapterImpl; + + + +@SuppressWarnings("restriction") +public class PomModelHandler extends ModelHandlerForXML { + + private static final String ASSOCIATED_CONTENT_TYPE_ID = "org.eclipse.m2e.pomFile"; //$NON-NLS-1$ + + private static final String POM_NAMESPACE = "http://maven.apache.org/POM/4.0.0"; //$NON-NLS-1$ + + private static final String POM_XSD = "http://maven.apache.org/xsd/maven-4.0.0.xsd"; //$NON-NLS-1$ + + public PomModelHandler() { + super(); + setAssociatedContentTypeId(ASSOCIATED_CONTENT_TYPE_ID); + } + + @Override + public IModelLoader getModelLoader() { + return new PomModelLoader(); + } + + private static class PomModelLoader extends XMLModelLoader { + + @SuppressWarnings("unchecked") + @Override + public List getAdapterFactories() { + List result = new ArrayList(); + INodeAdapterFactory factory = new ModelQueryAdapterFactoryForPom(); + result.add(factory); + return result; + } + + } + + static class ModelQueryAdapterFactoryForPom extends ModelQueryAdapterFactoryForXML { + + protected ModelQueryAdapterImpl modelQueryAdapterImpl; + + @Override + protected INodeAdapter createAdapter(INodeNotifier target) { + if(modelQueryAdapterImpl == null) { + ModelQueryAdapter mqa = (ModelQueryAdapter) super.createAdapter(target); + modelQueryAdapterImpl = new ModelQueryAdapterImpl(mqa.getCMDocumentCache(), new PomModelQueryImpl(mqa + .getCMDocumentCache(), mqa.getIdResolver()), mqa.getIdResolver()); + } + return modelQueryAdapterImpl; + } + + } + + static class PomModelQueryImpl extends ModelQueryImpl { + + public PomModelQueryImpl(CMDocumentCache cache, URIResolver idResolver) { + super(new PomModelQueryAssociationProvider(cache, idResolver)); + } + + } + + static class PomModelQueryAssociationProvider extends XMLModelQueryAssociationProvider { + + public PomModelQueryAssociationProvider(CMDocumentCache cache, URIResolver idResolver) { + super(cache, idResolver); + } + + @Override + public CMDocument getCMDocument(String publicId, String systemId, String type) { + if("".equals(publicId) && "".equals(systemId)) { //$NON-NLS-1$ //$NON-NLS-2$ + return null; + } + + return super.getCMDocument(publicId, systemId, type); + } + + @Override + public CMElementDeclaration getCMElementDeclaration(Element element) { + CMElementDeclaration result = super.getCMElementDeclaration(element); + + if(result == null) { + NamespaceTable namespaceTable = new NamespaceTable(element.getOwnerDocument()); + List list = NamespaceTable.getElementLineage(element); + Element rootElement = (Element) list.get(0); + namespaceTable.addElement(rootElement); + + documentManager.setPropertyEnabled(CMDocumentManager.PROPERTY_ASYNC_LOAD, false); + documentManager.addCMDocumentReference(POM_NAMESPACE, POM_XSD, "XSD"); //$NON-NLS-1$ + namespaceTable.addNamespaceInfo("", POM_NAMESPACE, ""); //$NON-NLS-1$ //$NON-NLS-2$ + + if(namespaceTable.isNamespaceEncountered()) { + result = getCMElementDeclaration(element, list, namespaceTable); + } + } + + return result; + } + } + +} diff --git a/org.eclipse.m2e.editor.xml/src/main/java/org/eclipse/m2e/editor/xml/PomQuickAssistProcessor.java b/org.eclipse.m2e.editor.xml/src/main/java/org/eclipse/m2e/editor/xml/PomQuickAssistProcessor.java new file mode 100644 index 00000000..b12153c1 --- /dev/null +++ b/org.eclipse.m2e.editor.xml/src/main/java/org/eclipse/m2e/editor/xml/PomQuickAssistProcessor.java @@ -0,0 +1,678 @@ +/******************************************************************************* + * 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.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.Text; + +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IMarker; +import org.eclipse.core.resources.IResource; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.jface.text.BadLocationException; +import org.eclipse.jface.text.IDocument; +import org.eclipse.jface.text.Position; +import org.eclipse.jface.text.contentassist.ICompletionProposal; +import org.eclipse.jface.text.contentassist.ICompletionProposalExtension5; +import org.eclipse.jface.text.contentassist.IContextInformation; +import org.eclipse.jface.text.quickassist.IQuickAssistInvocationContext; +import org.eclipse.jface.text.quickassist.IQuickAssistProcessor; +import org.eclipse.jface.text.source.Annotation; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.graphics.Point; +import org.eclipse.swt.widgets.Display; +import org.eclipse.text.edits.DeleteEdit; +import org.eclipse.text.edits.InsertEdit; +import org.eclipse.ui.IEditorPart; +import org.eclipse.ui.IMarkerResolution; +import org.eclipse.ui.internal.WorkbenchPlugin; +import org.eclipse.ui.texteditor.MarkerAnnotation; +import org.eclipse.wst.sse.core.StructuredModelManager; +import org.eclipse.wst.sse.core.internal.provisional.IndexedRegion; +import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocument; +import org.eclipse.wst.sse.core.utils.StringUtils; +import org.eclipse.wst.xml.core.internal.provisional.document.IDOMModel; + +import org.eclipse.m2e.core.core.IMavenConstants; +import org.eclipse.m2e.core.core.MavenLogger; +import org.eclipse.m2e.core.internal.project.MavenMarkerManager; +import org.eclipse.m2e.editor.xml.internal.Messages; + +public class PomQuickAssistProcessor implements IQuickAssistProcessor { + private static final String GROUP_ID_NODE = "groupId"; //$NON-NLS-1$ + private static final String ARTIFACT_ID_NODE = "artifactId"; //$NON-NLS-1$ + private static final String VERSION_NODE = "version"; //$NON-NLS-1$ + + public static final String PROJECT_NODE = "project"; //$NON-NLS-1$ + public static final String XSI_VALUE = " xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n"+ //$NON-NLS-1$ + "xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\""; //$NON-NLS-1$ + + public boolean canAssist(IQuickAssistInvocationContext arg0) { + return true; + } + + public boolean canFix(Annotation an) { + if(an instanceof MarkerAnnotation) { + MarkerAnnotation mark = (MarkerAnnotation) an; + String hint = mark.getMarker().getAttribute(IMavenConstants.MARKER_ATTR_EDITOR_HINT, null); + if(hint != null) { + return true; + } + } + return false; + } + + public ICompletionProposal[] computeQuickAssistProposals(IQuickAssistInvocationContext context) { + List<ICompletionProposal> proposals = new ArrayList<ICompletionProposal>(); + Iterator<Annotation> annotationIterator = context.getSourceViewer().getAnnotationModel().getAnnotationIterator(); + while(annotationIterator.hasNext()) { + Annotation annotation = annotationIterator.next(); + if(annotation instanceof MarkerAnnotation) { + MarkerAnnotation mark = (MarkerAnnotation) annotation; + try { + Position position = context.getSourceViewer().getAnnotationModel().getPosition(annotation); + int lineNum = context.getSourceViewer().getDocument().getLineOfOffset(position.getOffset()) + 1; + int currentLineNum = context.getSourceViewer().getDocument().getLineOfOffset(context.getOffset()) + 1; + if(currentLineNum == lineNum) { + String hint = mark.getMarker().getAttribute(IMavenConstants.MARKER_ATTR_EDITOR_HINT, null); + if(hint != null) { + if(hint.equals(IMavenConstants.EDITOR_HINT_PARENT_GROUP_ID)) { + proposals.add(new IdPartRemovalProposal(context, false, mark)); + } else if(hint.equals(IMavenConstants.EDITOR_HINT_PARENT_VERSION)) { + proposals.add(new IdPartRemovalProposal(context, true, mark)); + } else if(hint.equals(IMavenConstants.EDITOR_HINT_MANAGED_DEPENDENCY_OVERRIDE)) { + proposals.add(new ManagedVersionRemovalProposal(context, true, mark)); + //add a proposal to ignore the marker + proposals.add(new IgnoreWarningProposal(context, mark, IMavenConstants.MARKER_IGNORE_MANAGED)); + } else if(hint.equals(IMavenConstants.EDITOR_HINT_MANAGED_PLUGIN_OVERRIDE)) { + proposals.add(new ManagedVersionRemovalProposal(context, false, mark)); + //add a proposal to ignore the marker + proposals.add(new IgnoreWarningProposal(context, mark, IMavenConstants.MARKER_IGNORE_MANAGED)); + } else if(hint.equals(IMavenConstants.EDITOR_HINT_MISSING_SCHEMA)) { + proposals.add(new SchemaCompletionProposal(context, mark)); + } + } + } + } catch(Exception e) { + MvnIndexPlugin.getDefault().getLog().log(new Status(IStatus.ERROR, MvnIndexPlugin.PLUGIN_ID, "Exception in pom quick assist.", e)); + } + } + } + + if(proposals.size() > 0) { + return proposals.toArray(new ICompletionProposal[0]); + } + return null; + } + + public String getErrorMessage() { + return null; + } + + static Element getRootElement(IDocument doc) { + IDOMModel domModel = null; + try { + domModel = (IDOMModel) StructuredModelManager.getModelManager().getExistingModelForRead(doc); + IStructuredDocument document = domModel.getStructuredDocument(); + Element root = domModel.getDocument().getDocumentElement(); + return root; + } finally { + if (domModel != null) { + domModel.releaseFromRead(); + } + } + } + + static IStructuredDocument getDocument(IMarker marker) { + if (marker.getResource().getType() == IResource.FILE) + { + IDOMModel domModel = null; + try { + domModel = (IDOMModel)StructuredModelManager.getModelManager().getModelForEdit((IFile)marker.getResource()); + return domModel.getStructuredDocument(); + } catch(Exception e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } finally { + if (domModel != null) { + domModel.releaseFromRead(); + } + } + } + return null; + } + + static String previewForRemovedElement(IDocument doc, Element removed) { + if (removed != null && removed instanceof IndexedRegion) { + IndexedRegion reg = (IndexedRegion)removed; + try { + int line = doc.getLineOfOffset(reg.getStartOffset()); + int startLine = doc.getLineOffset(line); + int prev2 = doc.getLineOffset(Math.max(line - 2, 0)); + String prevString = StringUtils.convertToHTMLContent(doc.get(prev2, startLine - prev2)); + String currentLine = doc.get(startLine, doc.getLineLength(line)); + int nextLine = Math.min(line + 2, doc.getNumberOfLines() - 1); + int next2End = doc.getLineOffset(nextLine) + doc.getLineLength(nextLine); + int next2Start = startLine + doc.getLineLength( line ) + 1; + String nextString = StringUtils.convertToHTMLContent(doc.get(next2Start, next2End - next2Start)); + return "<html>...<br>" + prevString + /**"<del>" + currentLine + "</del>" +*/ nextString + "...<html>"; //$NON-NLS-1$ //$NON-NLS-2$ + } catch(BadLocationException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + return null; + } + +class SchemaCompletionProposal implements ICompletionProposal, ICompletionProposalExtension5 { + + IQuickAssistInvocationContext context; + private MarkerAnnotation annotation; + public SchemaCompletionProposal(IQuickAssistInvocationContext context, MarkerAnnotation mark){ + this.context = context; + annotation = mark; + } + + public void apply(IDocument doc) { + IDOMModel domModel = null; + try { + domModel = (IDOMModel) StructuredModelManager.getModelManager().getExistingModelForRead(doc); + IStructuredDocument document = domModel.getStructuredDocument(); + Element root = domModel.getDocument().getDocumentElement(); + + //now check parent version and groupid against the current project's ones.. + if (root.getNodeName().equals(PomQuickAssistProcessor.PROJECT_NODE)) { //$NON-NLS-1$ + if (root instanceof IndexedRegion) { + IndexedRegion off = (IndexedRegion) root; + + int offset = off.getStartOffset() + PomQuickAssistProcessor.PROJECT_NODE.length() + 1; + if (offset <= 0) { + return; + } + InsertEdit edit = new InsertEdit(offset, PomQuickAssistProcessor.XSI_VALUE); + try { + edit.apply(doc); + annotation.getMarker().delete(); + Display.getDefault().asyncExec(new Runnable() { + public void run() { + IEditorPart activeEditor = MvnIndexPlugin.getDefault().getWorkbench().getActiveWorkbenchWindow() + .getActivePage().getActiveEditor(); + MvnIndexPlugin.getDefault().getWorkbench().getActiveWorkbenchWindow().getActivePage() + .saveEditor(activeEditor, false); + } + }); + } catch(Exception e) { + MavenLogger.log("Unable to insert schema info", e); //$NON-NLS-1$ + } + } + } + } finally { + if (domModel != null) { + domModel.releaseFromRead(); + } + } + } + + public String getAdditionalProposalInfo() { + //NOT TO BE REALLY IMPLEMENTED, we have the other method + return null; + } + + public IContextInformation getContextInformation() { + return null; + } + + public String getDisplayString() { + return Messages.PomQuickAssistProcessor_name; + } + + public Image getImage() { + return WorkbenchPlugin.getDefault().getImageRegistry().get(org.eclipse.ui.internal.SharedImages.IMG_OBJ_ADD); + } + + public Point getSelection(IDocument arg0) { + return null; + } + + public Object getAdditionalProposalInfo(IProgressMonitor monitor) { + // TODO Auto-generated method stub + return "<html>...<br><project <b>" + PomQuickAssistProcessor.XSI_VALUE + "</b>><br>...</html>"; //$NON-NLS-1$ //$NON-NLS-2$ + } + +} + + +static class IdPartRemovalProposal implements ICompletionProposal, ICompletionProposalExtension5, IMarkerResolution { + + private IQuickAssistInvocationContext context; + private final boolean isVersion; + private final IMarker marker; + public IdPartRemovalProposal(IQuickAssistInvocationContext context, boolean version, MarkerAnnotation mark) { + this.context = context; + isVersion = version; + marker = mark.getMarker(); + } + + public IdPartRemovalProposal(IMarker marker, boolean version) { + this.marker = marker; + isVersion = version; + } + + public void apply(IDocument doc) { + Element root = getRootElement(doc); + processFix(doc, root, isVersion, marker); + } + + private void processFix(IDocument doc, Element root, boolean isversion, IMarker marker) { + //now check parent version and groupid against the current project's ones.. + if (root.getNodeName().equals(PomQuickAssistProcessor.PROJECT_NODE)) { //$NON-NLS-1$ + Element value = MavenMarkerManager.findChildElement(root, isversion ? VERSION_NODE : GROUP_ID_NODE); //$NON-NLS-1$ //$NON-NLS-2$ + if (value != null && value instanceof IndexedRegion) { + IndexedRegion off = (IndexedRegion) value; + + int offset = off.getStartOffset(); + if (offset <= 0) { + return; + } + Node prev = value.getNextSibling(); + if (prev instanceof Text) { + //check the content as well?? + off = ((IndexedRegion) prev); + } + DeleteEdit edit = new DeleteEdit(offset, off.getEndOffset() - offset); + try { + edit.apply(doc); + marker.delete(); + } catch(Exception e) { + MavenLogger.log("Unable to remove the element", e); //$NON-NLS-1$ + } + } + } + } + + public String getAdditionalProposalInfo() { + return null; + } + + public IContextInformation getContextInformation() { + return null; + } + + public String getDisplayString() { + return isVersion ? Messages.PomQuickAssistProcessor_title_version : Messages.PomQuickAssistProcessor_title_groupId; + } + + public Image getImage() { + return WorkbenchPlugin.getDefault().getImageRegistry().get(org.eclipse.ui.internal.SharedImages.IMG_TOOL_DELETE); + } + + public Point getSelection(IDocument arg0) { + return null; + } + + public Object getAdditionalProposalInfo(IProgressMonitor monitor) { + if (context == null) { + //no context in markerresolution, just to be sure.. + return null; + } + IDocument doc = context.getSourceViewer().getDocument(); + IDOMModel domModel = null; + try { + domModel = (IDOMModel) StructuredModelManager.getModelManager().getExistingModelForRead(doc); +// IStructuredDocument document = domModel.getStructuredDocument(); + Element root = domModel.getDocument().getDocumentElement(); + + //now check parent version and groupid against the current project's ones.. + if (root.getNodeName().equals(PomQuickAssistProcessor.PROJECT_NODE)) { //$NON-NLS-1$ + Element value = MavenMarkerManager.findChildElement(root, isVersion ? VERSION_NODE : GROUP_ID_NODE); //$NON-NLS-1$ //$NON-NLS-2$ + String toRet = previewForRemovedElement(doc, value); + if (toRet != null) { + return toRet; + } + } + } finally { + if (domModel != null) { + domModel.releaseFromRead(); + } + } + return Messages.PomQuickAssistProcessor_remove_hint; + } + + public String getLabel() { + return getDisplayString(); + } + + public void run(IMarker marker) { + IStructuredDocument doc = getDocument(marker); + if (doc != null) { + Element root = getRootElement(doc); + processFix(doc, root, isVersion, marker); + } + } +} + +static class ManagedVersionRemovalProposal implements ICompletionProposal, ICompletionProposalExtension5, IMarkerResolution { + + private IQuickAssistInvocationContext context; + private final boolean isDependency; + private final IMarker marker; + public ManagedVersionRemovalProposal(IQuickAssistInvocationContext context, boolean dependency, MarkerAnnotation mark) { + this.context = context; + isDependency = dependency; + marker = mark.getMarker(); + } + + public ManagedVersionRemovalProposal(IMarker marker, boolean dependency) { + this.marker = marker; + isDependency = dependency; + } + + + + public void apply(IDocument doc) { + Element root = getRootElement(doc); + processFix(doc, root, isDependency, marker); + + } + + private void processFix(IDocument doc, Element root, boolean isdep, IMarker marker) { + if (root.getNodeName().equals(PomQuickAssistProcessor.PROJECT_NODE)) { + Element artifact = findArtifactElement(root, isdep, marker); + if (artifact == null) { + //TODO report somehow? + MavenLogger.log("Unable to find the marked element"); //$NON-NLS-1$ + return; + } + Element value = MavenMarkerManager.findChildElement(artifact, VERSION_NODE); //$NON-NLS-1$ //$NON-NLS-2$ + if (value != null && value instanceof IndexedRegion) { + IndexedRegion off = (IndexedRegion) value; + + int offset = off.getStartOffset(); + if (offset <= 0) { + return; + } + Node prev = value.getNextSibling(); + if (prev instanceof Text) { + //check the content as well?? + off = ((IndexedRegion) prev); + } + DeleteEdit edit = new DeleteEdit(offset, off.getEndOffset() - offset); + try { + edit.apply(doc); + marker.delete(); + } catch(Exception e) { + MavenLogger.log("Unable to remove the element", e); //$NON-NLS-1$ + } + } + } + } + + private Element findArtifactElement(Element root, boolean isdep, IMarker marker) { + if (root == null) { + return null; + } + String groupId = marker.getAttribute("groupId", null); + String artifactId = marker.getAttribute("artifactId", null); + assert groupId != null; + assert artifactId != null; + + String profile = marker.getAttribute("profile", null); + Element artifactParent = root; + if (profile != null) { + Element profileRoot = MavenMarkerManager.findChildElement(root, "profiles"); + if (profileRoot != null) { + for (Element prf : MavenMarkerManager.findChildElements(profileRoot, "profile")) { + if (profile.equals(MavenMarkerManager.getElementTextValue(MavenMarkerManager.findChildElement(prf, "id")))) { + artifactParent = prf; + break; + } + } + } + } + if (!isdep) { + //we have plugins now, need to go one level down to build + artifactParent = MavenMarkerManager.findChildElement(artifactParent, "build"); + } + if (artifactParent == null) { + return null; + } + Element list = MavenMarkerManager.findChildElement(artifactParent, isdep ? "dependencies" : "plugins"); + if (list == null) { + return null; + } + Element artifact = null; + for (Element art : MavenMarkerManager.findChildElements(list, isdep ? "dependency" : "plugin")) { + String grpString = MavenMarkerManager.getElementTextValue(MavenMarkerManager.findChildElement(art, GROUP_ID_NODE)); + String artString = MavenMarkerManager.getElementTextValue(MavenMarkerManager.findChildElement(art, ARTIFACT_ID_NODE)); + if (groupId.equals(grpString) && artifactId.equals(artString)) { + artifact = art; + break; + } + } + return artifact; + } + + public String getAdditionalProposalInfo() { + return null; + } + + public IContextInformation getContextInformation() { + return null; + } + + public String getDisplayString() { + return Messages.PomQuickAssistProcessor_title_version; + } + + public Image getImage() { + return WorkbenchPlugin.getDefault().getImageRegistry().get(org.eclipse.ui.internal.SharedImages.IMG_TOOL_DELETE); + } + + public Point getSelection(IDocument arg0) { + return null; + } + + public Object getAdditionalProposalInfo(IProgressMonitor monitor) { + if (context == null) { + //no context in markerresolution, just to be sure.. + return null; + } + IDocument doc = context.getSourceViewer().getDocument(); + IDOMModel domModel = null; + try { + domModel = (IDOMModel) StructuredModelManager.getModelManager().getExistingModelForRead(doc); + Element root = domModel.getDocument().getDocumentElement(); + Element artifact = findArtifactElement(root, isDependency, marker); + if (artifact != null) { + Element value = MavenMarkerManager.findChildElement(artifact, VERSION_NODE); + String toRet = previewForRemovedElement(doc, value); + if (toRet != null) { + return toRet; + } + } + } finally { + if (domModel != null) { + domModel.releaseFromRead(); + } + } + return Messages.PomQuickAssistProcessor_remove_hint; + } + + public String getLabel() { + return getDisplayString(); + } + + public void run(IMarker marker) { + IStructuredDocument doc = getDocument(marker); + if (doc != null) { + Element root = getRootElement(doc); + processFix(doc, root, isDependency, marker); + } + } +} + +static class IgnoreWarningProposal implements ICompletionProposal, ICompletionProposalExtension5, IMarkerResolution { + + private IQuickAssistInvocationContext context; + private final IMarker marker; + private final String markupText; + public IgnoreWarningProposal(IQuickAssistInvocationContext context, MarkerAnnotation mark, String markupText) { + this.context = context; + marker = mark.getMarker(); + this.markupText = markupText; + } + + public IgnoreWarningProposal(IMarker marker, String markupText) { + this.marker = marker; + this.markupText = markupText; + } + + public void apply(IDocument doc) { + if (doc instanceof IStructuredDocument) { + processFix((IStructuredDocument) doc, marker); + } else { + IStructuredDocument strdoc = getDocument(marker); + if (strdoc != null) { + processFix(strdoc, marker); + } + } + } + + private void processFix(IStructuredDocument doc, IMarker marker) { + IDOMModel domModel = null; + try { + domModel = (IDOMModel) StructuredModelManager.getModelManager().getExistingModelForRead(doc); + int line; + if (context != null) { + line = doc.getLineOfOffset(context.getOffset()); + } else { + line = marker.getAttribute(IMarker.LINE_NUMBER, -1); + assert line != -1; + line = line - 1; + } + try { + int linestart = doc.getLineOffset(line); + int lineend = linestart + doc.getLineLength(line); + int start = linestart; + IndexedRegion reg = domModel.getIndexedRegion(start); + while (reg != null && !(reg instanceof Element) && start < lineend) { + reg = domModel.getIndexedRegion(reg.getEndOffset() + 1); + if (reg != null) { + start = reg.getStartOffset(); + } + } + if (reg != null && reg instanceof Element) { + InsertEdit edit = new InsertEdit(reg.getEndOffset(), "<!--" + markupText + "-->"); + try { + edit.apply(doc); + marker.delete(); + } catch(Exception e) { + MavenLogger.log("Unable to insert", e); //$NON-NLS-1$ + } + } + } catch(BadLocationException e1) { + // TODO Auto-generated catch block + e1.printStackTrace(); + } + } finally { + if (domModel != null) { + domModel.releaseFromRead(); + } + } + } + + public String getAdditionalProposalInfo() { + return null; + } + + public IContextInformation getContextInformation() { + return null; + } + + public String getDisplayString() { + return "Ignore this warning"; + } + + public Image getImage() { + return MvnImages.IMG_CLOSE; + } + + public Point getSelection(IDocument arg0) { + return null; + } + + public Object getAdditionalProposalInfo(IProgressMonitor monitor) { + if (context == null) { + //no context in markerresolution, just to be sure.. + return null; + } + IDOMModel domModel = null; + try { + IDocument doc = context.getSourceViewer().getDocument(); + domModel = (IDOMModel) StructuredModelManager.getModelManager().getExistingModelForRead(doc); + try { + //the offset of context is important here, not the offset of the marker!!! + //line/offset of marker only gets updated hen file gets saved. + //we need the proper handling also for unsaved documents.. + int line = doc.getLineOfOffset(context.getOffset()); + int linestart = doc.getLineOffset(line); + int lineend = linestart + doc.getLineLength(line); + int start = linestart; + IndexedRegion reg = domModel.getIndexedRegion(start); + while (reg != null && !(reg instanceof Element) && start < lineend) { + reg = domModel.getIndexedRegion(reg.getEndOffset() + 1); + if (reg != null) { + start = reg.getStartOffset(); + } + } + if (reg != null && reg instanceof Element) { //just a simple guard against moved marker + try { + int startLine = doc.getLineOffset(line); + String currentLine = StringUtils.convertToHTMLContent(doc.get(reg.getStartOffset(), reg.getEndOffset() - reg.getStartOffset())); + String insert = StringUtils.convertToHTMLContent("<!--" + markupText + "-->"); + return "<html>...<br>" + currentLine + "<b>" + insert + "</b><br>...<html>"; //$NON-NLS-1$ //$NON-NLS-2$ + } catch(BadLocationException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + } catch(BadLocationException e1) { + MavenLogger.log("Error while computing completion proposal", e1); + } + } finally { + if (domModel != null) { + domModel.releaseFromRead(); + } + } + return Messages.PomQuickAssistProcessor_remove_hint; + } + + public String getLabel() { + return getDisplayString(); + } + + public void run(IMarker marker) { + IStructuredDocument doc = getDocument(marker); + if (doc != null) { + processFix(doc, marker); + } + } +} + +} diff --git a/org.eclipse.m2e.editor.xml/src/main/java/org/eclipse/m2e/editor/xml/PomStructuredTextViewConfiguration.java b/org.eclipse.m2e.editor.xml/src/main/java/org/eclipse/m2e/editor/xml/PomStructuredTextViewConfiguration.java new file mode 100644 index 00000000..5f5b26b1 --- /dev/null +++ b/org.eclipse.m2e.editor.xml/src/main/java/org/eclipse/m2e/editor/xml/PomStructuredTextViewConfiguration.java @@ -0,0 +1,65 @@ +/******************************************************************************* + * 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 org.eclipse.jface.text.ITextHover; +import org.eclipse.jface.text.contentassist.IContentAssistProcessor; +import org.eclipse.jface.text.hyperlink.IHyperlinkDetector; +import org.eclipse.jface.text.quickassist.IQuickAssistAssistant; +import org.eclipse.jface.text.source.ISourceViewer; +import org.eclipse.wst.sse.core.text.IStructuredPartitions; +import org.eclipse.wst.xml.core.text.IXMLPartitions; +import org.eclipse.wst.xml.ui.StructuredTextViewerConfigurationXML; + +/** + * @author Lukas Krecan + */ +public class PomStructuredTextViewConfiguration extends StructuredTextViewerConfigurationXML { + + @Override + public IContentAssistProcessor[] getContentAssistProcessors(ISourceViewer sourceViewer, String partitionType) { + if(partitionType == IStructuredPartitions.DEFAULT_PARTITION || partitionType == IXMLPartitions.XML_DEFAULT) { + return new IContentAssistProcessor[] {new PomContentAssistProcessor(sourceViewer, this)}; + } + return super.getContentAssistProcessors(sourceViewer, partitionType); + } + + @Override + public ITextHover getTextHover(ISourceViewer sourceViewer, String contentType, int stateMask) { + return new PomTextHover(sourceViewer, contentType, stateMask); + } + + @Override + public IHyperlinkDetector[] getHyperlinkDetectors(ISourceViewer sourceViewer) { + IHyperlinkDetector[] detectors = super.getHyperlinkDetectors(sourceViewer); + if(detectors==null) { + detectors = new IHyperlinkDetector[0]; + } + + IHyperlinkDetector[] pomDetectors = new IHyperlinkDetector[detectors.length + 1]; + pomDetectors[0] = new PomHyperlinkDetector(); + System.arraycopy(detectors, 0, pomDetectors, 1, detectors.length); + + return pomDetectors; + } + + public IQuickAssistAssistant getQuickAssistAssistant(ISourceViewer sourceViewer) { + //not explicitly setting processor results in having a bunch of generic quick fixes around.. + //also see org.eclipse.wst.sse.ui.quickFixProcessor extension point regarding the way to declaratively + //register the pomquickassistprocessor + IQuickAssistAssistant quickAssistAssistant = super.getQuickAssistAssistant(sourceViewer); + quickAssistAssistant.setQuickAssistProcessor(new PomQuickAssistProcessor()); + return quickAssistAssistant; + } + +} + 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 new file mode 100644 index 00000000..0738befd --- /dev/null +++ b/org.eclipse.m2e.editor.xml/src/main/java/org/eclipse/m2e/editor/xml/PomTemplateContext.java @@ -0,0 +1,764 @@ +/******************************************************************************* + * 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.FileFilter; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Properties; + +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.Parameter; +import org.apache.maven.plugin.descriptor.PluginDescriptor; +import org.apache.maven.project.MavenProject; +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.codehaus.plexus.util.StringUtils; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; +import org.w3c.dom.Text; + +import org.eclipse.core.resources.IProject; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.jface.text.templates.Template; +import org.eclipse.osgi.util.NLS; + +import org.eclipse.m2e.core.MavenPlugin; +import org.eclipse.m2e.core.core.MavenLogger; +import org.eclipse.m2e.core.internal.project.MavenMarkerManager; +import org.eclipse.m2e.core.project.IMavenProjectFacade; +import org.eclipse.m2e.core.util.search.ArtifactInfo; +import org.eclipse.m2e.core.util.search.Packaging; +import org.eclipse.m2e.core.util.search.SearchEngine; +import org.eclipse.m2e.editor.xml.internal.Messages; + + +/** + * Context types. + * + * @author Lukas Krecan + * @author Eugene Kuleshov + */ +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$ + + MODULES("modules"), // //$NON-NLS-1$ + + PROPERTIES("properties"), // //$NON-NLS-1$ + + DEPENDENCIES("dependencies"), // //$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$ + + REPOSITORIES("repositories"), // //$NON-NLS-1$ + + CONFIGURATION("configuration") { //$NON-NLS-1$ + + @Override + protected void addTemplates(IProject project, Collection<Template> proposals, Node node, String prefix) throws CoreException { + if("execution".equals(node.getParentNode().getNodeName()) //$NON-NLS-1$ + || "reportSet".equals(node.getParentNode().getNodeName())) { //$NON-NLS-1$ + node = node.getParentNode().getParentNode(); + } + String groupId = getGroupId(node); + if(groupId==null) { + groupId = "org.apache.maven.plugins"; // TODO support other default groups //$NON-NLS-1$ + } + String artifactId = getArtifactId(node); + String version = extractVersion(project, 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(); + HashSet<String> params = new HashSet<String>(); + for(MojoDescriptor mojo : mojos) { + List<Parameter> parameters = (List<Parameter>) mojo.getParameters(); + for(Parameter parameter : parameters) { + boolean editable = parameter.isEditable(); + if(editable) { + String name = parameter.getName(); + if(!params.contains(name) && name.startsWith(prefix)) { + params.add(name); + + String text = NLS.bind(Messages.PomTemplateContext_param, parameter.isRequired(), parameter.getType()); + + String expression = parameter.getExpression(); + if(expression!=null) { + text += NLS.bind(Messages.PomTemplateContext_param_expr, expression); + } + + String defaultValue = parameter.getDefaultValue(); + if(defaultValue!=null) { + text += NLS.bind(Messages.PomTemplateContext_param_def, defaultValue); + } + + String desc = parameter.getDescription().trim(); + text += desc.startsWith("<p>") ? desc : "<br>" + desc; //$NON-NLS-1$ //$NON-NLS-2$ + + proposals.add(new Template(name, text, getContextTypeId(), // + "<" + name + ">${cursor}</" + name + ">", false)); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + } + } + } + } + } + } + }, + + GROUP_ID("groupId") { //$NON-NLS-1$ + @Override + public void addTemplates(IProject project, Collection<Template> proposals, Node node, String prefix) throws CoreException { + String contextTypeId = getContextTypeId(); + for(String groupId : getSearchEngine(project).findGroupIds(prefix, getPackaging(node), getContainingArtifact(node))) { + add(proposals, contextTypeId, groupId); + } + } + }, + + ARTIFACT_ID("artifactId") { //$NON-NLS-1$ + @Override + public void addTemplates(IProject project, 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) { + String contextTypeId = getContextTypeId(); + for(String artifactId : getSearchEngine(project).findArtifactIds(groupId, prefix, getPackaging(node), + getContainingArtifact(node))) { + add(proposals, contextTypeId, artifactId, groupId + ":" + artifactId); + } + } + } + }, + + VERSION("version") { //$NON-NLS-1$ + @Override + public void addTemplates(IProject project, 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) { + String contextTypeId = getContextTypeId(); + for(String version : getSearchEngine(project).findVersions(groupId, artifactId, prefix, getPackaging(node))) { + add(proposals, contextTypeId, version, groupId + ":" + artifactId + ":" + version); + } + } + //mkleint: this concept that all versions out there are equal is questionable.. + if (project != null && "dependency".equals(node.getParentNode().getNodeName())) { //$NON-NLS-1$ + //see if we can complete the properties ending with .version + + IMavenProjectFacade mvnproject = MavenPlugin.getDefault().getMavenProjectManager().getProject(project); + List<String> keys = new ArrayList<String>(); + String contextTypeId = getContextTypeId(); + if(mvnproject != null) { + MavenProject mvn = mvnproject.getMavenProject(); + if (mvn != null) { + //if groupid is the same, suggest ${project.version} + if (groupId.equals(mvn.getGroupId())) { + proposals.add(new Template("${project.version}", Messages.PomTemplateContext_project_version_hint, contextTypeId, "$${project.version}", false)); //$NON-NLS-1$ //$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 = MavenMarkerManager.getElementTextValue(MavenMarkerManager.findChildElement(root, "groupId"));//$NON-NLS-1$ + if (currentgroupid == null) { + Element parEl = MavenMarkerManager.findChildElement(root, "parent");//$NON-NLS-1$ + if (parEl != null) { + currentgroupid = MavenMarkerManager.getElementTextValue(MavenMarkerManager.findChildElement(parEl, "groupId"));//$NON-NLS-1$ + } + } + if (groupId.equals(currentgroupid)) { + proposals.add(new Template("${project.version}", Messages.PomTemplateContext_project_version_hint, contextTypeId, "$${project.version}", false)); //$NON-NLS-1$ //$NON-NLS-3$ + } + } + } + } + } + }, + + CLASSIFIER("classifier") { //$NON-NLS-1$ + @Override + public void addTemplates(IProject project, 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) { + String contextTypeId = getContextTypeId(); + for(String classifier : getSearchEngine(project).findClassifiers(groupId, artifactId, version, prefix, + getPackaging(node))) { + add(proposals, contextTypeId, classifier, groupId + ":" + artifactId + ":" + version + ":" + classifier); + } + } + } + }, + + TYPE("type") { //$NON-NLS-1$ + @Override + public void addTemplates(IProject project, Collection<Template> proposals, Node node, String prefix) throws CoreException { + String groupId = getGroupId(node); + String artifactId = getArtifactId(node); + String version = getVersion(node); + String contextTypeId = getContextTypeId(); + if(groupId != null && artifactId != null && version != null) { + for(String type : getSearchEngine(project).findTypes(groupId, artifactId, version, prefix, getPackaging(node))) { + add(proposals, contextTypeId, type, groupId + ":" + artifactId + ":" + version + ":" + type); + } + } + } + }, + + PACKAGING("packaging") { //$NON-NLS-1$ + @Override + public void addTemplates(IProject project, Collection<Template> proposals, Node node, String prefix) { + String contextTypeId = getContextTypeId(); + // TODO only show "pom" packaging in root section + add(proposals, contextTypeId, "pom"); //$NON-NLS-1$ + add(proposals, contextTypeId, "jar"); //$NON-NLS-1$ + add(proposals, contextTypeId, "war"); //$NON-NLS-1$ + add(proposals, contextTypeId, "ear"); //$NON-NLS-1$ + add(proposals, contextTypeId, "ejb"); //$NON-NLS-1$ + add(proposals, contextTypeId, "eclipse-plugin"); //$NON-NLS-1$ + add(proposals, contextTypeId, "eclipse-feature"); //$NON-NLS-1$ + add(proposals, contextTypeId, "eclipse-update-site"); //$NON-NLS-1$ + add(proposals, contextTypeId, "maven-plugin"); //$NON-NLS-1$ + add(proposals, contextTypeId, "maven-archetype"); //$NON-NLS-1$ + } + }, + + SCOPE("scope") { //$NON-NLS-1$ + @Override + public void addTemplates(IProject project, Collection<Template> proposals, Node node, String prefix) { + String contextTypeId = getContextTypeId(); + add(proposals, contextTypeId, "compile"); //$NON-NLS-1$ + add(proposals, contextTypeId, "test"); //$NON-NLS-1$ + add(proposals, contextTypeId, "provided"); //$NON-NLS-1$ + add(proposals, contextTypeId, "runtime"); //$NON-NLS-1$ + add(proposals, contextTypeId, "system"); //$NON-NLS-1$ + // TODO only show "import" scope in <dependencyManagement> + add(proposals, contextTypeId, "import"); //$NON-NLS-1$ + } + }, + + SYSTEM_PATH("systemPath"), //$NON-NLS-1$ + + PHASE("phase") { //$NON-NLS-1$ + @Override + public void addTemplates(IProject project, Collection<Template> proposals, Node node, String prefix) { + String contextTypeId = getContextTypeId(); + // TODO the following list should be derived from the packaging handler (the actual lifecycle) + + // Clean Lifecycle + add(proposals, contextTypeId, "pre-clean", Messages.PomTemplateContext_preclean); //$NON-NLS-1$ + add(proposals, contextTypeId, "clean", Messages.PomTemplateContext_clean); //$NON-NLS-1$ + add(proposals, contextTypeId, "post-clean", Messages.PomTemplateContext_postclean); //$NON-NLS-1$ + + // Default Lifecycle + add(proposals, contextTypeId, "validate", Messages.PomTemplateContext_validate); //$NON-NLS-1$ + add(proposals, contextTypeId, "generate-sources", Messages.PomTemplateContext_generatesources); //$NON-NLS-1$ + add(proposals, contextTypeId, "process-sources", Messages.PomTemplateContext_processsources); //$NON-NLS-1$ + add(proposals, contextTypeId, "generate-resources", Messages.PomTemplateContext_generateresources); //$NON-NLS-1$ + add(proposals, contextTypeId, "process-resources", Messages.PomTemplateContext_processresources); //$NON-NLS-1$ + add(proposals, contextTypeId, "compile", Messages.PomTemplateContext_compile); //$NON-NLS-1$ + add(proposals, contextTypeId, "process-classes", Messages.PomTemplateContext_processclasses); //$NON-NLS-1$ + add(proposals, contextTypeId, "generate-test-sources", Messages.PomTemplateContext_generatetestsources); //$NON-NLS-1$ + add(proposals, contextTypeId, "process-test-sources", Messages.PomTemplateContext_processtestsources); //$NON-NLS-1$ + add(proposals, contextTypeId, "generate-test-resources", Messages.PomTemplateContext_generatetestresources); //$NON-NLS-1$ + add(proposals, contextTypeId, "process-test-resources", Messages.PomTemplateContext_processtestresources); //$NON-NLS-1$ + add(proposals, contextTypeId, "test-compile", Messages.PomTemplateContext_testcompile); //$NON-NLS-1$ + add(proposals, contextTypeId, "process-test-classes", Messages.PomTemplateContext_processtestclasses); //$NON-NLS-1$ + add(proposals, contextTypeId, "test", Messages.PomTemplateContext_test); //$NON-NLS-1$ + add(proposals, contextTypeId, "prepare-package", Messages.PomTemplateContext_preparepackage); //$NON-NLS-1$ + add(proposals, contextTypeId, "package", Messages.PomTemplateContext_package); //$NON-NLS-1$ + add(proposals, contextTypeId, "pre-integration-test", Messages.PomTemplateContext_preintegrationtest); //$NON-NLS-1$ + add(proposals, contextTypeId, "integration-test", Messages.PomTemplateContext_integrationtest); //$NON-NLS-1$ + add(proposals, contextTypeId, "post-integration-test", Messages.PomTemplateContext_postintegrationtest); //$NON-NLS-1$ + add(proposals, contextTypeId, "verify", Messages.PomTemplateContext_verify); //$NON-NLS-1$ + add(proposals, contextTypeId, "install", Messages.PomTemplateContext_install); //$NON-NLS-1$ + add(proposals, contextTypeId, "deploy", Messages.PomTemplateContext_deploy); //$NON-NLS-1$ + + // Site Lifecycle + add(proposals, contextTypeId, "pre-site", Messages.PomTemplateContext_presite); //$NON-NLS-1$ + add(proposals, contextTypeId, "site", Messages.PomTemplateContext_site); //$NON-NLS-1$ + add(proposals, contextTypeId, "post-site", Messages.PomTemplateContext_postsite); //$NON-NLS-1$ + add(proposals, contextTypeId, "site-deploy", Messages.PomTemplateContext_sitedeploy); //$NON-NLS-1$ + } + }, + + GOAL("goal") { //$NON-NLS-1$ + @Override + public void addTemplates(IProject project, 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, 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) { + String contextTypeId = getContextTypeId(); + for (MojoDescriptor mojo : mojos) { + add(proposals, contextTypeId, mojo.getGoal(), mojo.getDescription()); + } + } + } + } + }, + + MODULE("module") { //$NON-NLS-1$ + @Override + public void addTemplates(IProject project, Collection<Template> proposals, Node node, String prefix) + throws CoreException { + if(project == null) { + //shall not happen just double check. + return; + } + //MNGECLIPSE-2204 collect the existing values from the surrounding xml content only.. + List<String> existings = new ArrayList<String>(); + Node moduleNode = node; + if (moduleNode != null) { + Node modulesNode = moduleNode.getParentNode(); + if (modulesNode != null) { + for (Element el : MavenMarkerManager.findChildElements((Element)modulesNode, "module")) { + if (el != moduleNode) { + String val = MavenMarkerManager.getElementTextValue(el); + if (val != null) { + existings.add(val); + } + } + } + } + } + + File directory = new File(project.getLocationURI()); + final File currentPom = new File(directory, "pom.xml"); + String path = prefix; + boolean endingSlash = path.endsWith("/"); //$NON-NLS-1$ + String[] elems = StringUtils.split(path, "/"); //$NON-NLS-1$ + String lastElement = null; + for(int i = 0; i < elems.length; i++ ) { + if("..".equals(elems[i])) { //$NON-NLS-1$ + directory = directory != null ? directory.getParentFile() : null; + } else if(i < elems.length - (endingSlash ? 0 : 1)) { + directory = directory != null ? new File(directory, elems[i]) : null; + } else { + lastElement = elems[i]; + } + } + path = lastElement != null ? path.substring(0, path.length() - lastElement.length()) : path; + FileFilter filter = new FileFilter() { + public boolean accept(File pathname) { + if (pathname.isDirectory()) { + File pom = new File(pathname, "pom.xml"); //$NON-NLS-1$ + //TODO shall also handle polyglot maven :) + return pom.exists() && pom.isFile() && !pom.equals(currentPom); + } + return false; + } + }; + if (directory != null && directory.exists() && directory.isDirectory()) { + File[] offerings = directory.listFiles(filter); + for (File candidate : offerings) { + if(lastElement == null || candidate.getName().startsWith(lastElement) ) { + String val = path + candidate.getName(); + if (!existings.contains(val)) { //only those not already being added in the surrounding area + add(proposals, getContextTypeId(), val, NLS.bind(Messages.PomTemplateContext_candidate, candidate)); + } + } + } + if (path.length() == 0 && directory.equals(currentPom.getParentFile())) { + //for the empty value, when searching in current directory, propose also stuff one level up. + File currentParent = directory.getParentFile(); + if (currentParent != null && currentParent.exists()) { + offerings = currentParent.listFiles(filter); + for (File candidate : offerings) { + String val = "../" + candidate.getName(); + if (!existings.contains(val)) { //only those not already being added in the surrounding area + add(proposals, getContextTypeId(), val, NLS.bind(Messages.PomTemplateContext_candidate, candidate)); + } + } + } + } + } + } + }; + + private static final String PREFIX = MvnIndexPlugin.PLUGIN_ID + ".templates.contextType."; //$NON-NLS-1$ + + private final String nodeName; + + private PomTemplateContext(String nodeName) { + this.nodeName = nodeName; + } + + /** + * Return templates depending on the context type. + */ + public Template[] getTemplates(IProject project, Node node, String prefix) { + Collection<Template> templates = new ArrayList<Template>(); + try { + addTemplates(project, templates, node, prefix); + } catch (CoreException e) { + MavenLogger.log(e); + } + return templates.toArray(new Template[templates.size()]); + } + + protected void addTemplates(IProject project, Collection<Template> templates, Node currentNode, String prefix) throws CoreException { + } + + protected String getNodeName() { + return nodeName; + } + + public String getContextTypeId() { + return PREFIX + nodeName; + } + + 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; + } + + private static SearchEngine getSearchEngine(IProject project) throws CoreException { + if(searchEngineForTests != null) { + return searchEngineForTests; + } + return MavenPlugin.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$ + } + + /** + * + * @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(IProject project, String version, String groupId, String artifactId, int strategy) + throws CoreException { + //interpolate the version found to get rid of expressions + version = simpleInterpolate(project, version); + + if (version==null) { + Packaging pack = Packaging.ALL; + if ( (strategy & EXTRACT_STRATEGY_PLUGIN) != 0) { + version = searchPM(project, groupId, artifactId); + pack = Packaging.PLUGIN; + } + if ( (strategy & EXTRACT_STRATEGY_DEPENDENCY) != 0) { + version = searchDM(project, 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 MNGECLIPSE-2540 change project parameter to MavenProject I guess.. + static String simpleInterpolate(IProject 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; + } + IMavenProjectFacade mvnproject = MavenPlugin.getDefault().getMavenProjectManager().getProject(project); + if (mvnproject != null) { + MavenProject prj = mvnproject.getMavenProject(); + if (prj != null) { + Properties props = prj.getProperties(); + RegexBasedInterpolator inter = new RegexBasedInterpolator(); + if (props != null) { + inter.addValueSource(new PropertiesBasedValueSource(props)); + } + inter.addValueSource(new PrefixedObjectValueSource(Arrays.asList( new String[]{ "pom.", "project." } ), prj.getModel(), false)); //$NON-NLS-1$ //$NON-NLS-2$ + try { + text = inter.interpolate(text); + } catch(InterpolationException e) { + text = null; + } + + } + } + } + return text; + } + + private static String searchPM(IProject project, String groupId, String artifactId) { + if (project == null) { + return null; + } + String version = null; + //see if we can find the plugin in plugin management of resolved project. + IMavenProjectFacade mvnproject = MavenPlugin.getDefault().getMavenProjectManager().getProject(project); + if(mvnproject != null) { + String id = Plugin.constructKey(groupId, artifactId); + MavenProject prj = mvnproject.getMavenProject(); + if(prj != null) { + PluginManagement pm = prj.getPluginManagement(); + if(pm != null) { + for(Plugin pl : pm.getPlugins()) { + if(id.equals(pl.getKey())) { + version = pl.getVersion(); + break; + } + } + } + } + } + return version; + } + + private static String searchDM(IProject 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. + IMavenProjectFacade mvnproject = MavenPlugin.getDefault().getMavenProjectManager().getProject(project); + if(mvnproject != null) { + String id = groupId + ":" + artifactId + ":"; + MavenProject prj = mvnproject.getMavenProject(); + if(prj != null) { + DependencyManagement dm = prj.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 getNodeTextValue(node); + } + + /** + * Returns sibling with given name. + */ + private static Node getSiblingWithName(Node node, String name) { + NodeList nodeList = node.getParentNode().getChildNodes(); + for(int i = 0; i < nodeList.getLength(); i++ ) { + if(name.equals(nodeList.item(i).getNodeName())) { + return nodeList.item(i); + } + } + return null; + } + + /** + * Returns text value of the node. + */ + private static String getNodeTextValue(Node node) { + if(node != null && hasOneNode(node.getChildNodes())) { + return ((Text) node.getChildNodes().item(0)).getData().trim(); + } + return null; + } + + /** + * Returns true if there is only one node in the nodeList. + */ + private static boolean hasOneNode(NodeList nodeList) { + return nodeList != null && nodeList.getLength() == 1; + } + + private static void add(Collection<Template> proposals, String contextTypeId, String name) { + add(proposals, contextTypeId, name, name); + } + + private static void add(Collection<Template> proposals, String contextTypeId, String name, String description) { + proposals.add(new Template(name, description, contextTypeId, name, false)); + } + +} diff --git a/org.eclipse.m2e.editor.xml/src/main/java/org/eclipse/m2e/editor/xml/PomTemplateContextType.java b/org.eclipse.m2e.editor.xml/src/main/java/org/eclipse/m2e/editor/xml/PomTemplateContextType.java new file mode 100644 index 00000000..597219e4 --- /dev/null +++ b/org.eclipse.m2e.editor.xml/src/main/java/org/eclipse/m2e/editor/xml/PomTemplateContextType.java @@ -0,0 +1,32 @@ +/******************************************************************************* + * 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 org.eclipse.jface.text.templates.GlobalTemplateVariables; +import org.eclipse.jface.text.templates.TemplateContextType; + + +/** + * @author Lukas Krecan + */ +public class PomTemplateContextType extends TemplateContextType { + public PomTemplateContextType() { + addResolver(new GlobalTemplateVariables.Cursor()); + addResolver(new GlobalTemplateVariables.Date()); + addResolver(new GlobalTemplateVariables.Dollar()); + addResolver(new GlobalTemplateVariables.LineSelection()); + addResolver(new GlobalTemplateVariables.Time()); + addResolver(new GlobalTemplateVariables.User()); + addResolver(new GlobalTemplateVariables.WordSelection()); + addResolver(new GlobalTemplateVariables.Year()); + } +} diff --git a/org.eclipse.m2e.editor.xml/src/main/java/org/eclipse/m2e/editor/xml/PomTemplateContextUtil.java b/org.eclipse.m2e.editor.xml/src/main/java/org/eclipse/m2e/editor/xml/PomTemplateContextUtil.java new file mode 100644 index 00000000..c140d2ba --- /dev/null +++ b/org.eclipse.m2e.editor.xml/src/main/java/org/eclipse/m2e/editor/xml/PomTemplateContextUtil.java @@ -0,0 +1,105 @@ +/******************************************************************************* + * 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.io.InputStream; +import java.io.InputStreamReader; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.zip.ZipEntry; +import java.util.zip.ZipFile; + +import org.apache.maven.artifact.Artifact; +import org.apache.maven.artifact.repository.ArtifactRepository; +import org.apache.maven.plugin.descriptor.PluginDescriptor; +import org.apache.maven.plugin.descriptor.PluginDescriptorBuilder; +import org.codehaus.plexus.util.IOUtil; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IStatus; + +import org.eclipse.m2e.core.MavenPlugin; +import org.eclipse.m2e.core.core.MavenConsole; +import org.eclipse.m2e.core.core.MavenLogger; +import org.eclipse.m2e.core.embedder.IMaven; + +class PomTemplateContextUtil { + + public static final PomTemplateContextUtil INSTANCE = new PomTemplateContextUtil(); + + private final Map<String, PluginDescriptor> descriptors = new HashMap<String, PluginDescriptor>(); + + public PluginDescriptor getPluginDescriptor(String groupId, String artifactId, String version) { + String name = groupId + ":" + artifactId + ":" + version; + PluginDescriptor descriptor = descriptors.get(name); + if(descriptor!=null) { + return descriptor; + } + + MavenPlugin plugin = MavenPlugin.getDefault(); + MavenConsole console = plugin.getConsole(); + try { + IMaven embedder = MavenPlugin.getDefault().getMaven(); + + List<ArtifactRepository> repositories = embedder.getArtifactRepositories(); + + Artifact artifact = embedder.resolve(groupId, artifactId, version, "maven-plugin", null, repositories, null); //$NON-NLS-1$ + + File file = artifact.getFile(); + if(file == null) { + String msg = "Can't resolve plugin " + name; //$NON-NLS-1$ + console.logError(msg); + } else { + InputStream is = null; + ZipFile zf = null; + try { + zf = new ZipFile(file); + ZipEntry entry = zf.getEntry("META-INF/maven/plugin.xml"); //$NON-NLS-1$ + if(entry != null) { + is = zf.getInputStream(entry); + PluginDescriptorBuilder builder = new PluginDescriptorBuilder(); + descriptor = builder.build(new InputStreamReader(is)); + descriptors.put(name, descriptor); + return descriptor; + } + + } catch(Exception ex) { + String msg = "Can't read configuration for " + name; //$NON-NLS-1$ + console.logError(msg); + MavenLogger.log(msg, ex); + + } finally { + IOUtil.close(is); + try { + zf.close(); + } catch(IOException ex) { + // ignore + } + } + } + + } catch(CoreException ex) { + IStatus status = ex.getStatus(); + if(status.getException() != null) { + console.logError(status.getMessage() + "; " + status.getException().getMessage()); + } else { + console.logError(status.getMessage()); + } + MavenLogger.log(ex); + } + return null; + } + +} diff --git a/org.eclipse.m2e.editor.xml/src/main/java/org/eclipse/m2e/editor/xml/PomTextHover.java b/org.eclipse.m2e.editor.xml/src/main/java/org/eclipse/m2e/editor/xml/PomTextHover.java new file mode 100644 index 00000000..c0322877 --- /dev/null +++ b/org.eclipse.m2e.editor.xml/src/main/java/org/eclipse/m2e/editor/xml/PomTextHover.java @@ -0,0 +1,148 @@ +package org.eclipse.m2e.editor.xml; + +import java.util.List; + +import org.apache.maven.model.Dependency; +import org.apache.maven.model.DependencyManagement; +import org.apache.maven.model.InputLocation; +import org.apache.maven.model.InputSource; +import org.apache.maven.model.Model; +import org.apache.maven.model.Plugin; +import org.apache.maven.model.PluginManagement; +import org.apache.maven.project.MavenProject; +import org.w3c.dom.Node; + +import org.eclipse.jface.text.IDocument; +import org.eclipse.jface.text.IRegion; +import org.eclipse.jface.text.ITextHover; +import org.eclipse.jface.text.ITextViewer; +import org.eclipse.jface.text.source.ISourceViewer; +import org.eclipse.osgi.util.NLS; + +import org.eclipse.m2e.core.MavenPlugin; +import org.eclipse.m2e.core.project.IMavenProjectFacade; +import org.eclipse.m2e.editor.xml.PomHyperlinkDetector.ExpressionRegion; +import org.eclipse.m2e.editor.xml.PomHyperlinkDetector.ManagedArtifactRegion; +import org.eclipse.m2e.editor.xml.internal.Messages; + +public class PomTextHover implements ITextHover { + + public PomTextHover(ISourceViewer sourceViewer, String contentType, int stateMask) { + } + + public String getHoverInfo(ITextViewer textViewer, IRegion hoverRegion) { + if (hoverRegion instanceof ExpressionRegion) { + ExpressionRegion region = (ExpressionRegion) hoverRegion; + IMavenProjectFacade mvnproject = MavenPlugin.getDefault().getMavenProjectManager().getProject(region.project); + if (mvnproject != null) { + MavenProject mavprj = mvnproject.getMavenProject(); + if (mavprj != null) { + String value = PomTemplateContext.simpleInterpolate(region.project, "${" + region.property + "}"); //$NON-NLS-1$ //$NON-NLS-2$ + String loc = null; + Model mdl = mavprj.getModel(); + if (mdl.getProperties() != null && mdl.getProperties().containsKey(region.property)) { + if (mdl.getLocation("properties") != null) { + InputLocation location = mdl.getLocation("properties").getLocation(region.property); //$NON-NLS-1$ + if (location != null) { + //MNGECLIPSE-2539 apparently you can have an InputLocation with null input source. + // check! + InputSource source = location.getSource(); + if (source != null) { + loc = source.getModelId(); + } + } + } + } + String ret = NLS.bind(Messages.PomTextHover_eval1, + value, loc != null ? NLS.bind(Messages.PomTextHover_eval2, loc) : ""); //$NON-NLS-2$ //$NON-NLS-1$ + return ret; + } + } + } else if (hoverRegion instanceof ManagedArtifactRegion) { + ManagedArtifactRegion region = (ManagedArtifactRegion) hoverRegion; + IMavenProjectFacade mvnproject = MavenPlugin.getDefault().getMavenProjectManager().getProject(region.project); + if (mvnproject != null) { + MavenProject mavprj = mvnproject.getMavenProject(); + if (mavprj != null) { + String version = null; + if (region.isDependency) { + DependencyManagement dm = mavprj.getDependencyManagement(); + if (dm != null) { + List<Dependency> list = dm.getDependencies(); + String id = region.groupId + ":" + region.artifactId + ":"; //$NON-NLS-1$ //$NON-NLS-2$ + if (list != null) { + for (Dependency dep : list) { + if (dep.getManagementKey().startsWith(id)) { + version = dep.getVersion(); + } + } + } + } + } + + if (region.isPlugin) { + PluginManagement pm = mavprj.getPluginManagement(); + if (pm != null) { + List<Plugin> list = pm.getPlugins(); + String id = Plugin.constructKey(region.groupId, region.artifactId); + if (list != null) { + for (Plugin plg : list) { + if (id.equals(plg.getKey())) { + version = plg.getVersion(); + } + } + } + + } + } + StringBuffer ret = new StringBuffer(); + ret.append("<html>"); //$NON-NLS-1$ + if (version != null) { + ret.append(NLS.bind(Messages.PomTextHover_managed_version, version)); + } else { + ret.append(Messages.PomTextHover_managed_version_missing); + } + ret.append("<br>"); //$NON-NLS-1$ + InputLocation openLocation = PomHyperlinkDetector.findLocationForManagedArtifact(region, mavprj); + if (openLocation != null) { + //MNGECLIPSE-2539 apparently you can have an InputLocation with null input source. + // check! + InputSource source = openLocation.getSource(); + if (source != null) { + ret.append(NLS.bind(Messages.PomTextHover_managed_location, source.getModelId())); + } + } else { + ret.append(Messages.PomTextHover_managed_location_missing); + } + ret.append("</html>"); //$NON-NLS-1$ + return ret.toString(); + } + } + } + + return null; + } + + public IRegion getHoverRegion(ITextViewer textViewer, int offset) { + IDocument document = textViewer.getDocument(); + if(document == null) { + return null; + } + + Node current = PomHyperlinkDetector.getCurrentNode(document, offset); + if (current != null) { + ExpressionRegion region = PomHyperlinkDetector.findExpressionRegion(current, textViewer, offset); + if (region != null) { + return region; + } + ManagedArtifactRegion manReg = PomHyperlinkDetector.findManagedArtifactRegion(current, textViewer, offset); + if (manReg != null) { + return manReg; + } + } + return null; + } + + + +} diff --git a/org.eclipse.m2e.editor.xml/src/main/java/org/eclipse/m2e/editor/xml/XMLSchemaMarkerResolution.java b/org.eclipse.m2e.editor.xml/src/main/java/org/eclipse/m2e/editor/xml/XMLSchemaMarkerResolution.java new file mode 100644 index 00000000..f35edc3d --- /dev/null +++ b/org.eclipse.m2e.editor.xml/src/main/java/org/eclipse/m2e/editor/xml/XMLSchemaMarkerResolution.java @@ -0,0 +1,75 @@ +/******************************************************************************* + * 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 org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IMarker; +import org.eclipse.core.resources.IResource; +import org.eclipse.jface.dialogs.MessageDialog; +import org.eclipse.text.edits.InsertEdit; +import org.eclipse.ui.IEditorPart; +import org.eclipse.ui.IMarkerResolution; +import org.eclipse.ui.ide.IDE; +import org.eclipse.wst.sse.core.StructuredModelManager; +import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocumentRegion; +import org.eclipse.wst.xml.core.internal.provisional.document.IDOMModel; + +import org.eclipse.m2e.core.core.MavenLogger; +import org.eclipse.m2e.editor.xml.internal.Messages; + +/** + * MavenMarkerResolution + * TODO mkleint: this class shall be eventually merged with the class doing the same in POMQuickAssistProcessor + * @author dyocum + */ +public class XMLSchemaMarkerResolution implements IMarkerResolution { + + /* (non-Javadoc) + * @see org.eclipse.ui.IMarkerResolution#getLabel() + */ + public String getLabel() { + return Messages.MavenMarkerResolution_schema_label; + } + + /* (non-Javadoc) + * @see org.eclipse.ui.IMarkerResolution#run(org.eclipse.core.resources.IMarker) + */ + public void run(final IMarker marker) { + if(marker.getResource().getType() == IResource.FILE){ + try { + IDOMModel domModel = (IDOMModel)StructuredModelManager.getModelManager().getModelForEdit((IFile)marker.getResource()); + int offset = ((Integer)marker.getAttribute("offset")); //$NON-NLS-1$ + IStructuredDocumentRegion regionAtCharacterOffset = domModel.getStructuredDocument().getRegionAtCharacterOffset(offset); + if(regionAtCharacterOffset != null && regionAtCharacterOffset.getText() != null && + regionAtCharacterOffset.getText().lastIndexOf("<project") >=0){ //$NON-NLS-1$ + //in case there are unsaved changes, find the current offset of the <project> node before inserting + offset = regionAtCharacterOffset.getStartOffset(); + IDE.openEditor(MvnIndexPlugin.getDefault().getWorkbench().getActiveWorkbenchWindow().getActivePage(), (IFile)marker.getResource()); + InsertEdit edit = new InsertEdit(offset+8, PomQuickAssistProcessor.XSI_VALUE); + try { + edit.apply(domModel.getStructuredDocument()); + IEditorPart activeEditor = MvnIndexPlugin.getDefault().getWorkbench().getActiveWorkbenchWindow().getActivePage().getActiveEditor(); + MvnIndexPlugin.getDefault().getWorkbench().getActiveWorkbenchWindow().getActivePage().saveEditor(activeEditor, false); + } catch(Exception e){ + MavenLogger.log("Unable to insert schema info", e); //$NON-NLS-1$ + } + } else { + String msg = Messages.MavenMarkerResolution_error; + MessageDialog.openError(MvnIndexPlugin.getDefault().getWorkbench().getActiveWorkbenchWindow().getShell(), Messages.MavenMarkerResolution_error_title, msg); + } + } catch(Exception e) { + MavenLogger.log("Unable to run quick fix for maven marker", e); //$NON-NLS-1$ + } + } + } + +} diff --git a/org.eclipse.m2e.editor.xml/src/main/java/org/eclipse/m2e/editor/xml/internal/Messages.java b/org.eclipse.m2e.editor.xml/src/main/java/org/eclipse/m2e/editor/xml/internal/Messages.java new file mode 100644 index 00000000..aa74c0cb --- /dev/null +++ b/org.eclipse.m2e.editor.xml/src/main/java/org/eclipse/m2e/editor/xml/internal/Messages.java @@ -0,0 +1,156 @@ +/******************************************************************************* + * 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.internal; + +import org.eclipse.osgi.util.NLS; + + +public class Messages extends NLS { + private static final String BUNDLE_NAME = "org.eclipse.m2e.editor.xml.internal.messages"; //$NON-NLS-1$ + + public static String InsertArtifactProposal_additionals; + + public static String InsertArtifactProposal_display_name; + + public static String InsertArtifactProposal_insert_plugin_description; + + public static String InsertArtifactProposal_insert_plugin_display_name; + + public static String InsertArtifactProposal_insert_plugin_title; + + public static String InsertArtifactProposal_searchDialog_title; + + public static String InsertExpressionProposal_hint1; + + public static String InsertExpressionProposal_hint2; + + public static String MavenMarkerResolution_error; + + public static String MavenMarkerResolution_error_title; + + public static String MavenMarkerResolution_schema_label; + public static String PomContentAssistProcessor_insert_relPath_title; + + public static String PomContentAssistProcessor_set_relPath_title; + + public static String PomHyperlinkDetector_23; + + public static String PomHyperlinkDetector_error_message; + + public static String PomHyperlinkDetector_error_title; + + public static String PomHyperlinkDetector_hyperlink_pattern; + + public static String PomHyperlinkDetector_job_name; + + public static String PomHyperlinkDetector_link_managed; + + public static String PomHyperlinkDetector_open_module; + + public static String PomHyperlinkDetector_open_property; + + public static String PomQuickAssistProcessor_name; + + public static String PomQuickAssistProcessor_remove_hint; + public static String PomQuickAssistProcessor_title_groupId; + + public static String PomQuickAssistProcessor_title_version; + + public static String PomTemplateContext_candidate; + + public static String PomTemplateContext_clean; + + public static String PomTemplateContext_compile; + + public static String PomTemplateContext_deploy; + + public static String PomTemplateContext_expression_description; + + public static String PomTemplateContext_generateresources; + + public static String PomTemplateContext_generatesources; + + public static String PomTemplateContext_generatetestresources; + + public static String PomTemplateContext_generatetestsources; + + public static String PomTemplateContext_install; + + public static String PomTemplateContext_integrationtest; + + public static String PomTemplateContext_package; + + public static String PomTemplateContext_param; + + public static String PomTemplateContext_param_def; + + public static String PomTemplateContext_param_expr; + + public static String PomTemplateContext_postclean; + + public static String PomTemplateContext_postintegrationtest; + + public static String PomTemplateContext_postsite; + + public static String PomTemplateContext_preclean; + + public static String PomTemplateContext_preintegrationtest; + + public static String PomTemplateContext_preparepackage; + + public static String PomTemplateContext_presite; + + public static String PomTemplateContext_processclasses; + + public static String PomTemplateContext_processresources; + + public static String PomTemplateContext_processsources; + + public static String PomTemplateContext_processtestclasses; + + public static String PomTemplateContext_processtestresources; + + public static String PomTemplateContext_processtestsources; + + public static String PomTemplateContext_project_version_hint; + + public static String PomTemplateContext_site; + + public static String PomTemplateContext_sitedeploy; + + public static String PomTemplateContext_test; + + public static String PomTemplateContext_testcompile; + + public static String PomTemplateContext_validate; + + public static String PomTemplateContext_verify; + + public static String PomTextHover_eval1; + + public static String PomTextHover_eval2; + public static String PomTextHover_managed_location; + + public static String PomTextHover_managed_location_missing; + + public static String PomTextHover_managed_version; + + public static String PomTextHover_managed_version_missing; + static { + // initialize resource bundle + NLS.initializeMessages(BUNDLE_NAME, Messages.class); + } + + private Messages() { + } +} diff --git a/org.eclipse.m2e.editor.xml/src/main/java/org/eclipse/m2e/editor/xml/internal/POMMarkerAnnotationModel.java b/org.eclipse.m2e.editor.xml/src/main/java/org/eclipse/m2e/editor/xml/internal/POMMarkerAnnotationModel.java new file mode 100644 index 00000000..5a7bc121 --- /dev/null +++ b/org.eclipse.m2e.editor.xml/src/main/java/org/eclipse/m2e/editor/xml/internal/POMMarkerAnnotationModel.java @@ -0,0 +1,34 @@ +package org.eclipse.m2e.editor.xml.internal; + +import org.eclipse.core.resources.IMarker; +import org.eclipse.core.resources.IResource; +import org.eclipse.ui.texteditor.MarkerAnnotation; +import org.eclipse.wst.sse.ui.internal.StructuredResourceMarkerAnnotationModel; + +import org.eclipse.m2e.core.core.IMavenConstants; + +/** + * created this file to get the proper lightbulb icon for the warnings with hint + * @author mkleint + */ +public class POMMarkerAnnotationModel extends StructuredResourceMarkerAnnotationModel { + + public POMMarkerAnnotationModel(IResource resource) { + super(resource); + } + + public POMMarkerAnnotationModel(IResource resource, String secondaryID) { + super(resource, secondaryID); + } + + @Override + protected MarkerAnnotation createMarkerAnnotation(IMarker marker) { + String hint = marker.getAttribute(IMavenConstants.MARKER_ATTR_EDITOR_HINT, null); + if(hint != null) { + MarkerAnnotation ann = new MarkerAnnotation(marker); + ann.setQuickFixable(true); + return ann; + } + return super.createMarkerAnnotation(marker); + } +} diff --git a/org.eclipse.m2e.editor.xml/src/main/java/org/eclipse/m2e/editor/xml/internal/POMMarkerAnnotationModelFactory.java b/org.eclipse.m2e.editor.xml/src/main/java/org/eclipse/m2e/editor/xml/internal/POMMarkerAnnotationModelFactory.java new file mode 100644 index 00000000..026f359a --- /dev/null +++ b/org.eclipse.m2e.editor.xml/src/main/java/org/eclipse/m2e/editor/xml/internal/POMMarkerAnnotationModelFactory.java @@ -0,0 +1,34 @@ +package org.eclipse.m2e.editor.xml.internal; + +import org.eclipse.core.filebuffers.FileBuffers; +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.ResourcesPlugin; +import org.eclipse.core.runtime.IPath; +import org.eclipse.jface.text.source.IAnnotationModel; +import org.eclipse.ui.texteditor.ResourceMarkerAnnotationModelFactory; + +/** + * created this file to get the proper lightbulb icon for the warnings with hint + * is almost exact copy of the wst one.. + * @author mkleint + */ +public class POMMarkerAnnotationModelFactory extends ResourceMarkerAnnotationModelFactory { + public POMMarkerAnnotationModelFactory() { + super(); + } + + /* + * @see org.eclipse.core.filebuffers.IAnnotationModelFactory#createAnnotationModel(org.eclipse.core.runtime.IPath) + */ + public IAnnotationModel createAnnotationModel(IPath location) { + IAnnotationModel model = null; + IFile file = FileBuffers.getWorkspaceFileAtLocation(location); + if (file != null) { + model = new POMMarkerAnnotationModel(file); + } + else { + model = new POMMarkerAnnotationModel(ResourcesPlugin.getWorkspace().getRoot(), location.toString()); + } + return model; + } +} diff --git a/org.eclipse.m2e.editor.xml/src/main/java/org/eclipse/m2e/editor/xml/internal/messages.properties b/org.eclipse.m2e.editor.xml/src/main/java/org/eclipse/m2e/editor/xml/internal/messages.properties new file mode 100644 index 00000000..169707c9 --- /dev/null +++ b/org.eclipse.m2e.editor.xml/src/main/java/org/eclipse/m2e/editor/xml/internal/messages.properties @@ -0,0 +1,66 @@ +InsertArtifactProposal_additionals=Opens a search dialog where you can select the parent pom for this project. +InsertArtifactProposal_display_name=Insert reference to parent POM +InsertArtifactProposal_insert_plugin_description=Opens a search dialog where you can select a Maven plugin to add to this project +InsertArtifactProposal_insert_plugin_display_name=Insert plugin +InsertArtifactProposal_insert_plugin_title=Select Plugin +InsertArtifactProposal_searchDialog_title=Select Parent +InsertExpressionProposal_hint1=The expression evaluates to <b>{0}</b> in the current effective pom. +InsertExpressionProposal_hint2=<br>It is based on property defined in <b>{0}</b> +MavenMarkerResolution_error=Unable to apply the quick fix. The file may have unsaved changes that invalidate the current quick fix. +MavenMarkerResolution_error_title=Error +MavenMarkerResolution_schema_label=Add Schema information to the specified pom.xml +PomContentAssistProcessor_insert_relPath_title=Insert relativePath pointing to {0} +PomContentAssistProcessor_set_relPath_title=Set relativePath to {0} +PomHyperlinkDetector_23=</artifactId> +PomHyperlinkDetector_error_message=Can't open editor for {0}\n{1} +PomHyperlinkDetector_error_title=Open Maven POM +PomHyperlinkDetector_hyperlink_pattern=Open pom.xml for {0}:{1} +PomHyperlinkDetector_job_name=Opening POM +PomHyperlinkDetector_link_managed=Open managed location for {0} +PomHyperlinkDetector_open_module=Open module project pom.xml at {0} +PomHyperlinkDetector_open_property=Open definition of property {0} +PomQuickAssistProcessor_name=Add Maven XML Schema declaration +PomQuickAssistProcessor_remove_hint=It removes the current definition to rely on value inherited from parent +PomQuickAssistProcessor_title_groupId=Remove groupId declaration +PomQuickAssistProcessor_title_version=Remove version declaration +PomTemplateContext_candidate=Maven project at {0} +PomTemplateContext_clean=Removes all files generated by the previous build +PomTemplateContext_compile=Compile the source code of the project +PomTemplateContext_deploy=Done in an integration or release environment, copies the final package to the remote repository for sharing with other developers and projects +PomTemplateContext_expression_description=Property defined in effective pom. +PomTemplateContext_generateresources=Generate resources for inclusion in the package +PomTemplateContext_generatesources=Generate any source code for inclusion in compilation +PomTemplateContext_generatetestresources=Create resources for testing +PomTemplateContext_generatetestsources=Generate any test source code for inclusion in compilation +PomTemplateContext_install=Install the package into the local repository, for use as a dependency in other projects locally +PomTemplateContext_integrationtest=Process and deploy the package if necessary into an environment where integration tests can be run +PomTemplateContext_package=Take the compiled code and package it in its distributable format, such as a JAR +PomTemplateContext_param=<b>required:</b> {0}<br><b>type:</b> {1}<br> +PomTemplateContext_param_def=default: {0}<br> +PomTemplateContext_param_expr=expression: {0}<br> +PomTemplateContext_postclean=Executes processes needed to finalize the project cleaning +PomTemplateContext_postintegrationtest=Perform actions required after integration tests have been executed. This may including cleaning up the environment +PomTemplateContext_postsite=Executes processes needed to finalize the site generation, and to prepare for site deployment +PomTemplateContext_preclean=Executes processes needed prior to the actual project cleaning +PomTemplateContext_preintegrationtest=Perform actions required before integration tests are executed. This may involve things such as setting up the required environment +PomTemplateContext_preparepackage=Perform any operations necessary to prepare a package before the actual packaging. This often results in an unpacked, processed version of the package. (Maven 2.1 and above) +PomTemplateContext_presite=Executes processes needed prior to the actual project site generation +PomTemplateContext_processclasses=Post-process the generated files from compilation, for example to do bytecode enhancement on Java classes +PomTemplateContext_processresources=Copy and process the resources into the destination directory, ready for packaging +PomTemplateContext_processsources=Process the source code, for example to filter any values +PomTemplateContext_processtestclasses=Post-process the generated files from test compilation, for example to do bytecode enhancement on Java classes. For Maven 2.0.5 and above +PomTemplateContext_processtestresources=Copy and process the resources into the test destination directory +PomTemplateContext_processtestsources=Process the test source code, for example to filter any values +PomTemplateContext_project_version_hint=For projects developed in sync only. +PomTemplateContext_site=Generates the project's site documentation +PomTemplateContext_sitedeploy=Deploys the generated site documentation to the specified web server +PomTemplateContext_test=Run tests using a suitable unit testing framework. These tests should not require the code be packaged or deployed +PomTemplateContext_testcompile=Compile the test source code into the test destination directory +PomTemplateContext_validate=Validate the project is correct and all necessary information is available +PomTemplateContext_verify=Run any checks to verify the package is valid and meets quality criteria +PomTextHover_eval1=<html>This expression evaluates to <b>{0}</b>{1}</html> +PomTextHover_eval2=<br>The property is defined in {0} +PomTextHover_managed_location=The artifact is managed in {0} +PomTextHover_managed_location_missing=The managed artifact's location could not be determined. +PomTextHover_managed_version=The managed version is <b>{0}</b> +PomTextHover_managed_version_missing=The managed version could not be determined diff --git a/org.eclipse.m2e.editor.xml/src/main/java/org/eclipse/m2e/editor/xml/preferences/PomTemplatesPreferencePage.java b/org.eclipse.m2e.editor.xml/src/main/java/org/eclipse/m2e/editor/xml/preferences/PomTemplatesPreferencePage.java new file mode 100644 index 00000000..67022b62 --- /dev/null +++ b/org.eclipse.m2e.editor.xml/src/main/java/org/eclipse/m2e/editor/xml/preferences/PomTemplatesPreferencePage.java @@ -0,0 +1,43 @@ +/******************************************************************************* + * 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.preferences; + +import org.eclipse.ui.texteditor.templates.TemplatePreferencePage; + +import org.eclipse.m2e.editor.xml.MvnIndexPlugin; + + + +/** + * @author Eugene Kuleshov + */ +public class PomTemplatesPreferencePage extends TemplatePreferencePage { + + public PomTemplatesPreferencePage() { + setPreferenceStore(MvnIndexPlugin.getDefault().getPreferenceStore()); + setTemplateStore(MvnIndexPlugin.getDefault().getTemplateStore()); + setContextTypeRegistry(MvnIndexPlugin.getDefault().getContextTypeRegistry()); + } + + @Override + public boolean performOk() { + boolean ok = super.performOk(); + MvnIndexPlugin.getDefault().savePluginPreferences(); + return ok; + } + + @Override + protected boolean isShowFormatterSetting() { + return false; + } + +} diff --git a/org.eclipse.m2e.editor.xml/src/main/java/org/eclipse/m2e/editor/xml/template/.gitignore b/org.eclipse.m2e.editor.xml/src/main/java/org/eclipse/m2e/editor/xml/template/.gitignore new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/org.eclipse.m2e.editor.xml/src/main/java/org/eclipse/m2e/editor/xml/template/.gitignore |