package tk.eclipse.plugin.htmleditor;
import java.util.ArrayList;
import java.util.List;
import jp.aonir.fuzzyxml.FuzzyXMLAttribute;
import jp.aonir.fuzzyxml.FuzzyXMLDocument;
import jp.aonir.fuzzyxml.FuzzyXMLElement;
import jp.aonir.fuzzyxml.FuzzyXMLParser;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.IPath;
import org.eclipse.jdt.core.IJavaElement;
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.Region;
import org.eclipse.jface.text.hyperlink.IHyperlink;
import org.eclipse.jface.text.hyperlink.IHyperlinkDetector;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IFileEditorInput;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.ide.IDE;
import tk.eclipse.plugin.htmleditor.editors.HTMLHyperlinkInfo;
import tk.eclipse.plugin.htmleditor.editors.HTMLSourceEditor;
/**
* The <code>IHyperlinkDetector</code> implementation for
* the <code>HTMLSourceEditor</code>.
* <p>
* This class detects the <strong>href</string> attribute
* as the hyperlink in default. And it's possible to add
* additional rules by <code>addHyperlinkProvider()</code>.
*
* @author Naoki Takezoe
* @see tk.eclipse.plugin.htmleditor.editors.HTMLSourceEditor
* @see tk.eclipse.plugin.htmleditor.IHyperlinkProvider
*/
public class HTMLHyperlinkDetector implements IHyperlinkDetector {
private HTMLSourceEditor editor;
private List<IHyperlinkProvider> providers = new ArrayList<IHyperlinkProvider>();
/**
* @param editor the <code>HTMLSourceEditor</code> instance
*/
public void setEditor(HTMLSourceEditor editor) {
this.editor = editor;
}
/**
* Adds the additional hyperlink provider.
*
* @param provider the additional hyperlink provider
*/
public void addHyperlinkProvider(IHyperlinkProvider provider) {
this.providers.add(provider);
}
/**
* Returns the <code>IProject</code> of the editing file.
* <p>
* If the editor input isn't <code>IFileEditorInput</code>,
* this method returns <code>null</code>.
*
* @return the <code>IProject</code>
*/
private IProject getProject() {
IEditorInput input = editor.getEditorInput();
if (input instanceof IFileEditorInput) {
return ((IFileEditorInput) input).getFile().getProject();
}
return null;
}
public IHyperlink[] detectHyperlinks(ITextViewer textViewer, IRegion region, boolean canShowMultipleHyperlinks) {
IHyperlink hyperlink = detectHyperlink(textViewer.getDocument(), region.getOffset());
if (hyperlink != null) {
return new IHyperlink[] { hyperlink };
}
return null;
}
private IHyperlink detectHyperlink(IDocument doc, int offset) {
FuzzyXMLDocument document = new FuzzyXMLParser(false).parse(editor.getHTMLSource());
FuzzyXMLElement element = document.getElementByOffset(offset);
if (element == null) {
return null;
}
FuzzyXMLAttribute[] attrs = element.getAttributes();
HTMLHyperlinkInfo info = getOpenFileInfo(document, element, null, null, offset);
if (info != null) {
return (IHyperlink) info.getObject();
}
else {
for (int i = 0; i < attrs.length; i++) {
if (attrs[i].getOffset() < offset && offset < attrs[i].getOffset() + attrs[i].getLength()) {
int attrOffset = getAttributeValueOffset(doc.get(), attrs[i]);
int attrLength = attrs[i].getValue().length();
if (attrOffset >= 0 && attrLength >= 0 && attrOffset <= offset) {
info = getOpenFileInfo(document, element, attrs[i].getName(), attrs[i].getValue(), offset - attrOffset);
IHyperlink hyperlink = null;
if (info != null && info.getObject() != null) {
if (info.getObject() instanceof IHyperlink) {
hyperlink = (IHyperlink) info.getObject();
}
else {
hyperlink = new HTMLHyperlink(new Region(attrOffset + info.getOffset(), info.getLength()), info.getObject());
}
}
return hyperlink;
}
}
}
}
return null;
}
/**
* Returns a target of hyperlink.
*/
private HTMLHyperlinkInfo getOpenFileInfo(FuzzyXMLDocument doc, FuzzyXMLElement element, String attrName, String attrValue, int offset) {
try {
IProject project = getProject();
if (project == null) {
return null;
}
IFile file = ((IFileEditorInput) editor.getEditorInput()).getFile();
for (int i = 0; i < providers.size(); i++) {
IHyperlinkProvider provider = this.providers.get(i);
HTMLHyperlinkInfo info = provider.getHyperlinkInfo(file, doc, element, attrName, attrValue, offset);
if (info != null && info.getObject() != null) {
return info;
}
}
if (attrName != null && attrName.equalsIgnoreCase("href")) {
String href = attrValue;
if (href.indexOf("#") > 0) {
href = href.substring(0, href.indexOf("#"));
}
IPath path = file.getParent().getProjectRelativePath();
IResource resource = project.findMember(path.append(href));
if (resource != null && resource.exists() && resource instanceof IFile) {
HTMLHyperlinkInfo info = new HTMLHyperlinkInfo();
info.setObject(resource);
info.setOffset(0);
info.setLength(attrValue.length());
return info;
}
}
}
catch (Exception ex) {
HTMLPlugin.logException(ex);
}
return null;
}
/**
* Returns an attribute value offset.
*
* @param source the source code
* @param attr the attribute
* @return the offset of the attribute
*/
private int getAttributeValueOffset(String source, FuzzyXMLAttribute attr) {
int offset = source.indexOf('=', attr.getOffset());
if (offset == -1) {
return -1;
}
char c = ' ';
while (c == ' ' || c == '\t' || c == '\r' || c == '\n' || c == '"' || c == '\'') {
offset++;
if (source.length() == offset + 1) {
break;
}
c = source.charAt(offset);
}
return offset;
}
private class HTMLHyperlink implements IHyperlink {
private IRegion region;
private Object openObject;
public HTMLHyperlink(IRegion region, Object openObject) {
this.region = region;
this.openObject = openObject;
}
public IRegion getHyperlinkRegion() {
return region;
}
public String getTypeLabel() {
return null;
}
public String getHyperlinkText() {
return null;
}
public void open() {
try {
if (openObject instanceof IFile) {
IWorkbenchWindow window = PlatformUI.getWorkbench().getActiveWorkbenchWindow();
IDE.openEditor(window.getActivePage(), (IFile) openObject, true);
}
else if (openObject instanceof IJavaElement) {
JavaUI.revealInEditor(JavaUI.openInEditor((IJavaElement) openObject), (IJavaElement) openObject);
}
}
catch (Exception ex) {
HTMLPlugin.logException(ex);
}
}
}
}