1 19 20 package org.netbeans.modules.java.hints; 21 22 import java.io.IOException ; 23 import java.util.ArrayList ; 24 import java.util.Arrays ; 25 import java.util.Collections ; 26 import java.util.HashMap ; 27 import java.util.List ; 28 import java.util.Map ; 29 import javax.lang.model.element.PackageElement; 30 import javax.lang.model.type.TypeKind; 31 import javax.swing.text.Document ; 32 import javax.swing.text.StyledDocument ; 33 import javax.tools.Diagnostic; 34 import org.netbeans.api.java.source.CancellableTask; 35 import org.netbeans.api.timers.TimesCollector; 36 import org.netbeans.spi.editor.hints.Fix; 37 import org.netbeans.spi.editor.hints.LazyFixList; 38 import org.netbeans.spi.editor.hints.Severity; 39 import org.openide.ErrorManager; 40 import org.openide.filesystems.FileObject; 41 import org.openide.filesystems.FileUtil; 42 import org.openide.loaders.DataObject; 43 import org.openide.text.Line; 44 import com.sun.source.util.TreePath; 45 import java.util.EnumMap ; 46 import java.util.HashSet ; 47 import java.util.Set ; 48 import java.util.logging.Level ; 49 import java.util.logging.Logger ; 50 import javax.lang.model.element.Element; 51 import javax.swing.text.BadLocationException ; 52 import javax.swing.text.Position ; 53 import javax.swing.text.Position.Bias; 54 import org.netbeans.api.java.lexer.JavaTokenId; 55 import org.netbeans.api.java.source.CompilationInfo; 56 import org.netbeans.api.java.source.JavaSource; 57 import org.netbeans.api.lexer.Token; 58 import org.netbeans.api.lexer.TokenHierarchy; 59 import org.netbeans.api.lexer.TokenSequence; 60 import org.netbeans.modules.java.hints.CreatorBasedLazyFixList; 61 import org.netbeans.modules.java.hints.ImportClassEnabler.ImportCandidatesHolder; 62 import org.netbeans.modules.java.hints.LazyHintComputationFactory; 63 import org.netbeans.modules.java.hints.infrastructure.RulesManager; 64 import org.netbeans.modules.java.hints.spi.ErrorRule; 65 import org.netbeans.modules.java.hints.spi.ErrorRule.Data; 66 import org.netbeans.spi.editor.hints.ErrorDescription; 67 import org.netbeans.spi.editor.hints.ErrorDescriptionFactory; 68 import org.netbeans.spi.editor.hints.HintsController; 69 import org.openide.cookies.EditorCookie; 70 import org.openide.cookies.LineCookie; 71 import org.openide.text.NbDocument; 72 73 74 75 79 public final class JavaHintsProvider implements CancellableTask<CompilationInfo> { 80 81 public static ErrorManager ERR = ErrorManager.getDefault().getInstance("org.netbeans.modules.java.hints"); public static Logger LOG = Logger.getLogger("org.netbeans.modules.java.hints"); 84 private FileObject file; 85 86 87 JavaHintsProvider(FileObject file) { 88 this.file = file; 89 } 90 91 private static final Map <Diagnostic.Kind, Severity> errorKind2Severity; 92 93 static { 94 errorKind2Severity = new EnumMap <Diagnostic.Kind, Severity>(Diagnostic.Kind.class); 95 errorKind2Severity.put(Diagnostic.Kind.ERROR, Severity.ERROR); 96 errorKind2Severity.put(Diagnostic.Kind.MANDATORY_WARNING, Severity.WARNING); 97 errorKind2Severity.put(Diagnostic.Kind.WARNING, Severity.WARNING); 98 errorKind2Severity.put(Diagnostic.Kind.NOTE, Severity.WARNING); 99 errorKind2Severity.put(Diagnostic.Kind.OTHER, Severity.WARNING); 100 } 101 102 List <ErrorDescription> computeErrors(CompilationInfo info, Document doc) { 103 JavaSource js = JavaSource.forFileObject(file); 104 List <Diagnostic> errors = info.getDiagnostics(); 105 List <ErrorDescription> descs = new ArrayList <ErrorDescription>(); 106 107 if (ERR.isLoggable(ErrorManager.INFORMATIONAL)) 108 ERR.log(ErrorManager.INFORMATIONAL, "errors = " + errors ); 109 110 Map <Class , Data> data = new HashMap <Class , Data>(); 111 112 for (Diagnostic d : errors) { 113 if (isCanceled()) 114 return null; 115 116 if (ERR.isLoggable(ErrorManager.INFORMATIONAL)) 117 ERR.log(ErrorManager.INFORMATIONAL, "d = " + d ); 118 119 Map <String , List <ErrorRule>> code2Rules = RulesManager.getInstance().getErrors(); 120 121 List <ErrorRule> rules = code2Rules.get(d.getCode()); 122 123 if (ERR.isLoggable(ErrorManager.INFORMATIONAL)) { 124 ERR.log(ErrorManager.INFORMATIONAL, "code= " + d.getCode()); 125 ERR.log(ErrorManager.INFORMATIONAL, "rules = " + rules); 126 } 127 128 LazyFixList ehm; 129 130 if (rules != null) { 131 ehm = new CreatorBasedLazyFixList(info.getFileObject(), d.getCode(), (int)getPrefferedPosition(info, d), rules, data); 132 } else { 133 ehm = ErrorDescriptionFactory.lazyListForFixes(Collections.<Fix>emptyList()); 134 } 135 136 if (ERR.isLoggable(ErrorManager.INFORMATIONAL)) 137 ERR.log(ErrorManager.INFORMATIONAL, "ehm=" + ehm); 138 139 final String desc = d.getMessage(null); 140 final Position [] range = getLine(info, d, doc, (int)d.getStartPosition(), (int)d.getEndPosition()); 141 142 if (isCanceled()) 143 return null; 144 145 if (range[0] == null || range[1] == null) 146 continue; 147 148 descs.add(ErrorDescriptionFactory.createErrorDescription(errorKind2Severity.get(d.getKind()), desc, ehm, doc, range[0], range[1])); 149 } 150 151 if (isCanceled()) 152 return null; 153 154 LazyHintComputationFactory.getAndClearToCompute(file); 155 156 return descs; 157 } 158 159 public Document getDocument() { 160 try { 161 DataObject d = DataObject.find(file); 162 EditorCookie ec = (EditorCookie) d.getCookie(EditorCookie.class); 163 164 if (ec == null) 165 return null; 166 167 return ec.getDocument(); 168 } catch (IOException e) { 169 Logger.getLogger(JavaHintsProvider.class.getName()).log(Level.INFO, "SemanticHighlighter: Cannot find DataObject for file: " + FileUtil.getFileDisplayName(file), e); 170 return null; 171 } 172 } 173 174 public static Token findUnresolvedElementToken(CompilationInfo info, int offset) { 175 TokenSequence<JavaTokenId> ts = info.getTokenHierarchy().tokenSequence(); 176 177 ts.move(offset); 178 if (ts.moveNext()) { 179 Token t = ts.token(); 180 181 if (t.id() == JavaTokenId.DOT) { 182 ts.moveNext(); 183 t = ts.token(); 184 } else { 185 if (t.id() == JavaTokenId.LT) { 186 ts.moveNext(); 187 t = ts.token(); 188 } 189 } 190 191 if (t.id() == JavaTokenId.IDENTIFIER) { 192 return ts.offsetToken(); 193 } 194 } 195 return null; 196 } 197 198 private static int[] findUnresolvedElementSpan(CompilationInfo info, int offset) { 199 TokenHierarchy th = info.getTokenHierarchy(); 200 201 Token t = findUnresolvedElementToken(info, offset); 202 203 if (t != null) { 204 return new int[] { 205 t.offset(th), 206 t.offset(th) + t.length() 207 }; 208 } 209 210 return null; 211 } 212 213 static TreePath findUnresolvedElement(CompilationInfo info, int offset) { 214 int[] span = findUnresolvedElementSpan(info, offset); 215 216 if (span != null) { 217 return info.getTreeUtilities().pathFor(span[0] + 1); 218 } else { 219 return null; 220 } 221 } 222 223 private static final Set <String > CANNOT_RESOLVE = new HashSet <String >(Arrays.asList( 224 "compiler.err.cant.resolve", 225 "compiler.err.cant.resolve.location", 226 "compiler.err.doesnt.exist" 227 )); 228 229 private static final Set <String > UNDERLINE_IDENTIFIER = new HashSet <String >(Arrays.asList( 230 "compiler.err.local.var.accessed.from.icls.needs.final", 231 "compiler.err.var.might.not.have.been.initialized" 232 )); 233 234 private Position [] getLine(CompilationInfo info, Diagnostic d, final Document doc, int startOffset, int endOffset) { 235 StyledDocument sdoc = (StyledDocument ) doc; 236 DataObject dObj = (DataObject)doc.getProperty(doc.StreamDescriptionProperty ); 237 LineCookie lc = (LineCookie) dObj.getCookie(LineCookie.class); 238 int lineNumber = NbDocument.findLineNumber(sdoc, startOffset); 239 int lineOffset = NbDocument.findLineOffset(sdoc, lineNumber); 240 Line line = lc.getLineSet().getCurrent(lineNumber); 241 242 boolean rangePrepared = false; 243 244 if (CANNOT_RESOLVE.contains(d.getCode())) { 245 int[] span = findUnresolvedElementSpan(info, (int) getPrefferedPosition(info, d)); 246 247 if (span != null && span[0] != (-1) && span[1] != (-1)) { 248 startOffset = span[0]; 249 endOffset = span[1]; 250 rangePrepared = true; 251 } 252 } 253 254 if (UNDERLINE_IDENTIFIER.contains(d.getCode())) { 255 TokenSequence<JavaTokenId> ts = info.getTokenHierarchy().tokenSequence(JavaTokenId.language()); 256 257 int diff = ts.move((int) getPrefferedPosition(info, d)); 258 259 if (ts.moveNext() && diff >= 0 && diff < ts.token().length()) { 260 Token<JavaTokenId> t = ts.token(); 261 262 if (t.id() == JavaTokenId.IDENTIFIER) { 263 startOffset = ts.offset(); 264 endOffset = startOffset + t.length(); 265 rangePrepared = true; 266 } 267 } 268 } 269 270 if (!rangePrepared) { 271 String text = line.getText(); 272 273 int column = 0; 274 int length = text.length(); 275 276 while (column < text.length() && Character.isWhitespace(text.charAt(column))) 277 column++; 278 279 while (length > 0 && Character.isWhitespace(text.charAt(length - 1))) 280 length--; 281 282 startOffset = lineOffset + column; 283 endOffset = lineOffset + length; 284 } 285 286 if (ERR.isLoggable(ErrorManager.INFORMATIONAL)) { 287 ERR.log(ErrorManager.INFORMATIONAL, "startOffset = " + startOffset ); 288 ERR.log(ErrorManager.INFORMATIONAL, "endOffset = " + endOffset ); 289 } 290 291 final int startOffsetFinal = startOffset; 292 final int endOffsetFinal = endOffset; 293 final Position [] result = new Position [2]; 294 295 doc.render(new Runnable () { 296 public void run() { 297 if (isCanceled()) 298 return; 299 300 int len = doc.getLength(); 301 302 if (startOffsetFinal >= len || endOffsetFinal >= len) { 303 if (!isCanceled() && ERR.isLoggable(ErrorManager.WARNING)) { 304 ERR.log(ErrorManager.WARNING, "document changed, but not canceled?" ); 305 ERR.log(ErrorManager.WARNING, "len = " + len ); 306 ERR.log(ErrorManager.WARNING, "startOffset = " + startOffsetFinal ); 307 ERR.log(ErrorManager.WARNING, "endOffset = " + endOffsetFinal ); 308 } 309 cancel(); 310 311 return; 312 } 313 314 try { 315 result[0] = NbDocument.createPosition(doc, startOffsetFinal, Bias.Forward); 316 result[1] = NbDocument.createPosition(doc, endOffsetFinal, Bias.Backward); 317 } catch (BadLocationException e) { 318 ERR.notify(ErrorManager.ERROR, e); 319 } 320 } 321 }); 322 323 return result; 324 } 325 326 private boolean cancel; 327 328 synchronized boolean isCanceled() { 329 return cancel; 330 } 331 332 public synchronized void cancel() { 333 cancel = true; 334 } 335 336 synchronized void resume() { 337 cancel = false; 338 } 339 340 public void run(CompilationInfo info) { 341 resume(); 342 343 Document doc = getDocument(); 344 345 if (doc == null) { 346 Logger.getLogger(JavaHintsProvider.class.getName()).log(Level.INFO, "SemanticHighlighter: Cannot get document!"); 347 return ; 348 } 349 350 long start = System.currentTimeMillis(); 351 352 List <ErrorDescription> errors = computeErrors(info, doc); 353 354 if (errors == null) return ; 356 357 HintsController.setErrors(doc, "java-hints", errors); 358 359 long end = System.currentTimeMillis(); 360 361 TimesCollector.getDefault().reportTime(info.getFileObject(), "java-hints", "Java Hints", end - start); 362 } 363 364 private long getPrefferedPosition(CompilationInfo info, Diagnostic d) { 365 if ("compiler.err.doesnt.exist".equals(d.getCode())) { 366 return d.getStartPosition(); 367 } 368 if ("compiler.err.not.stmt".equals(d.getCode())) { 369 TreePath path = findUnresolvedElement(info, (int) d.getStartPosition() - 1); 371 Element el = path != null ? info.getTrees().getElement(path) : null; 372 373 if (el == null || el.asType().getKind() == TypeKind.ERROR) { 374 return d.getStartPosition() - 1; 375 } 376 377 if (el.asType().getKind() == TypeKind.PACKAGE) { 378 String s = ((PackageElement) el).getQualifiedName().toString(); 380 if (info.getElements().getPackageElement(s) == null) { 381 return d.getStartPosition() - 1; 383 } 384 } 385 386 return d.getStartPosition(); 387 } 388 389 return d.getPosition(); 390 } 391 392 } 393 394 | Popular Tags |