001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041:
042: package org.netbeans.modules.gsfret.hints;
043:
044: import java.io.IOException;
045: import java.util.ArrayList;
046: import java.util.Collections;
047: import java.util.HashMap;
048: import java.util.List;
049: import java.util.Map;
050: import javax.swing.text.Document;
051: import javax.swing.text.StyledDocument;
052: import org.netbeans.modules.gsf.api.CancellableTask;
053: import org.netbeans.modules.gsf.api.Error;
054: import org.netbeans.spi.editor.hints.Fix;
055: import org.netbeans.spi.editor.hints.LazyFixList;
056: import org.netbeans.spi.editor.hints.Severity;
057: import org.openide.ErrorManager;
058: import org.openide.filesystems.FileObject;
059: import org.openide.filesystems.FileUtil;
060: import org.openide.loaders.DataObject;
061: import org.openide.text.Line;
062: import java.util.EnumMap;
063: import java.util.Set;
064: import java.util.logging.Level;
065: import java.util.logging.Logger;
066: import javax.swing.text.BadLocationException;
067: import javax.swing.text.Position;
068: import javax.swing.text.Position.Bias;
069: import org.netbeans.modules.gsf.api.HintsProvider;
070: import org.netbeans.modules.gsf.api.ParserResult;
071: import org.netbeans.modules.gsf.LanguageRegistry;
072: import org.netbeans.napi.gsfret.source.CompilationInfo;
073: import org.netbeans.napi.gsfret.source.Source;
074: import org.netbeans.spi.editor.hints.ErrorDescription;
075: import org.netbeans.spi.editor.hints.ErrorDescriptionFactory;
076: import org.netbeans.spi.editor.hints.HintsController;
077: import org.openide.cookies.EditorCookie;
078: import org.openide.cookies.LineCookie;
079: import org.openide.text.NbDocument;
080:
081: /**
082: * This class is based on JavaHintsFactory in Retouche's org.netbeans.modules.java.hints
083: * This file is originally from Retouche, the Java Support
084: * infrastructure in NetBeans. I have modified the file as little
085: * as possible to make merging Retouche fixes back as simple as
086: * possible.
087: *
088: *
089: * @author Jan Lahoda
090: * @author leon chiver
091: * @author Tor Norbye
092: */
093: public final class GsfHintsProvider implements
094: CancellableTask<CompilationInfo> {
095:
096: public static ErrorManager ERR = ErrorManager.getDefault()
097: .getInstance("org.netbeans.modules.gsfret.hints"); // NOI18N
098: public static Logger LOG = Logger
099: .getLogger("org.netbeans.modules.gsfret.hints"); // NOI18N
100:
101: private FileObject file;
102:
103: /**
104: * Creates a new instance of GsfHintsProvider
105: */
106: GsfHintsProvider(FileObject file) {
107: this .file = file;
108: }
109:
110: private static final Map<org.netbeans.modules.gsf.api.Severity, Severity> errorKind2Severity;
111:
112: static {
113: errorKind2Severity = new EnumMap<org.netbeans.modules.gsf.api.Severity, Severity>(
114: org.netbeans.modules.gsf.api.Severity.class);
115: errorKind2Severity.put(
116: org.netbeans.modules.gsf.api.Severity.ERROR,
117: Severity.ERROR);
118: errorKind2Severity.put(
119: org.netbeans.modules.gsf.api.Severity.WARNING,
120: Severity.WARNING);
121: // errorKind2Severity.put(Error/*Diagnostic*/.Kind.WARNING, Severity.WARNING);
122: // errorKind2Severity.put(Error/*Diagnostic*/.Kind.NOTE, Severity.WARNING);
123: // errorKind2Severity.put(Error/*Diagnostic*/.Kind.OTHER, Severity.WARNING);
124: }
125:
126: List<ErrorDescription> computeErrors(CompilationInfo info,
127: Document doc, ParserResult result, List<Error> errors,
128: List<ErrorDescription> descs) {
129: Source js = Source.forFileObject(file);
130:
131: if (ERR.isLoggable(ErrorManager.INFORMATIONAL)) {
132: ERR.log(ErrorManager.INFORMATIONAL, "errors = " + errors);
133: }
134:
135: for (Error/*Diagnostic*/d : errors) {
136: if (isCanceled()) {
137: return null;
138: }
139:
140: if (ERR.isLoggable(ErrorManager.INFORMATIONAL)) {
141: ERR.log(ErrorManager.INFORMATIONAL, "d = " + d);
142:
143: //Map<String, List<ErrorRule>> code2Rules = RulesManager.getInstance().getErrors();
144: }
145:
146: //Map<String, List<ErrorRule>> code2Rules = RulesManager.getInstance().getErrors();
147:
148: //List<ErrorRule> rules = code2Rules.get(d.getKey());
149:
150: //if (ERR.isLoggable(ErrorManager.INFORMATIONAL)) {
151: //ERR.log(ErrorManager.INFORMATIONAL, "code= " + d.getKey());
152: //ERR.log(ErrorManager.INFORMATIONAL, "rules = " + rules);
153: //}
154:
155: //int position = (int)d.getPosition();
156: int astOffset = d.getStartPosition();
157: int astEndOffset = d.getEndPosition();
158:
159: int position, endPosition;
160: if (result.getTranslatedSource() != null) {
161: position = result.getTranslatedSource()
162: .getLexicalOffset(astOffset);
163: if (position == -1) {
164: continue;
165: }
166: endPosition = position + (astEndOffset - astOffset);
167: } else {
168: position = astOffset;
169: endPosition = astEndOffset;
170: }
171:
172: LazyFixList ehm;
173:
174: //if (rules != null) {
175: // ehm = new CreatorBasedLazyFixList(info.getFileObject(), d.getKey(), (int)getPrefferedPosition(info, d), rules, data);
176: //} else {
177: ehm = ErrorDescriptionFactory.lazyListForFixes(Collections
178: .<Fix> emptyList());
179: //}
180:
181: if (ERR.isLoggable(ErrorManager.INFORMATIONAL)) {
182: ERR.log(ErrorManager.INFORMATIONAL, "ehm=" + ehm);
183: }
184:
185: final String desc = d.getDisplayName();
186: final Position[] range = getLine(info, d, doc, position,
187: endPosition);
188:
189: if (isCanceled()) {
190: return null;
191: }
192:
193: if (range[0] == null || range[1] == null) {
194: continue;
195: }
196:
197: descs.add(ErrorDescriptionFactory.createErrorDescription(
198: errorKind2Severity.get(d.getSeverity()), desc, ehm,
199: doc, range[0], range[1]));
200: }
201:
202: if (isCanceled()) {
203: return null;
204: }
205:
206: return descs;
207: }
208:
209: public Document getDocument() {
210: try {
211: DataObject d = DataObject.find(file);
212: EditorCookie ec = d.getCookie(EditorCookie.class);
213:
214: if (ec == null) {
215: return null;
216: }
217:
218: return ec.getDocument();
219: } catch (IOException e) {
220: Logger.getLogger(GsfHintsProvider.class.getName()).log(
221: Level.INFO,
222: "GsfHintsProvider: Cannot find DataObject for file: "
223: + FileUtil.getFileDisplayName(file), e);
224: return null;
225: }
226: }
227:
228: private Position[] getLine(CompilationInfo info, Error d,
229: final Document doc, int startOffset, int endOffset) {
230: StyledDocument sdoc = (StyledDocument) doc;
231: DataObject dObj = (DataObject) doc
232: .getProperty(doc.StreamDescriptionProperty);
233: LineCookie lc = dObj.getCookie(LineCookie.class);
234: int lineNumber = NbDocument.findLineNumber(sdoc, startOffset);
235: int lineOffset = NbDocument.findLineOffset(sdoc, lineNumber);
236: Line line = lc.getLineSet().getCurrent(lineNumber);
237:
238: boolean rangePrepared = false;
239:
240: if (!rangePrepared) {
241: String text = line.getText();
242:
243: int column = 0;
244: int length = text.length();
245:
246: while (column < text.length()
247: && Character.isWhitespace(text.charAt(column))) {
248: column++;
249: }
250:
251: while (length > 0
252: && Character.isWhitespace(text.charAt(length - 1))) {
253: length--;
254: }
255:
256: startOffset = lineOffset + column;
257: endOffset = lineOffset + length;
258: if (startOffset > endOffset) {
259: // Space only on the line
260: startOffset = lineOffset;
261: }
262: }
263:
264: if (ERR.isLoggable(ErrorManager.INFORMATIONAL)) {
265: ERR.log(ErrorManager.INFORMATIONAL, "startOffset = "
266: + startOffset);
267: ERR.log(ErrorManager.INFORMATIONAL, "endOffset = "
268: + endOffset);
269: }
270:
271: final int startOffsetFinal = startOffset;
272: final int endOffsetFinal = endOffset;
273: final Position[] result = new Position[2];
274:
275: doc.render(new Runnable() {
276: public void run() {
277: if (isCanceled()) {
278: return;
279: }
280:
281: int len = doc.getLength();
282:
283: if (startOffsetFinal > len || endOffsetFinal > len) {
284: if (!isCanceled()
285: && ERR.isLoggable(ErrorManager.WARNING)) {
286: ERR.log(ErrorManager.WARNING,
287: "document changed, but not canceled?");
288: ERR.log(ErrorManager.WARNING, "len = " + len);
289: ERR.log(ErrorManager.WARNING, "startOffset = "
290: + startOffsetFinal);
291: ERR.log(ErrorManager.WARNING, "endOffset = "
292: + endOffsetFinal);
293: }
294: cancel();
295:
296: return;
297: }
298:
299: try {
300: result[0] = NbDocument.createPosition(doc,
301: startOffsetFinal, Bias.Forward);
302: result[1] = NbDocument.createPosition(doc,
303: endOffsetFinal, Bias.Backward);
304: } catch (BadLocationException e) {
305: ERR.notify(ErrorManager.ERROR, e);
306: }
307: }
308: });
309:
310: return result;
311: }
312:
313: private boolean cancel;
314:
315: synchronized boolean isCanceled() {
316: return cancel;
317: }
318:
319: public synchronized void cancel() {
320: cancel = true;
321: }
322:
323: synchronized void resume() {
324: cancel = false;
325: }
326:
327: public void run(CompilationInfo info) {
328: resume();
329:
330: Document doc = getDocument();
331:
332: if (doc == null) {
333: Logger.getLogger(GsfHintsProvider.class.getName()).log(
334: Level.INFO,
335: "SemanticHighlighter: Cannot get document!");
336: return;
337: }
338:
339: long start = System.currentTimeMillis();
340:
341: Set<String> mimeTypes = info.getEmbeddedMimeTypes();
342: LanguageRegistry registry = LanguageRegistry.getInstance();
343: List<ErrorDescription> descriptions = new ArrayList<ErrorDescription>();
344:
345: for (String mimeType : mimeTypes) {
346: for (ParserResult result : info
347: .getEmbeddedResults(mimeType)) {
348: assert result != null;
349:
350: HintsProvider provider = registry
351: .getLanguageByMimeType(mimeType)
352: .getHintsProvider();
353: List<Error> errors = result.getDiagnostics();
354: List<ErrorDescription> desc = new ArrayList<ErrorDescription>();
355: if (provider != null) {
356: errors = provider.computeErrors(info, desc);
357: }
358: // Process errors without codes
359: desc = computeErrors(info, doc, result, errors, desc);
360: if (desc == null) {
361: //meaning: cancelled
362: return;
363: }
364:
365: descriptions.addAll(desc);
366: }
367: }
368: HintsController.setErrors(doc, "gsf-hints", descriptions);
369:
370: long end = System.currentTimeMillis();
371:
372: //TimesCollector.getDefault().reportTime(info.getFileObject(), "com-hints", "Hints", end - start);
373: }
374: }
|