/******************************************************************************* * Copyright (c) 2007, 2009 David Green 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: * David Green - initial API and implementation *******************************************************************************/ package org.eclipse.mylyn.internal.wikitext.ui.viewer; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.Iterator; import java.util.List; import org.eclipse.jface.text.IRegion; import org.eclipse.jface.text.ITextViewer; import org.eclipse.jface.text.Position; import org.eclipse.jface.text.Region; import org.eclipse.jface.text.hyperlink.IHyperlink; import org.eclipse.jface.text.hyperlink.IHyperlinkDetector; import org.eclipse.jface.text.hyperlink.URLHyperlink; import org.eclipse.jface.text.source.Annotation; import org.eclipse.jface.text.source.IAnnotationModel; import org.eclipse.jface.text.source.ISourceViewer; import org.eclipse.mylyn.wikitext.ui.annotation.AnchorHrefAnnotation; import org.eclipse.mylyn.wikitext.ui.annotation.AnchorNameAnnotation; import org.eclipse.mylyn.wikitext.ui.annotation.IdAnnotation; public class AnnotationHyperlinkDetector implements IHyperlinkDetector { public IHyperlink[] detectHyperlinks(ITextViewer textViewer, IRegion region, boolean canShowMultipleHyperlinks) { if (textViewer instanceof ISourceViewer) { // Note: only find hyperlinks that include the region end point, otherwise we may get hyperlinks that // are not even relevant. int interestingOffset = region.getOffset() + region.getLength(); ISourceViewer sourceViewer = (ISourceViewer) textViewer; IAnnotationModel annotationModel = sourceViewer.getAnnotationModel(); if (annotationModel != null) { List<AnchorHrefAnnotation> hrefs = null; Iterator<?> iterator = annotationModel.getAnnotationIterator(); while (iterator.hasNext()) { Annotation annotation = (Annotation) iterator.next(); if (annotation instanceof AnchorHrefAnnotation) { AnchorHrefAnnotation href = (AnchorHrefAnnotation) annotation; Position position = annotationModel.getPosition(href); if (position.getOffset() <= interestingOffset && position.getOffset() + position.getLength() >= interestingOffset) { if (hrefs == null) { hrefs = new ArrayList<>(); } hrefs.add(href); } } } if (hrefs != null) { if (hrefs.size() > 1) { // put greatest offset annotations first. Collections.sort(hrefs, new OffsetComparator(annotationModel)); } return new IHyperlink[] { createHyperlink(sourceViewer, annotationModel, hrefs.get(0)) }; } } } return null; } protected IHyperlink createHyperlink(ISourceViewer viewer, IAnnotationModel annotationModel, AnchorHrefAnnotation anchorHrefAnnotation) { Position position = annotationModel.getPosition(anchorHrefAnnotation); IRegion region = new Region(position.getOffset(), position.getLength()); String href = anchorHrefAnnotation.getAnchorHref(); if (href != null && href.startsWith("#")) { //$NON-NLS-1$ return new DocumentHyperlink(viewer, region, href); } else { return createUrlHyperlink(region, href); } } protected IHyperlink createUrlHyperlink(IRegion region, String href) { return new URLHyperlink(region, href); } /** * a comparator that puts greatest offset annotations first */ private static class OffsetComparator implements Comparator<Annotation> { private final IAnnotationModel annotationModel; public OffsetComparator(IAnnotationModel annotationModel) { this.annotationModel = annotationModel; } public int compare(Annotation o1, Annotation o2) { if (o1 == o2) { return 0; } Position p1 = annotationModel.getPosition(o1); Position p2 = annotationModel.getPosition(o2); if (p1.getOffset() > p2.getOffset()) { return -1; } else if (p2.getOffset() > p1.getOffset()) { return 1; } else { if (p1.getLength() > p2.getLength()) { return -1; } else if (p2.getLength() > p1.getLength()) { return 1; } return Integer.valueOf(System.identityHashCode(p1)).compareTo(System.identityHashCode(p2)); } } } /** * A hyperlink implementation that causes the viewer's selection (and scrolling) to adjust to the hyperlink target. * * @author David Green */ protected static class DocumentHyperlink implements IHyperlink { private final ISourceViewer viewer; private final IRegion region; private final String href; public DocumentHyperlink(ISourceViewer viewer, IRegion region, String href) { this.viewer = viewer; this.region = region; this.href = href; } public IRegion getHyperlinkRegion() { return region; } public String getHyperlinkText() { return null; } public String getTypeLabel() { return null; } public void open() { String lookingFor = href.substring(1); // lose the leading '#' IAnnotationModel annotationModel = viewer.getAnnotationModel(); Iterator<?> iterator = annotationModel.getAnnotationIterator(); while (iterator.hasNext()) { Annotation annotation = (Annotation) iterator.next(); if (annotation instanceof IdAnnotation) { IdAnnotation idAnnotation = (IdAnnotation) annotation; if (!idAnnotation.getElementId().equals(lookingFor)) { continue; } } else if (annotation instanceof AnchorNameAnnotation) { AnchorNameAnnotation nameAnnotation = (AnchorNameAnnotation) annotation; if (!nameAnnotation.getAnchorName().equals(lookingFor)) { continue; } } else { continue; } Position position = annotationModel.getPosition(annotation); viewer.getTextWidget().setSelection(position.getOffset()); break; } } } }