/******************************************************************************* * 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 static org.eclipse.m2e.core.ui.internal.editing.PomEdits.ARTIFACT_ID; import static org.eclipse.m2e.core.ui.internal.editing.PomEdits.DEPENDENCY; import static org.eclipse.m2e.core.ui.internal.editing.PomEdits.EXTENSION; import static org.eclipse.m2e.core.ui.internal.editing.PomEdits.GROUP_ID; import static org.eclipse.m2e.core.ui.internal.editing.PomEdits.NAME; import static org.eclipse.m2e.core.ui.internal.editing.PomEdits.PARENT; import static org.eclipse.m2e.core.ui.internal.editing.PomEdits.PLUGIN; import static org.eclipse.m2e.core.ui.internal.editing.PomEdits.PROPERTIES; import static org.eclipse.m2e.core.ui.internal.editing.PomEdits.VERSION; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.Arrays; import java.util.Iterator; import java.util.List; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.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.IMarker; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Path; 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.Position; import org.eclipse.jface.text.Region; import org.eclipse.jface.text.hyperlink.IHyperlink; import org.eclipse.jface.text.hyperlink.IHyperlinkDetector; import org.eclipse.jface.text.source.Annotation; import org.eclipse.jface.text.source.IAnnotationModel; import org.eclipse.jface.text.source.ISourceViewer; import org.eclipse.osgi.util.NLS; import org.eclipse.swt.widgets.Display; import org.eclipse.ui.IEditorInput; 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.ui.texteditor.MarkerAnnotation; 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.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.Model; import org.apache.maven.model.Plugin; import org.apache.maven.model.PluginManagement; import org.apache.maven.project.MavenProject; import org.eclipse.m2e.core.internal.IMavenConstants; import org.eclipse.m2e.core.ui.internal.actions.OpenPomAction; import org.eclipse.m2e.core.ui.internal.actions.OpenPomAction.MavenPathStorageEditorInput; import org.eclipse.m2e.editor.xml.internal.Messages; import org.eclipse.m2e.editor.xml.internal.NodeOperation; import org.eclipse.m2e.editor.xml.internal.XmlUtils; /** * @author Eugene Kuleshov * @author Milos Kleint */ public class PomHyperlinkDetector implements IHyperlinkDetector { private static final Logger log = LoggerFactory.getLogger(PomHyperlinkDetector.class); public IHyperlink[] detectHyperlinks(final 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; } final List<IHyperlink> hyperlinks = new ArrayList<IHyperlink>(); final int offset = region.getOffset(); XmlUtils.performOnCurrentElement(document, offset, new NodeOperation<Node>() { public void process(Node node, IStructuredDocument structured) { if(textViewer instanceof ISourceViewer) { IHyperlink[] links = openExternalMarkerDefinition((ISourceViewer) textViewer, offset); if(links.length > 0) { hyperlinks.addAll(Arrays.asList(links)); } } //check if we have a property expression at cursor IHyperlink link = openPropertyDefinition(node, 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(node, textViewer, offset); if(link != null) { hyperlinks.add(link); } //check if <module> text is selected. link = openModule(node, textViewer, offset); if(link != null) { hyperlinks.add(link); } link = openPOMbyID(node, textViewer, offset); 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(ARTIFACT_ID.equals(current.getNodeName())) { //$NON-NLS-1$ artNode = current; } if(GROUP_ID.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 && ARTIFACT_ID.equals(el.getNodeName())) { //$NON-NLS-1$ artNode = el; } if(groupNode == null && GROUP_ID.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 = XmlUtils.getTextValue(groupNode); String artifactId = XmlUtils.getTextValue(artNode); final MavenProject prj = XmlUtils.extractMavenProject(textViewer); if(prj != null) { //now we can create the region I guess, return new ManagedArtifactRegion(startOffset, length, groupId, artifactId, isDependency, isPlugin, prj); } } } return null; } public static IHyperlink createHyperlink(final ManagedArtifactRegion region) { 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. MavenProject mavprj = region.project; if(mavprj != null) { InputLocation openLocation = findLocationForManagedArtifact(region, mavprj); if(openLocation != null) { File file = XmlUtils.fileForInputLocation(openLocation, mavprj); if(file != null) { IFileStore fileStore = EFS.getLocalFileSystem().getStore(file.toURI()); openXmlEditor(fileStore, openLocation.getLineNumber(), openLocation.getColumnNumber(), openLocation .getSource().getModelId()); } } } } }; } private IHyperlink openDPManagement(Node current, ITextViewer textViewer, int offset) { final ManagedArtifactRegion region = findManagedArtifactRegion(current, textViewer, offset); if(region != null) { return createHyperlink(region); } 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(ARTIFACT_ID); //$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(ARTIFACT_ID); //$NON-NLS-1$ //when would this be null? if(location != null) { openLocation = location; break; } } } } } } } return openLocation; } 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. // } MavenProject prj = XmlUtils.extractMavenProject(viewer); if(prj != null) { return new ExpressionRegion(startOffset, length, prop, prj); } } } } return null; } public static IHyperlink createHyperlink(final ExpressionRegion region) { 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. MavenProject mavprj = region.project; if(mavprj != null) { //TODO get rid of InputLocation here and use the dom tree to find the property Model mdl = mavprj.getModel(); InputLocation location = null; if(mdl.getProperties().containsKey(region.property)) { location = mdl.getLocation(PROPERTIES).getLocation(region.property); } else if(region.property != null && region.property.startsWith("project.")) {//$NON-NLS-1$ if("project.version".equals(region.property)) { location = mdl.getLocation(VERSION); } else if("project.name".equals(region.property)) { location = mdl.getLocation(NAME); } } if(location != null) { File file = XmlUtils.fileForInputLocation(location, mavprj); if(file != null) { IFileStore fileStore = EFS.getLocalFileSystem().getStore(file.toURI()); openXmlEditor(fileStore, location.getLineNumber(), location.getColumnNumber(), location.getSource() .getModelId()); } } } } }; } public static boolean canCreateHyperLink(final ExpressionRegion region) { if("project.version".equals(region.property) || "project.name".equals(region.property)) { return true; } return region.project != null && region.project.getModel().getProperties().containsKey(region.property); } //only create the hyperlink when the origin location for jumping is present. //in some cases (managed version comes from imported dependencies) we don't have the location and have nowhere to jump) public static boolean canCreateHyperLink(final ManagedArtifactRegion region) { return region.project != null && PomHyperlinkDetector.findLocationForManagedArtifact(region, region.project) != null; } static IHyperlink[] openExternalMarkerDefinition(ISourceViewer sourceViewer, int offset) { List<IHyperlink> toRet = new ArrayList<IHyperlink>(); MarkerRegion[] regions = findMarkerRegions(sourceViewer, offset); for(MarkerRegion reg : regions) { if(reg.isDefinedInParent()) { toRet.add(createHyperlink(reg)); } } return toRet.toArray(new IHyperlink[0]); } static MarkerRegion[] findMarkerRegions(ISourceViewer sourceViewer, int offset) { List<MarkerRegion> toRet = new ArrayList<MarkerRegion>(); IAnnotationModel model = sourceViewer.getAnnotationModel(); if(model != null) { //eg. in tests @SuppressWarnings("unchecked") Iterator<Annotation> it = model.getAnnotationIterator(); while(it.hasNext()) { Annotation ann = it.next(); if(ann instanceof MarkerAnnotation) { Position pos = sourceViewer.getAnnotationModel().getPosition(ann); if(pos.includes(offset)) { toRet.add(new MarkerRegion(pos.getOffset(), pos.getLength(), (MarkerAnnotation) ann)); } } } } return toRet.toArray(new MarkerRegion[0]); } public static IHyperlink createHyperlink(final MarkerRegion mark) { return new IHyperlink() { public IRegion getHyperlinkRegion() { return new Region(mark.getOffset(), mark.getLength()); } public String getTypeLabel() { return "marker-error-defined-in-parent"; //$NON-NLS-1$; } public String getHyperlinkText() { return NLS.bind("Open definition in parent for {0}", mark.getAnnotation().getText()); //TODO if there are multiple markers in one spot, how to differentiate better.. } public void open() { IMarker marker = mark.getAnnotation().getMarker(); String loc = marker.getAttribute(IMavenConstants.MARKER_CAUSE_RESOURCE_PATH, null); if(loc != null) { IFileStore fileStore = EFS.getLocalFileSystem().getStore(new Path(loc)); int row = marker.getAttribute(IMavenConstants.MARKER_CAUSE_LINE_NUMBER, 0); int column = marker.getAttribute(IMavenConstants.MARKER_CAUSE_COLUMN_START, 0); String name = marker.getAttribute(IMavenConstants.MARKER_CAUSE_RESOURCE_ID, null); // String hint = marker.getAttribute(IMavenConstants.MARKER_ATTR_EDITOR_HINT, null); // if (IMavenConstants.EDITOR_HINT_NOT_COVERED_MOJO_EXECUTION.equals(hint)) { // } openXmlEditor(fileStore, row, column, name); } } }; } private IHyperlink openPropertyDefinition(Node current, ITextViewer viewer, int offset) { final ExpressionRegion region = findExpressionRegion(current, viewer, offset); if(region != null && canCreateHyperLink(region)) { return createHyperlink(region); } return null; } private IHyperlink openModule(Node current, ITextViewer textViewer, int offset) { while(current != null && !(current instanceof Element)) { current = current.getParentNode(); } if(current == null) { return null; } String pathUp = XmlUtils.pathUp(current, 2); if(!"modules/module".equals(pathUp)) { //$NON-NLS-1$ //just in case we are in some random plugin configuration snippet.. return null; } ITextFileBuffer buf = FileBuffers.getTextFileBufferManager().getTextFileBuffer(textViewer.getDocument()); if(buf == null) { //for repository based poms.. return null; } IFileStore folder = buf.getFileStore().getParent(); String path = XmlUtils.getTextValue(current); final String fPath = path; //construct IPath for the child pom file, handle relative paths.. while(folder != null && path.startsWith("../")) { //$NON-NLS-1$ folder = folder.getParent(); path = path.substring("../".length());//$NON-NLS-1$ } if(folder == null) { return null; } IFileStore modulePom = folder.getChild(path); if(!modulePom.getName().endsWith("xml")) { //$NON-NLS-1$ modulePom = modulePom.getChild("pom.xml");//$NON-NLS-1$ } final IFileStore fileStore = modulePom; if(!fileStore.fetchInfo().exists()) { return null; } assert current instanceof IndexedRegion; final IndexedRegion region = (IndexedRegion) current; return new IHyperlink() { public IRegion getHyperlinkRegion() { return new Region(region.getStartOffset(), region.getEndOffset() - region.getStartOffset()); } public String getHyperlinkText() { return NLS.bind(Messages.PomHyperlinkDetector_open_module, fPath); } public String getTypeLabel() { return "pom-module"; //$NON-NLS-1$ } public void open() { openXmlEditor(fileStore); } }; } private IHyperlink openPOMbyID(Node current, final ITextViewer viewer, int offset) { while(current != null && !(current instanceof Element)) { current = current.getParentNode(); } if(current == null) { return null; } current = current.getParentNode(); if(current == null || !(current instanceof Element)) { return null; } Element parent = (Element) current; String parentName = parent.getNodeName(); if(DEPENDENCY.equals(parentName) || PARENT.equals(parentName) || PLUGIN.equals(parentName) || "reportPlugin".equals(parentName) || EXTENSION.equals(parentName)) { final Node groupId = XmlUtils.findChild(parent, GROUP_ID); final Node artifactId = XmlUtils.findChild(parent, ARTIFACT_ID); final Node version = XmlUtils.findChild(parent, VERSION); final MavenProject prj = XmlUtils.extractMavenProject(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 ? ((IndexedRegion) groupId).getEndOffset() : Integer.MIN_VALUE; int min = groupId != null ? ((IndexedRegion) groupId).getStartOffset() : Integer.MAX_VALUE; max = Math.max(max, artifactId != null ? ((IndexedRegion) artifactId).getEndOffset() : Integer.MIN_VALUE); min = Math.min(min, artifactId != null ? ((IndexedRegion) artifactId).getStartOffset() : Integer.MAX_VALUE); max = Math.max(max, version != null ? ((IndexedRegion) version).getEndOffset() : Integer.MIN_VALUE); min = Math.min(min, version != null ? ((IndexedRegion) version).getStartOffset() : Integer.MAX_VALUE); return new Region(min, max - min); } public String getHyperlinkText() { return NLS.bind(Messages.PomHyperlinkDetector_hyperlink_pattern, XmlUtils.getTextValue(groupId), XmlUtils.getTextValue(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" : XmlUtils.getTextValue(groupId); //$NON-NLS-1$ String artidString = artifactId == null ? null : XmlUtils.getTextValue(artifactId); String versionString = version == null ? null : XmlUtils.getTextValue(version); if(prj != null && gridString != null && artidString != null && (versionString == null || versionString.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, null, versionString, gridString, artidString, PomTemplateContext.EXTRACT_STRATEGY_DEPENDENCY); } catch(CoreException e) { versionString = null; } } if(versionString == null) { return Status.OK_STATUS; } OpenPomAction.openEditor(gridString, artidString, versionString, prj, monitor); // TODO: it's preferable to open the xml page, but this code will blink and open overview first and later switch. looks bad // Display.getDefault().syncExec(new Runnable() { // public void run() { // selectEditorPage(page); // } // }); return Status.OK_STATUS; } }.schedule(); } }; return pomHyperlink; } return null; } private void openXmlEditor(final IFileStore fileStore) { openXmlEditor(fileStore, -1, -1, fileStore.getName()); } private static void openXmlEditor(final IFileStore fileStore, int line, int column, String name) { assert fileStore != null; IWorkbenchWindow window = PlatformUI.getWorkbench().getActiveWorkbenchWindow(); if(window != null) { IWorkbenchPage page = window.getActivePage(); if(page != null) { try { if(!fileStore.getName().endsWith(".pom")) { //.pom means stuff from local repository? IEditorPart part = IDE.openEditorOnFileStore(page, fileStore); reveal(selectEditorPage(part), line, column); } else { //we need special EditorInput for stuff from repository name = name + ".pom"; //$NON-NLS-1$ File file = new File(fileStore.toURI()); try { IEditorInput input = new MavenPathStorageEditorInput(name, name, file.getAbsolutePath(), readStream(new FileInputStream(file))); IEditorPart part = OpenPomAction.openEditor(input, name); reveal(selectEditorPage(part), line, column); } catch(IOException e) { log.error("failed opening editor", e); } } } catch(PartInitException e) { MessageDialog.openInformation( Display.getDefault().getActiveShell(), // Messages.PomHyperlinkDetector_error_title, NLS.bind(Messages.PomHyperlinkDetector_error_message, fileStore, e.toString())); } } } } private static StructuredTextEditor selectEditorPage(IEditorPart part) { if(part == null) { return null; } if(part instanceof FormEditor) { FormEditor ed = (FormEditor) part; ed.setActivePage(null); //null means source, always or just in the case of MavenPomEditor? if(ed.getActiveEditor() instanceof StructuredTextEditor) { return (StructuredTextEditor) ed.getActiveEditor(); } } return null; } private static void reveal(StructuredTextEditor structured, int line, int column) { if(structured == null || line < 0 || column < 0) { return; } 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) { log.error("failed selecting part of editor", e); } } } /** * duplicate of OpenPomAction method * * @param is * @return * @throws IOException */ private static byte[] readStream(InputStream is) throws IOException { byte[] b = new byte[is.available()]; int len = 0; while(true) { int n = is.read(b, len, b.length - len); if(n == -1) { if(len < b.length) { byte[] c = new byte[len]; System.arraycopy(b, 0, c, 0, len); b = c; } return b; } len += n; if(len == b.length) { byte[] c = new byte[b.length + 1000]; System.arraycopy(b, 0, c, 0, len); b = c; } } } public static class ExpressionRegion implements IRegion { final String property; private int length; private int offset; final MavenProject project; public ExpressionRegion(int startOffset, int length, String prop, MavenProject project) { this.offset = startOffset; this.length = length; this.property = prop; this.project = project; assert project != null; } public int getLength() { return length; } public int getOffset() { return offset; } } public static class ManagedArtifactRegion implements IRegion { private int length; private int offset; final MavenProject 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, MavenProject project) { this.offset = startOffset; this.length = length; this.project = project; assert project != null; this.artifactId = artifactId; this.groupId = groupId; this.isDependency = isDependency; this.isPlugin = isPlugin; } public int getLength() { return length; } public int getOffset() { return offset; } } public static class MarkerRegion implements IRegion { private final MarkerAnnotation ann; final int offset; final int length; public MarkerRegion(int offset, int length, MarkerAnnotation applicable) { this.offset = offset; this.length = length; this.ann = applicable; } public int getLength() { return length; } public int getOffset() { return offset; } public MarkerAnnotation getAnnotation() { return ann; } public boolean isDefinedInParent() { IMarker mark = ann.getMarker(); String isElsewhere = mark.getAttribute(IMavenConstants.MARKER_CAUSE_RESOURCE_PATH, null); if(isElsewhere != null) { return true; } return false; } } }