001: /*******************************************************************************
002: * Copyright (c) 2000, 2006 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.jdt.internal.ui.text.javadoc;
011:
012: import java.util.ArrayList;
013: import java.util.Collections;
014: import java.util.List;
015:
016: import org.eclipse.core.runtime.IProgressMonitor;
017:
018: import org.eclipse.core.resources.IFile;
019:
020: import org.eclipse.swt.graphics.Image;
021:
022: import org.eclipse.jface.text.BadLocationException;
023: import org.eclipse.jface.text.IDocument;
024: import org.eclipse.jface.text.IRegion;
025:
026: import org.eclipse.ui.IEditorInput;
027: import org.eclipse.ui.part.FileEditorInput;
028:
029: import org.eclipse.jdt.core.ICompilationUnit;
030: import org.eclipse.jdt.core.JavaModelException;
031:
032: import org.eclipse.jdt.ui.JavaUI;
033: import org.eclipse.jdt.ui.text.java.ContentAssistInvocationContext;
034: import org.eclipse.jdt.ui.text.java.IJavaCompletionProposalComputer;
035: import org.eclipse.jdt.ui.text.java.IJavadocCompletionProcessor;
036:
037: import org.eclipse.jdt.internal.ui.JavaPluginImages;
038: import org.eclipse.jdt.internal.ui.text.java.JavaCompletionProposal;
039:
040: /**
041: * @since 3.2 (renamed from JavaDocCompletionEvaluator which got introduced in 2.0)
042: */
043: public class HTMLTagCompletionProposalComputer implements
044: IJavaCompletionProposalComputer {
045:
046: private static final String[] fgHTMLProposals = new String[IHtmlTagConstants.HTML_GENERAL_TAGS.length * 2];
047: {
048: String tag = null;
049:
050: int index = 0;
051: int offset = 0;
052:
053: while (index < fgHTMLProposals.length) {
054:
055: tag = IHtmlTagConstants.HTML_GENERAL_TAGS[offset];
056: fgHTMLProposals[index++] = IHtmlTagConstants.HTML_TAG_PREFIX
057: + tag + IHtmlTagConstants.HTML_TAG_POSTFIX;
058: fgHTMLProposals[index++] = IHtmlTagConstants.HTML_CLOSE_PREFIX
059: + tag + IHtmlTagConstants.HTML_TAG_POSTFIX;
060: offset++;
061: }
062: }
063:
064: private IDocument fDocument;
065: private int fCurrentPos;
066: private int fCurrentLength;
067: private String fErrorMessage;
068: private List fResult;
069:
070: private boolean fRestrictToMatchingCase;
071:
072: public HTMLTagCompletionProposalComputer() {
073: }
074:
075: private static boolean isWordPart(char ch) {
076: return Character.isJavaIdentifierPart(ch) || (ch == '#')
077: || (ch == '.') || (ch == '/');
078: }
079:
080: private static int findCharBeforeWord(IDocument doc,
081: int lineBeginPos, int pos) {
082: int currPos = pos - 1;
083: if (currPos > lineBeginPos) {
084: try {
085: while (currPos > lineBeginPos
086: && isWordPart(doc.getChar(currPos))) {
087: currPos--;
088: }
089: return currPos;
090: } catch (BadLocationException e) {
091: // ignore
092: }
093: }
094: return pos;
095: }
096:
097: private static int findClosingCharacter(IDocument doc, int pos,
098: int end, char endChar) throws BadLocationException {
099: int curr = pos;
100: while (curr < end && (doc.getChar(curr) != endChar)) {
101: curr++;
102: }
103: if (curr < end) {
104: return curr + 1;
105: }
106: return pos;
107: }
108:
109: private static int findReplaceEndPos(IDocument doc, String newText,
110: String oldText, int pos) {
111: if (oldText.length() == 0 || oldText.equals(newText)) {
112: return pos;
113: }
114:
115: try {
116: IRegion lineInfo = doc.getLineInformationOfOffset(pos);
117: int end = lineInfo.getOffset() + lineInfo.getLength();
118:
119: // for html, search the tag end character
120: return findClosingCharacter(doc, pos, end, '>');
121: } catch (BadLocationException e) {
122: // ignore
123: }
124: return pos;
125: }
126:
127: /*
128: * @see org.eclipse.jdt.ui.text.java.IJavaCompletionProposalComputer#computeCompletionProposals(org.eclipse.jdt.ui.text.java.ContentAssistInvocationContext, org.eclipse.core.runtime.IProgressMonitor)
129: * @since 3.2
130: */
131: public List computeCompletionProposals(
132: ContentAssistInvocationContext context,
133: IProgressMonitor monitor) {
134: if (!(context instanceof JavadocContentAssistInvocationContext))
135: return Collections.EMPTY_LIST;
136:
137: JavadocContentAssistInvocationContext docContext = (JavadocContentAssistInvocationContext) context;
138: int flags = docContext.getFlags();
139: fCurrentPos = docContext.getInvocationOffset();
140: fCurrentLength = docContext.getSelectionLength();
141: fRestrictToMatchingCase = (flags & IJavadocCompletionProcessor.RESTRICT_TO_MATCHING_CASE) != 0;
142:
143: ICompilationUnit cu = docContext.getCompilationUnit();
144: if (cu == null)
145: return Collections.EMPTY_LIST;
146: IEditorInput editorInput = new FileEditorInput((IFile) cu
147: .getResource());
148: fDocument = JavaUI.getDocumentProvider().getDocument(
149: editorInput);
150: if (fDocument == null) {
151: return null;
152: }
153:
154: try {
155: fResult = new ArrayList(100);
156: evalProposals();
157: return fResult;
158: } catch (JavaModelException e) {
159: fErrorMessage = e.getLocalizedMessage();
160: } finally {
161: fResult = null;
162: }
163: return null;
164: }
165:
166: private void evalProposals() throws JavaModelException {
167: try {
168:
169: IRegion info = fDocument
170: .getLineInformationOfOffset(fCurrentPos);
171: int lineBeginPos = info.getOffset();
172:
173: int word1Begin = findCharBeforeWord(fDocument,
174: lineBeginPos, fCurrentPos);
175: if (word1Begin == fCurrentPos)
176: return;
177:
178: char firstChar = fDocument.getChar(word1Begin);
179: if (firstChar == '<') {
180: String prefix = fDocument.get(word1Begin, fCurrentPos
181: - word1Begin);
182: addProposals(prefix, fgHTMLProposals,
183: JavaPluginImages.IMG_OBJS_HTMLTAG);
184: return;
185: } else if (!Character.isWhitespace(firstChar)) {
186: return;
187: }
188:
189: // TODO really show all tags when there is no prefix?
190: // TODO find any unclosed open tag and offer the corresponding close tag
191: String prefix = fDocument.get(word1Begin + 1, fCurrentPos
192: - word1Begin - 1);
193: addAllTags(prefix);
194: } catch (BadLocationException e) {
195: // ignore
196: }
197: }
198:
199: private boolean prefixMatches(String prefix, String proposal) {
200: if (fRestrictToMatchingCase) {
201: return proposal.startsWith(prefix);
202: } else if (proposal.length() >= prefix.length()) {
203: return prefix.equalsIgnoreCase(proposal.substring(0, prefix
204: .length()));
205: }
206: return false;
207: }
208:
209: private void addAllTags(String prefix) {
210: String htmlPrefix = "<" + prefix; //$NON-NLS-1$
211: for (int i = 0; i < fgHTMLProposals.length; i++) {
212: String curr = fgHTMLProposals[i];
213: if (prefixMatches(htmlPrefix, curr)) {
214: fResult
215: .add(createCompletion(
216: curr,
217: prefix,
218: curr,
219: JavaPluginImages
220: .get(JavaPluginImages.IMG_OBJS_HTMLTAG),
221: 0));
222: }
223: }
224: }
225:
226: private void addProposals(String prefix, String[] choices,
227: String imageName) {
228: for (int i = 0; i < choices.length; i++) {
229: String curr = choices[i];
230: if (prefixMatches(prefix, curr)) {
231: fResult.add(createCompletion(curr, prefix, curr,
232: JavaPluginImages.get(imageName), 0));
233: }
234: }
235: }
236:
237: private JavaCompletionProposal createCompletion(String newText,
238: String oldText, String labelText, Image image, int severity) {
239: int offset = fCurrentPos - oldText.length();
240: int length = fCurrentLength + oldText.length();
241: if (fCurrentLength == 0)
242: length = findReplaceEndPos(fDocument, newText, oldText,
243: fCurrentPos)
244: - offset;
245:
246: // bump opening over closing tags
247: if (!newText.startsWith(IHtmlTagConstants.HTML_CLOSE_PREFIX))
248: severity++;
249: JavaCompletionProposal proposal = new JavaCompletionProposal(
250: newText, offset, length, image, labelText, severity,
251: true);
252: proposal.setTriggerCharacters(new char[] { '>' });
253: return proposal;
254: }
255:
256: /*
257: * @see org.eclipse.jdt.ui.text.java.IJavaCompletionProposalComputer#computeContextInformation(org.eclipse.jdt.ui.text.java.ContentAssistInvocationContext, org.eclipse.core.runtime.IProgressMonitor)
258: * @since 3.2
259: */
260: public List computeContextInformation(
261: ContentAssistInvocationContext context,
262: IProgressMonitor monitor) {
263: return Collections.EMPTY_LIST;
264: }
265:
266: /*
267: * @see org.eclipse.jdt.ui.text.java.IJavaCompletionProposalComputer#getErrorMessage()
268: * @since 3.2
269: */
270: public String getErrorMessage() {
271: return fErrorMessage;
272: }
273:
274: /*
275: * @see org.eclipse.jdt.ui.text.java.IJavaCompletionProposalComputer#sessionEnded()
276: * @since 3.2
277: */
278: public void sessionEnded() {
279: fErrorMessage = null;
280: }
281:
282: /*
283: * @see org.eclipse.jdt.ui.text.java.IJavaCompletionProposalComputer#sessionStarted()
284: * @since 3.2
285: */
286: public void sessionStarted() {
287: fErrorMessage = null;
288: }
289: }
|