/******************************************************************************* * Copyright (c) 2008, 2015 IBM Corporation and others. * 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: * IBM Corporation - initial API and implementation *******************************************************************************/ package org.eclipse.jst.jsp.ui.internal.hyperlink; import org.eclipse.core.filebuffers.FileBuffers; import org.eclipse.core.filebuffers.ITextFileBuffer; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.Path; import org.eclipse.jdt.core.IJavaElement; import org.eclipse.jdt.core.IJavaProject; import org.eclipse.jdt.core.IPackageFragmentRoot; import org.eclipse.jdt.core.JavaCore; import org.eclipse.jdt.core.JavaModelException; import org.eclipse.jdt.ui.JavaElementLabels; import org.eclipse.jdt.ui.JavaUI; 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.AbstractHyperlinkDetector; import org.eclipse.jface.text.hyperlink.IHyperlink; import org.eclipse.jst.jsp.core.internal.Logger; import org.eclipse.jst.jsp.core.internal.java.IJSPTranslation; import org.eclipse.jst.jsp.core.internal.java.JSPTranslation; import org.eclipse.jst.jsp.core.internal.java.JSPTranslationAdapter; import org.eclipse.jst.jsp.ui.internal.JSPUIMessages; import org.eclipse.osgi.util.NLS; import org.eclipse.ui.PartInitException; import org.eclipse.wst.sse.core.StructuredModelManager; import org.eclipse.wst.sse.core.internal.provisional.IStructuredModel; import org.eclipse.wst.sse.core.utils.StringUtils; import org.eclipse.wst.xml.core.internal.provisional.document.IDOMDocument; import org.eclipse.wst.xml.core.internal.provisional.document.IDOMModel; /** * Detects hyper-links to type names */ public class XMLJavaHyperlinkDetector extends AbstractHyperlinkDetector { private static final String JAR_FILE_PROTOCOL = "jar:file:"; //$NON-NLS-1$ /** * Based on org.eclipse.jdt.internal.ui.javaeditor.JavaElementHyperlink */ static class JavaElementHyperlink implements IHyperlink { private final IJavaElement fElement; private final IRegion fRegion; /** * Creates a new Java element hyperlink. * * @param region * the region of the link * @param element * the java element to open * @param qualify * <code>true</code> if the hyper-link text should show a * qualified name for element. */ JavaElementHyperlink(IRegion region, IJavaElement element) { fRegion = region; fElement = element; } public IRegion getHyperlinkRegion() { return fRegion; } public String getHyperlinkText() { String elementLabel = JavaElementLabels.getElementLabel(fElement, JavaElementLabels.ALL_POST_QUALIFIED); return NLS.bind(JSPUIMessages.Open, elementLabel); } public String getTypeLabel() { return null; } public void open() { try { JavaUI.openInEditor(fElement); } catch (PartInitException e) { } catch (JavaModelException e) { } } } private IHyperlink createHyperlink(String elementName, IRegion region, IDocument document) { // try file buffers ITextFileBuffer textFileBuffer = FileBuffers.getTextFileBufferManager().getTextFileBuffer(document); if (textFileBuffer != null) { IPath basePath = textFileBuffer.getLocation(); if (basePath != null && !basePath.isEmpty()) { IProject project = ResourcesPlugin.getWorkspace().getRoot().getProject(basePath.segment(0)); try { if (basePath.segmentCount() > 1 && project.isAccessible() && project.hasNature(JavaCore.NATURE_ID)) { return createJavaElementHyperlink(JavaCore.create(project), elementName, region); } } catch (CoreException e) { Logger.logException(e); } } } // fallback to SSE-specific knowledge IStructuredModel model = null; try { model = StructuredModelManager.getModelManager().getExistingModelForRead(document); if (model != null) { String baseLocation = model.getBaseLocation(); // URL fixup from the taglib index record if (baseLocation.startsWith("jar:/file:")) { //$NON-NLS-1$ baseLocation = StringUtils.replace(baseLocation, "jar:/", "jar:"); //$NON-NLS-1$ //$NON-NLS-2$ } /* * Handle opened TLD files from JARs on the Java Build Path by * finding a package fragment root for the same .jar file and * opening the class from there. Note that this might be from * a different Java project's build path than the TLD. */ if (baseLocation.startsWith(JAR_FILE_PROTOCOL) && baseLocation.indexOf('!') > JAR_FILE_PROTOCOL.length()) { String baseFile = baseLocation.substring(JAR_FILE_PROTOCOL.length(), baseLocation.indexOf('!')); IPath basePath = new Path(baseFile); IProject[] projects = ResourcesPlugin.getWorkspace().getRoot().getProjects(); for (int i = 0; i < projects.length; i++) { try { if (projects[i].isAccessible() && projects[i].hasNature(JavaCore.NATURE_ID)) { IJavaProject javaProject = JavaCore.create(projects[i]); if (javaProject.exists()) { IPackageFragmentRoot root = javaProject.findPackageFragmentRoot(basePath); if (root != null) { return createJavaElementHyperlink(javaProject, elementName, region); } } } } catch (CoreException e) { Logger.logException(e); } } } else { IPath basePath = new Path(baseLocation); if (basePath.segmentCount() > 1) { return createJavaElementHyperlink(JavaCore.create(ResourcesPlugin.getWorkspace().getRoot().getProject(basePath.segment(0))), elementName, region); } } } } finally { if (model != null) model.releaseFromRead(); } return null; } private boolean isJspJavaContent(IDocument document, IRegion region) { JSPTranslation translation = null; IStructuredModel model = null; try { model = StructuredModelManager.getModelManager().getExistingModelForRead(document); if (model instanceof IDOMModel) { IDOMModel xmlModel = (IDOMModel) model; if (xmlModel != null) { final IDOMDocument xmlDoc = xmlModel.getDocument(); final JSPTranslationAdapter adapter = (JSPTranslationAdapter) xmlDoc.getAdapterFor(IJSPTranslation.class); if (adapter != null) { translation = adapter.getJSPTranslation(); if (translation!=null) { int javaOffset = translation.getJavaOffset(region.getOffset()); if (javaOffset > -1) { return true; } } } } } } finally { if (model != null) { model.releaseFromRead(); } } return false; } private IHyperlink createJavaElementHyperlink(IJavaProject javaProject, String elementName, IRegion region) { if (javaProject != null && javaProject.exists()) { try { IJavaElement element = javaProject.findType(elementName); if (element != null && element.exists()) { return new JavaElementHyperlink(region, element); } } catch (JavaModelException e) { // bad name, no link } } return null; } /* * (non-Javadoc) * * @see * org.eclipse.jface.text.hyperlink.IHyperlinkDetector#detectHyperlinks * (org.eclipse.jface.text.ITextViewer, org.eclipse.jface.text.IRegion, * boolean) */ public IHyperlink[] detectHyperlinks(ITextViewer textViewer, IRegion region, boolean canShowMultipleHyperlinks) { if (region != null && textViewer != null) { IDocument document = textViewer.getDocument(); // find hyperlink range for Java element IRegion hyperlinkRegion = region.getLength() > 0 ? region : selectQualifiedName(document, region.getOffset()); if (hyperlinkRegion == null || // Bug #462949 - Because selectQualifiedName may return null isJspJavaContent(document, hyperlinkRegion)) { // Handled by JSPJavaHyperlinkDetector return null; } String name = null; try { name = document.get(hyperlinkRegion.getOffset(), hyperlinkRegion.getLength()).trim(); } catch (BadLocationException e) { } if (name != null) { IHyperlink link = createHyperlink(name, hyperlinkRegion, document); if (link != null) return new IHyperlink[]{link}; } } return null; } /** * Java always selects word when defining region * * @param document * @param anchor * @return IRegion */ private IRegion selectQualifiedName(IDocument document, int anchor) { try { int offset = anchor; char c; while (offset >= 0) { c = document.getChar(offset); if (!Character.isJavaIdentifierPart(c) && c != '.') break; --offset; } int start = offset; offset = anchor; int length = document.getLength(); while (offset < length) { c = document.getChar(offset); if (!Character.isJavaIdentifierPart(c) && c != '.') break; ++offset; } int end = offset; if (start == end) return new Region(start, 0); return new Region(start + 1, end - start - 1); } catch (BadLocationException x) { return null; } } }