/*******************************************************************************
* Copyright (c) 2007 IBM Corporation.
* 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:
* Robert Fuhrer (rfuhrer@watson.ibm.com) - initial API and implementation
*******************************************************************************/
package org.eclipse.imp.editor;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.IPath;
import org.eclipse.imp.language.ILanguageService;
import org.eclipse.imp.language.Language;
import org.eclipse.imp.language.ServiceFactory;
import org.eclipse.imp.parser.IParseController;
import org.eclipse.imp.parser.ISourcePositionLocator;
import org.eclipse.imp.services.IEntityNameLocator;
import org.eclipse.imp.services.IReferenceResolver;
import org.eclipse.imp.services.ISourceHyperlinkDetector;
import org.eclipse.jface.text.IRegion;
import org.eclipse.jface.text.ITextViewer;
import org.eclipse.jface.text.hyperlink.IHyperlink;
import org.eclipse.ui.IFileEditorInput;
import org.eclipse.ui.texteditor.ITextEditor;
/**
* Provides a method to detect hyperlinks originating from a
* given region in the parse stream of a given parse controller.
*/
public class HyperlinkDetector implements ISourceHyperlinkDetector, ILanguageService {
private IReferenceResolver fResolver;
private IEntityNameLocator fEntityNameLocator;
private final Language fLanguage;
public HyperlinkDetector(Language lang) {
fLanguage= lang;
fEntityNameLocator= ServiceFactory.getInstance().getEntityNameLocator(fLanguage);
}
public IHyperlink[] detectHyperlinks(final IRegion region, ITextEditor editor, final ITextViewer textViewer, IParseController parseController) {
// This is the only language-specific bit ...
if (fResolver == null) {
fResolver = ServiceFactory.getInstance().getReferenceResolver(fLanguage);
}
// SMS 17 Aug 2007
if (fResolver == null)
return null;
if (parseController == null)
return null;
// Get stuff for getting link source node
Object ast= parseController.getCurrentAst();
if (ast == null) return null;
int offset= region.getOffset();
ISourcePositionLocator nodeLocator = parseController.getSourcePositionLocator();
// Get link source node
Object source = nodeLocator.findNode(ast, offset);
if (source == null) return null;
// Got a suitable link source node; get link target node
Object target = fResolver.getLinkTarget(source, parseController);
if (target == null) return null;
if (fEntityNameLocator != null) {
Object name= fEntityNameLocator.getName(target);
if (name != null) {
target= name;
}
}
// Link target node exists; get info for new hyperlink
// Note: source presumably has a legitimate starting offset
// and length (since they have been selected from the source file)
final int srcStart= nodeLocator.getStartOffset(source);
final int srcLength= nodeLocator.getEndOffset(source) - srcStart + 1;
// The target (depending on what--and where--the target is) may not have a
// legitimate location (or one within the file). In that case, set the target
// to the beginning of the file and give it a nominal length.
final int targetStart= (nodeLocator.getStartOffset(target) < 0) ? 0 : nodeLocator.getStartOffset(target);
final int targetLength= 0; // nodeLocator.getEndOffset(target) - targetStart + 1;
// Use the file path info to determine whether the target editor is the same as
// the source editor, and initialize the TargetLink accordingly.
final IPath targetPath= nodeLocator.getPath(target);
// SMS 10 Sep 2007
if (targetPath == null) {
//System.out.println("HyperlinkDetector.detectHyperlinks(..): targetPath == null, returning null");
return null;
}
final String linkText = fResolver.getLinkText(source);
IPath srcPath= ((IFileEditorInput) editor.getEditorInput()).getFile().getLocation();
// SMS 11 Jun 2007: default implementation of getPath in NodeLocator template returns
// an empty path, so test for that here and assume it means that the link target is in
// the same unit as the link source
IPath wsPath= ResourcesPlugin.getWorkspace().getRoot().getLocation();
boolean isSamePath= targetPath.equals(srcPath) || srcPath.removeFirstSegments(wsPath.segmentCount()).setDevice(null).makeAbsolute().equals(targetPath);
// SMS 5 Aug 2008
// The above test for isSamePath doesn't necessarily work because the default node
// locator always returns an empty path for any given node. Special customization
// is needed which, among our known IDEs, has only been done for X10DT (PolyglotNodeLocator)
// and LPG. So in cases where it might make sense, we can try relying on the node locator
// to return a negative offset for a node that is not in a given AST. I.e., if the target
// node is not in the same AST as the source node then assume their paths are different.
// OK, this might not be strictly true if you can get more than one AST from a single
// file, but it will prevent problems in the common case of one AST per source file.
if (!isSamePath && targetPath.segmentCount() == 0) {
isSamePath = nodeLocator.getStartOffset(target) >= 0;
}
ITextEditor targetEditor= (targetPath.segmentCount() == 0 || isSamePath) ? editor : null;
Object targetArg= targetEditor == null ? targetPath : target;
// If the target is exactly the same entity, don't bother with the hyperlink.
if (srcStart == targetStart && srcLength == targetLength && targetPath.equals(srcPath))
return null;
IRegionSelectionService selService= isSamePath ? (IRegionSelectionService) editor.getAdapter(IRegionSelectionService.class) : null;
IHyperlink[] result = new IHyperlink[] {
new TargetLink(linkText, srcStart, srcLength, targetArg, targetStart, targetLength, selService)
};
return result;
}
}