package tk.eclipse.plugin.jseditor.editors;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IMarker;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IWorkspaceRoot;
import org.eclipse.core.resources.ResourcesPlugin;
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.ui.IEditorPart;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.ide.IDE;
import org.eclipse.ui.ide.IGotoMarker;
import tk.eclipse.plugin.htmleditor.HTMLPlugin;
import tk.eclipse.plugin.htmleditor.HTMLProjectParams;
import tk.eclipse.plugin.htmleditor.HTMLUtil;
import tk.eclipse.plugin.jseditor.launch.JavaScriptLibraryTable;
/**
* <code>IHyperlinkDetector</code> for JavaScript.
* <p>
* This detector detects available functions and variables
* at the caret position.
*
* @author Naoki Takezoe
*/
public class JavaScriptHyperlinkDetector implements IHyperlinkDetector {
/** Functions which are defined in the library files */
private List<FunctionInfo> functions = new ArrayList<FunctionInfo>();
/**
* Returns hyperlinks or null.
*/
public IHyperlink[] detectHyperlinks(ITextViewer textViewer,
IRegion region, boolean canShowMultipleHyperlinks) {
String source = textViewer.getDocument().get();
int offset = region.getOffset();
// extracts the word at the caret position
String word = String.valueOf(source.charAt(offset));
int index = offset -1;
while(index >= 0){
char c = source.charAt(index);
if(Character.isJavaIdentifierPart(c)){
word = c + word;
index--;
} else {
break;
}
}
index = offset + 1;
offset = offset - word.length() + 1;
while(source.length()>index){
char c = source.charAt(index);
if(Character.isJavaIdentifierPart(c)){
word = word + c;
index++;
} else if(c=='('){
break;
} else {
return null;
}
}
// itselfs
JavaScriptModel model = new JavaScriptModel(source);
JavaScriptContext context = model.getContextFromOffset(offset);
if(context!=null){
JavaScriptElement[] children = context.getVisibleElements();
for(int i=0;i<children.length;i++){
if(children[i] instanceof JavaScriptFunction){
JavaScriptFunction function = (JavaScriptFunction)children[i];
if(children[i].getName().equals(word)){
IRegion hyperlinkRegion = new Region(offset, word.length());
return new IHyperlink[]{
new JavaScriptHyperlink(hyperlinkRegion, textViewer,
function.getOffset())
};
}
}
}
}
// libraries
for(int i=0;i<functions.size();i++){
FunctionInfo info = functions.get(i);
if(info.function.getName().equals(word)){
IRegion hyperlinkRegion = new Region(offset, word.length());
return new IHyperlink[]{
new JavaScriptHyperlink(hyperlinkRegion, info.resource,
info.function.getOffset())
};
}
}
return null;
}
/**
* Updates internal informations.
*
* @param file the editing file
*/
public void update(IFile file){
try {
functions.clear();
HTMLProjectParams params = new HTMLProjectParams(file.getProject());
String[] javaScripts = params.getJavaScripts();
IWorkspaceRoot wsroot = ResourcesPlugin.getWorkspace().getRoot();
for(int i=0;i<javaScripts.length;i++){
InputStream in = null;
Object obj = null;
if(javaScripts[i].startsWith(JavaScriptLibraryTable.PREFIX)){
IResource resource = wsroot.findMember(javaScripts[i].substring(JavaScriptLibraryTable.PREFIX.length()));
if(resource!=null && resource instanceof IFile && resource.exists()){
in = ((IFile)resource).getContents();
obj = resource;
}
} else {
obj = new File(javaScripts[i]);
in = new FileInputStream(javaScripts[i]);
}
String source = new String(HTMLUtil.readStream(in));
JavaScriptModel model = new JavaScriptModel(source);
JavaScriptElement[] elements = model.getChildren();
for(int j=0;j<elements.length;j++){
if(elements[j] instanceof JavaScriptFunction){
FunctionInfo info = new FunctionInfo();
info.resource = obj;
info.function = (JavaScriptFunction)elements[j];
functions.add(info);
}
}
}
} catch(Exception ex){
HTMLPlugin.logException(ex);
}
}
/**
* An internal object to stores <code>JavaScriptFunction</code>
* and resource which defines them.
*/
private class FunctionInfo {
private Object resource;
private JavaScriptFunction function;
}
/**
* <code>IHyperlink</code> implementation to jump the target resource.
*/
private class JavaScriptHyperlink implements IHyperlink {
private IRegion region;
private Object resource;
private int beginOffset;
/**
* The constructor.
*
* @param region the hyperlink region
* @param resource the target resource
* (<code>IFile</code>, <code>ITextViewer</code> or <code>java.io.File</code>)
* @param beginOffset the begin offset
*/
public JavaScriptHyperlink(IRegion region, Object resource, int beginOffset){
this.region = region;
this.resource = resource;
this.beginOffset = beginOffset;
}
public IRegion getHyperlinkRegion() {
return region;
}
public String getTypeLabel() {
return null;
}
public String getHyperlinkText() {
return null;
}
/**
* TODO <code>java.io.File</code> is unsupported.
*/
public void open() {
// IFile
if(resource instanceof IFile){
IWorkbenchPage page = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage();
try {
IEditorPart editor = IDE.openEditor(page, (IFile)resource, true);
IGotoMarker gotoMarker = (IGotoMarker)editor.getAdapter(IGotoMarker.class);
if(gotoMarker!=null){
IMarker marker= ((IFile)resource).createMarker(IMarker.TEXT);
marker.setAttribute(IMarker.CHAR_START, beginOffset);
marker.setAttribute(IMarker.CHAR_END, beginOffset);
gotoMarker.gotoMarker(marker);
}
} catch (Exception ex) {
HTMLPlugin.logException(ex);
}
}
if(resource instanceof ITextViewer){
((ITextViewer)resource).setSelectedRange(beginOffset, 0);
}
// java.io.File (external file)
if(resource instanceof File){
}
}
}
}