/*******************************************************************************
* Copyright (c) 2007 Red Hat, Inc.
* Distributed under license by Red Hat, Inc. All rights reserved.
* This program is 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:
* Red Hat, Inc. - initial API and implementation
******************************************************************************/
package org.jboss.tools.seam.ui.text.java;
import java.util.Map;
import java.util.Set;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IMember;
import org.eclipse.jdt.core.IMethod;
import org.eclipse.jdt.core.ISourceRange;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.MethodDeclaration;
import org.eclipse.jdt.internal.ui.javaeditor.EditorUtility;
import org.eclipse.jdt.internal.ui.javaeditor.JavaEditor;
import org.eclipse.jdt.internal.ui.text.JavaWordFinder;
import org.eclipse.jdt.ui.JavaUI;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IRegion;
import org.eclipse.jface.text.ITextViewer;
import org.eclipse.jface.text.hyperlink.AbstractHyperlinkDetector;
import org.eclipse.jface.text.hyperlink.IHyperlink;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.texteditor.ITextEditor;
import org.jboss.tools.seam.core.IOpenableElement;
import org.jboss.tools.seam.core.ISeamComponentDeclaration;
import org.jboss.tools.seam.core.ISeamContextShortVariable;
import org.jboss.tools.seam.core.ISeamContextVariable;
import org.jboss.tools.seam.core.ISeamProject;
import org.jboss.tools.seam.core.SeamCorePlugin;
import org.jboss.tools.seam.internal.core.BijectedAttribute;
import org.jboss.tools.seam.internal.core.Role;
import org.jboss.tools.seam.internal.core.SeamComponent;
import org.jboss.tools.seam.internal.core.SeamJavaContextVariable;
import org.jboss.tools.seam.internal.core.scanner.ScannerException;
import org.jboss.tools.seam.internal.core.scanner.java.AnnotatedASTNode;
import org.jboss.tools.seam.internal.core.scanner.java.ResolvedAnnotation;
import org.jboss.tools.seam.internal.core.scanner.java.SeamAnnotations;
import org.jboss.tools.seam.ui.SeamGuiPlugin;
import org.jboss.tools.seam.ui.text.java.scanner.JavaAnnotationScanner;
/**
* @author Jeremy
*/
public class JavaStringHyperlinkDetector extends AbstractHyperlinkDetector {
/*
* If the hyperlink is performed from the @Factory annotation value
* the declaration of the variable will be openned in the editor
*
* @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) {
ITextEditor textEditor= (ITextEditor)getAdapter(ITextEditor.class);
if (region == null || canShowMultipleHyperlinks || !(textEditor instanceof JavaEditor))
return null;
int offset= region.getOffset();
IJavaElement input= EditorUtility.getEditorInputJavaElement(textEditor, false);
if (input == null)
return null;
if (input.getResource() == null || input.getResource().getProject() == null)
return null;
ISeamProject seamProject = SeamCorePlugin.getSeamProject(input.getResource().getProject(), true);
IDocument document= textEditor.getDocumentProvider().getDocument(textEditor.getEditorInput());
IRegion wordRegion= JavaWordFinder.findWord(document, offset);
if (wordRegion == null)
return null;
JavaAnnotationScanner annotationScanner = new JavaAnnotationScanner();
Map<ResolvedAnnotation, AnnotatedASTNode<ASTNode>> loadedAnnotations = null;
IType loadedType = null;
try {
annotationScanner.parse((ICompilationUnit)input);
loadedAnnotations = annotationScanner.getResolvedAnnotations();
loadedType = annotationScanner.getResolvedType();
} catch (ScannerException e) {
SeamGuiPlugin.getPluginLog().logError(e);
return null;
}
ResolvedAnnotation a = annotationScanner.findAnnotationByValueOffset(offset);
if (!annotationScanner.isAnnotationOfType(a, SeamAnnotations.FACTORY_ANNOTATION_TYPE))
return null;
String value = annotationScanner.getAnnotationValue(a);
// Look at the annotated method:
// If its return type is not void - the Declaration is the factory itself
// If its return type is void - search for the declarations
AnnotatedASTNode<ASTNode> node = loadedAnnotations.get(a);
if (!(node.getNode() instanceof MethodDeclaration))
return null;
MethodDeclaration mDecl = (MethodDeclaration)node.getNode();
IMember member = findMethod(loadedType, mDecl);
IMethod method = (member instanceof IMethod ? (IMethod)member : null);
if (method == null)
return null;
String returnType = null;
try {
returnType = method.getReturnType();
} catch (JavaModelException e) {
SeamGuiPlugin.getPluginLog().logError(e);
return null;
}
if ("V".equals(returnType)) {
// search for the declaration of the variable
Set<ISeamContextVariable> variables = seamProject.getVariablesByName(value);
if (variables != null && !variables.isEmpty()) {
for (ISeamContextVariable var : variables) {
if (var instanceof ISeamContextShortVariable) {
// Extract the original variable
var = ((ISeamContextShortVariable)var).getOriginal();
}
if (var instanceof SeamComponent) {
SeamComponent comp = (SeamComponent)var;
Set<ISeamComponentDeclaration> declarations = comp.getAllDeclarations();
for (ISeamComponentDeclaration decl : declarations) {
if (decl instanceof IOpenableElement)
return new IHyperlink[] {new SeamOpenableElementHyperlink(wordRegion, (IOpenableElement)decl)};
}
}
if (var instanceof BijectedAttribute ||
var instanceof Role) {
return new IHyperlink[] {new JavaMemberHyperlink(wordRegion, ((SeamJavaContextVariable)var).getSourceMember())};
}
}
}
return null;
}
// open the factory method itself as the declaration
return new IHyperlink[] {new JavaMemberHyperlink(wordRegion, method)};
}
/*
* Finds the IMethod in IType by its MethodDeclaration
*
* @param type
* @param m
*
* @return IMethod found
*/
public IMethod findMethod(IType type, MethodDeclaration m) {
if(m == null || m.getName() == null) return null;
IMethod[] ms = null;
try {
ms = type.getMethods();
} catch (JavaModelException e) {
SeamGuiPlugin.getDefault().logError(e);
}
String name = m.getName().getIdentifier();
if(ms != null) for (int i = 0; i < ms.length; i++) {
if(!name.equals(ms[i].getElementName())) continue;
int s = m.getStartPosition() + m.getLength() / 2;
try {
ISourceRange range = ms[i].getSourceRange();
if(range == null) {
//no source and we cannot check position.
return ms[i];
}
int b = range.getOffset();
int e = b + range.getLength();
if(s >= b && s <= e) return ms[i];
} catch (JavaModelException e) {
return ms[i];
}
}
return null;
}
class JavaMemberHyperlink implements IHyperlink {
private final IRegion fRegion;
private final IMember fMember;
IMember member;
public JavaMemberHyperlink(IRegion region, IMember member) {
this.fRegion = region;
this.fMember = member;
}
public IRegion getHyperlinkRegion() {
return fRegion;
}
public String getHyperlinkText() {
return null;
}
public String getTypeLabel() {
return null;
}
public void open() {
try {
IEditorPart part = JavaUI.openInEditor(fMember);
if (part != null) {
JavaUI.revealInEditor(part, (IJavaElement)fMember);
}
} catch (PartInitException e) {
SeamGuiPlugin.getDefault().logError(e);
} catch (JavaModelException e) {
SeamGuiPlugin.getDefault().logError(e);
}
}
}
class SeamOpenableElementHyperlink implements IHyperlink {
private final IRegion fRegion;
private final IOpenableElement fOpenable;
IMember member;
public SeamOpenableElementHyperlink(IRegion region, IOpenableElement openable) {
this.fRegion = region;
this.fOpenable = openable;
}
public IRegion getHyperlinkRegion() {
return fRegion;
}
public String getHyperlinkText() {
return null;
}
public String getTypeLabel() {
return null;
}
public void open() {
fOpenable.open();
}
}
}