001: /*******************************************************************************
002: * Copyright (c) 2006, 2007 IBM Corporation and others.
003: * All rights reserved. This program and the accompanying materials
004: * are made available under the terms of the Eclipse Public License v1.0
005: * which accompanies this distribution, and is available at
006: * http://www.eclipse.org/legal/epl-v10.html
007: *
008: * Contributors:
009: * IBM Corporation - initial API and implementation
010: *******************************************************************************/package org.eclipse.pde.internal.ui.editor.plugin;
011:
012: import org.eclipse.jface.text.BadLocationException;
013: import org.eclipse.jface.text.IDocument;
014: import org.eclipse.jface.text.IRegion;
015: import org.eclipse.jface.text.ITextViewer;
016: import org.eclipse.jface.text.Region;
017: import org.eclipse.jface.text.hyperlink.IHyperlink;
018: import org.eclipse.jface.text.hyperlink.IHyperlinkDetector;
019: import org.eclipse.pde.internal.core.ICoreConstants;
020: import org.eclipse.pde.internal.core.text.IDocumentRange;
021: import org.eclipse.pde.internal.core.text.IEditingModel;
022: import org.eclipse.pde.internal.core.text.bundle.BasePackageHeader;
023: import org.eclipse.pde.internal.core.text.bundle.BundleActivatorHeader;
024: import org.eclipse.pde.internal.core.text.bundle.ManifestHeader;
025: import org.eclipse.pde.internal.core.text.bundle.RequireBundleHeader;
026: import org.eclipse.pde.internal.ui.editor.PDESourcePage;
027: import org.eclipse.pde.internal.ui.editor.text.BundleHyperlink;
028: import org.eclipse.pde.internal.ui.editor.text.JavaHyperlink;
029: import org.eclipse.pde.internal.ui.editor.text.PackageHyperlink;
030: import org.eclipse.pde.internal.ui.editor.text.TranslationHyperlink;
031:
032: public class BundleHyperlinkDetector implements IHyperlinkDetector {
033:
034: private PDESourcePage fSourcePage;
035:
036: /**
037: * @param editor the editor in which to detect the hyperlink
038: */
039: public BundleHyperlinkDetector(PDESourcePage page) {
040: fSourcePage = page;
041: }
042:
043: /*
044: * @see org.eclipse.jface.text.hyperlink.IHyperlinkDetector#detectHyperlinks(org.eclipse.jface.text.ITextViewer, org.eclipse.jface.text.IRegion, boolean)
045: */
046: public IHyperlink[] detectHyperlinks(ITextViewer textViewer,
047: IRegion region, boolean canShowMultipleHyperlinks) {
048: if (region == null || canShowMultipleHyperlinks)
049: return null;
050:
051: IDocumentRange element = fSourcePage.getRangeElement(region
052: .getOffset(), false);
053: if (!(element instanceof ManifestHeader))
054: return null;
055:
056: ManifestHeader header = (ManifestHeader) element;
057: if (!header.getModel().isEditable())
058: return null;
059:
060: String headerName = header.getName();
061: if (region.getOffset() <= header.getOffset()
062: + headerName.length())
063: return null;
064:
065: String[] translatable = ICoreConstants.TRANSLATABLE_HEADERS;
066: for (int i = 0; i < translatable.length; i++) {
067: if (!headerName.equals(translatable[i]))
068: continue;
069: String value = header.getValue();
070: if (value == null || value.length() == 0
071: || value.charAt(0) != '%')
072: break;
073:
074: IDocumentRange range = BundleSourcePage.getSpecificRange(
075: header.getModel(), header, value);
076: return new IHyperlink[] { new TranslationHyperlink(
077: new Region(range.getOffset(), range.getLength()),
078: value, header.getModel()) };
079: }
080:
081: if (header instanceof BundleActivatorHeader) { // add else if statments for other headers
082: String target = ((BundleActivatorHeader) element)
083: .getClassName();
084: if (target == null || target.length() == 0)
085: return null;
086: IDocumentRange range = BundleSourcePage.getSpecificRange(
087: header.getModel(), header, target);
088: if (range == null)
089: return null;
090: return new IHyperlink[] { new JavaHyperlink(new Region(
091: range.getOffset(), range.getLength()), target,
092: header.getModel().getUnderlyingResource()) };
093: } else if (header instanceof BasePackageHeader
094: || header instanceof RequireBundleHeader) {
095: return matchLinkFor(header, region.getOffset());
096: }
097: return null;
098: }
099:
100: private IHyperlink[] matchLinkFor(ManifestHeader header,
101: int mainOffset) {
102: IDocument doc = ((IEditingModel) header.getModel())
103: .getDocument();
104: String value;
105: try {
106: value = doc.get(header.getOffset(), header.getLength());
107: int offset = mainOffset - header.getOffset();
108: // Our offset falls outside the value range
109: if (offset >= value.length()) {
110: return null;
111: }
112:
113: // ensure we are over a letter or period
114: char c = value.charAt(offset);
115: if (!elementChar(c, true))
116: return null;
117:
118: // scan backwards to find the start of the word
119: int downOffset = offset;
120: c = value.charAt(--downOffset);
121: while (c != ',' && c != ':' && downOffset > 0) {
122: // c == ';' means we are at a directive / attribute name (NOT value)
123: if (c == ';' || !elementChar(c, false))
124: return null;
125: downOffset -= 1;
126: c = value.charAt(downOffset);
127: }
128: // backtrack forwards to remove whitespace etc.
129: while (downOffset < offset && !elementChar(c, true))
130: c = value.charAt(++downOffset);
131:
132: // scan forwards to find the end of the word
133: int upOffset = offset;
134: c = value.charAt(upOffset);
135: int length = value.length();
136: while (c != ';' && c != ',' && upOffset < length - 1) {
137: if (!elementChar(c, false))
138: return null;
139: upOffset += 1;
140: c = value.charAt(upOffset);
141: }
142: // backtrack to remove extra chars
143: if (c == ';' || c == ',')
144: upOffset -= 1;
145:
146: String match = value.substring(downOffset, upOffset + 1);
147: if (match.length() > 0) {
148: IRegion region = new Region(mainOffset
149: - (offset - downOffset), match.length());
150: if (header instanceof BasePackageHeader)
151: return new IHyperlink[] { new PackageHyperlink(
152: region, match, (BasePackageHeader) header) };
153: if (header instanceof RequireBundleHeader)
154: return new IHyperlink[] { new BundleHyperlink(
155: region, match) };
156: }
157: } catch (BadLocationException e) {
158: }
159: return null;
160: }
161:
162: private boolean elementChar(char c, boolean noWhitespace) {
163: if (noWhitespace && Character.isWhitespace(c))
164: return false;
165: return ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z')
166: || c == '.' || Character.isWhitespace(c);
167: }
168:
169: }
|