api/src/org/netbeans/modules/jackpot30/impl/hints/HintsInvoker.java
author Jan Lahoda <jlahoda@netbeans.org>
Sun, 06 Mar 2011 19:23:09 +0100
branchmarks
changeset 555 b9a0402dbea3
parent 554 59cba1c67c40
permissions -rw-r--r--
Fixing @SuppressWarnings for marks-based hints.
     1 /*
     2  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
     3  *
     4  * Copyright 2008-2010 Sun Microsystems, Inc. All rights reserved.
     5  *
     6  * The contents of this file are subject to the terms of either the GNU
     7  * General Public License Version 2 only ("GPL") or the Common
     8  * Development and Distribution License("CDDL") (collectively, the
     9  * "License"). You may not use this file except in compliance with the
    10  * License. You can obtain a copy of the License at
    11  * http://www.netbeans.org/cddl-gplv2.html
    12  * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
    13  * specific language governing permissions and limitations under the
    14  * License.  When distributing the software, include this License Header
    15  * Notice in each file and include the License file at
    16  * nbbuild/licenses/CDDL-GPL-2-CP.  Sun designates this
    17  * particular file as subject to the "Classpath" exception as provided
    18  * by Sun in the GPL Version 2 section of the License file that
    19  * accompanied this code. If applicable, add the following below the
    20  * License Header, with the fields enclosed by brackets [] replaced by
    21  * your own identifying information:
    22  * "Portions Copyrighted [year] [name of copyright owner]"
    23  *
    24  * If you wish your version of this file to be governed by only the CDDL
    25  * or only the GPL Version 2, indicate your decision by adding
    26  * "[Contributor] elects to include this software in this distribution
    27  * under the [CDDL or GPL Version 2] license." If you do not indicate a
    28  * single choice of license, a recipient has the option to distribute
    29  * your version of this file under either the CDDL, the GPL Version 2 or
    30  * to extend the choice of license to its licensees as provided above.
    31  * However, if you add GPL Version 2 code and therefore, elected the GPL
    32  * Version 2 license, then the option applies only if the new code is
    33  * made subject to such option by the copyright holder.
    34  *
    35  * Contributor(s):
    36  *
    37  * Portions Copyrighted 2008-2010 Sun Microsystems, Inc.
    38  */
    39 
    40 package org.netbeans.modules.jackpot30.impl.hints;
    41 
    42 import com.sun.source.tree.Tree;
    43 import java.util.Stack;
    44 import java.util.concurrent.atomic.AtomicBoolean;
    45 import javax.lang.model.element.AnnotationMirror;
    46 import javax.lang.model.element.AnnotationValue;
    47 import javax.lang.model.element.Element;
    48 import javax.lang.model.element.ExecutableElement;
    49 import javax.lang.model.element.TypeElement;
    50 import javax.swing.text.Document;
    51 import org.netbeans.api.java.source.support.CancellableTreePathScanner;
    52 import org.netbeans.editor.GuardedDocument;
    53 import org.netbeans.editor.MarkBlock;
    54 import org.netbeans.editor.MarkBlockChain;
    55 import org.openide.filesystems.FileObject;
    56 
    57 import com.sun.source.tree.Tree.Kind;
    58 import com.sun.source.util.TreePath;
    59 import com.sun.source.util.Trees;
    60 import java.io.IOException;
    61 import java.util.ArrayList;
    62 import java.util.Collection;
    63 import java.util.Collections;
    64 import java.util.Comparator;
    65 import java.util.HashMap;
    66 import java.util.HashSet;
    67 import java.util.Iterator;
    68 import java.util.LinkedList;
    69 import java.util.List;
    70 import java.util.Map;
    71 import java.util.Map.Entry;
    72 import java.util.Set;
    73 import javax.annotation.processing.ProcessingEnvironment;
    74 import javax.lang.model.type.TypeMirror;
    75 import org.netbeans.api.java.source.CompilationInfo;
    76 import org.netbeans.modules.jackpot30.impl.MessageImpl;
    77 import org.netbeans.modules.jackpot30.impl.RulesManager;
    78 import org.netbeans.modules.jackpot30.impl.Utilities;
    79 import org.netbeans.modules.jackpot30.impl.pm.BulkSearch;
    80 import org.netbeans.modules.jackpot30.impl.pm.BulkSearch.BulkPattern;
    81 import org.netbeans.modules.jackpot30.impl.pm.CopyFinder;
    82 import org.netbeans.modules.jackpot30.impl.pm.CopyFinder.VariableAssignments;
    83 import org.netbeans.modules.jackpot30.impl.pm.Pattern;
    84 import org.netbeans.modules.jackpot30.spi.Hacks;
    85 import org.netbeans.modules.jackpot30.spi.HintContext;
    86 import org.netbeans.modules.jackpot30.spi.HintDescription;
    87 import org.netbeans.modules.jackpot30.spi.HintDescription.Condition;
    88 import org.netbeans.modules.jackpot30.spi.HintDescription.CustomCondition;
    89 import org.netbeans.modules.jackpot30.spi.HintDescription.DeclarativeFixDescription;
    90 import org.netbeans.modules.jackpot30.spi.HintDescription.ErrorDescriptionAcceptor;
    91 import org.netbeans.modules.jackpot30.spi.HintDescription.FixAcceptor;
    92 import org.netbeans.modules.jackpot30.spi.HintDescription.Literal;
    93 import org.netbeans.modules.jackpot30.spi.HintDescription.MarkCondition;
    94 import org.netbeans.modules.jackpot30.spi.HintDescription.MarksWorker;
    95 import org.netbeans.modules.jackpot30.spi.HintDescription.Operator;
    96 import org.netbeans.modules.jackpot30.spi.HintDescription.OtherwiseCondition;
    97 import org.netbeans.modules.jackpot30.spi.HintDescription.PatternDescription;
    98 import org.netbeans.modules.jackpot30.spi.HintDescription.Selector;
    99 import org.netbeans.modules.jackpot30.spi.HintDescription.Value;
   100 import org.netbeans.modules.jackpot30.spi.HintMetadata;
   101 import org.netbeans.modules.jackpot30.spi.HintMetadata.HintSeverity;
   102 import org.netbeans.modules.jackpot30.spi.support.FixFactory;
   103 import org.netbeans.spi.editor.hints.ErrorDescription;
   104 import org.netbeans.spi.editor.hints.ErrorDescriptionFactory;
   105 import org.netbeans.spi.editor.hints.Fix;
   106 import org.netbeans.spi.editor.hints.Severity;
   107 import org.openide.util.Exceptions;
   108 
   109 /**
   110  *
   111  * @author lahvac
   112  */
   113 public class HintsInvoker {
   114 
   115     private final Map<String, Long> timeLog = new HashMap<String, Long>();
   116 
   117     private final CompilationInfo info;
   118     private final int caret;
   119     private final int from;
   120     private final int to;
   121     private final AtomicBoolean cancel;
   122 
   123     public HintsInvoker(CompilationInfo info, AtomicBoolean cancel) {
   124         this(info, -1, cancel);
   125     }
   126 
   127     public HintsInvoker(CompilationInfo info, int caret, AtomicBoolean cancel) {
   128         this(info, caret, -1, -1, cancel);
   129     }
   130 
   131     public HintsInvoker(CompilationInfo info, int from, int to, AtomicBoolean cancel) {
   132         this(info, -1, from, to, cancel);
   133     }
   134 
   135     private HintsInvoker(CompilationInfo info, int caret, int from, int to, AtomicBoolean cancel) {
   136         this.info = info;
   137         this.caret = caret;
   138         this.from = from;
   139         this.to = to;
   140         this.cancel = cancel;
   141     }
   142 
   143     public List<ErrorDescription> computeHints(CompilationInfo info) {
   144         return computeHints(info, new TreePath(info.getCompilationUnit()));
   145     }
   146 
   147     private List<ErrorDescription> computeHints(CompilationInfo info, TreePath startAt) {
   148         List<HintDescription> descs = new LinkedList<HintDescription>();
   149         for (Entry<HintMetadata, Collection<? extends HintDescription>> e : RulesManager.getInstance().allHints.entrySet()) {
   150             HintMetadata m = e.getKey();
   151 
   152             if (!RulesManager.getInstance().isHintEnabled(m)) {
   153                 continue;
   154             }
   155 
   156             if (caret != -1) {
   157                 if (m.kind == HintMetadata.Kind.SUGGESTION || m.kind == HintMetadata.Kind.SUGGESTION_NON_GUI) {
   158                     descs.addAll(e.getValue());
   159                 } else {
   160                     if (RulesManager.getInstance().getHintSeverity(m) == HintSeverity.CURRENT_LINE_WARNING) {
   161                         descs.addAll(e.getValue());
   162                     }
   163                 }
   164             } else {
   165                 if (m.kind == HintMetadata.Kind.HINT || m.kind == HintMetadata.Kind.HINT_NON_GUI) {
   166                     if (RulesManager.getInstance().getHintSeverity(m) != HintSeverity.CURRENT_LINE_WARNING || from != (-1)) {
   167                         descs.addAll(e.getValue());
   168                     }
   169                 }
   170             }
   171         }
   172 
   173         Map<Kind, List<HintDescription>> kindHints = new HashMap<Kind, List<HintDescription>>();
   174         Map<PatternDescription, List<HintDescription>> patternHints = new HashMap<PatternDescription, List<HintDescription>>();
   175 
   176         RulesManager.sortOut(descs, kindHints, patternHints);
   177 
   178         long elementBasedStart = System.currentTimeMillis();
   179 
   180         RulesManager.computeElementBasedHintsXXX(info, cancel, kindHints, patternHints);
   181 
   182         long elementBasedEnd = System.currentTimeMillis();
   183 
   184         timeLog.put("Computing Element Based Hints", elementBasedEnd - elementBasedStart);
   185 
   186         List<ErrorDescription> errors = join(computeHints(info, startAt, kindHints, patternHints, new LinkedList<MessageImpl>()));
   187 
   188         dumpTimeSpentInHints();
   189         
   190         return errors;
   191     }
   192 
   193     public List<ErrorDescription> computeHints(CompilationInfo info,
   194                                         Map<Kind, List<HintDescription>> hints,
   195                                         Map<PatternDescription, List<HintDescription>> patternHints) {
   196         return computeHints(info, hints, patternHints, new LinkedList<MessageImpl>());
   197     }
   198 
   199     public List<ErrorDescription> computeHints(CompilationInfo info,
   200                                         Map<Kind, List<HintDescription>> hints,
   201                                         Map<PatternDescription, List<HintDescription>> patternHints,
   202                                         Collection<? super MessageImpl> problems) {
   203         return join(computeHints(info, new TreePath(info.getCompilationUnit()), hints, patternHints, problems));
   204     }
   205 
   206     public Map<HintDescription, List<ErrorDescription>> computeHints(CompilationInfo info,
   207                                         TreePath startAt,
   208                                         Map<Kind, List<HintDescription>> hints,
   209                                         Map<PatternDescription, List<HintDescription>> patternHints,
   210                                         Collection<? super MessageImpl> problems) {
   211         if (caret != -1) {
   212             TreePath tp = info.getTreeUtilities().pathFor(caret);
   213             return computeSuggestions(info, tp, hints, patternHints, problems);
   214         } else {
   215             if (from != (-1) && to != (-1)) {
   216                 return computeHintsInSpan(info, hints, patternHints, problems);
   217             } else {
   218                 return computeHintsImpl(info, startAt, hints, patternHints, problems);
   219             }
   220         }
   221     }
   222 
   223     Map<HintDescription, List<ErrorDescription>> computeHintsImpl(CompilationInfo info,
   224                                         TreePath startAt,
   225                                         Map<Kind, List<HintDescription>> hints,
   226                                         Map<PatternDescription, List<HintDescription>> patternHints,
   227                                         Collection<? super MessageImpl> problems) {
   228         Map<HintDescription, List<ErrorDescription>> errors = new HashMap<HintDescription, List<ErrorDescription>>();
   229 
   230         long kindCount = 0;
   231 
   232         for (Entry<Kind, List<HintDescription>> e : hints.entrySet()) {
   233             kindCount += e.getValue().size();
   234         }
   235 
   236         timeLog.put("[C] Kind Based Hints", kindCount);
   237 
   238         if (!hints.isEmpty()) {
   239             long kindStart = System.currentTimeMillis();
   240 
   241             new ScannerImpl(info, cancel, hints).scan(startAt, errors);
   242 
   243             long kindEnd = System.currentTimeMillis();
   244 
   245             timeLog.put("Kind Based Hints", kindEnd - kindStart);
   246         }
   247 
   248         timeLog.put("[C] Pattern Based Hints", (long) patternHints.size());
   249 
   250         long patternStart = System.currentTimeMillis();
   251 
   252         Map<String, List<PatternDescription>> patternTests = computePatternTests(patternHints);
   253 
   254         long bulkPatternStart = System.currentTimeMillis();
   255 
   256         BulkPattern bulkPattern = BulkSearch.getDefault().create(info, patternTests.keySet());
   257 
   258         long bulkPatternEnd = System.currentTimeMillis();
   259 
   260         timeLog.put("Bulk Pattern preparation", bulkPatternEnd - bulkPatternStart);
   261 
   262         long bulkStart = System.currentTimeMillis();
   263 
   264         Map<String, Collection<TreePath>> occurringPatterns = BulkSearch.getDefault().match(info, startAt, bulkPattern, timeLog);
   265 
   266         long bulkEnd = System.currentTimeMillis();
   267 
   268         timeLog.put("Bulk Search", bulkEnd - bulkStart);
   269 
   270         mergeAll(errors, doComputeHints(info, occurringPatterns, patternTests, patternHints, problems));
   271 
   272         long patternEnd = System.currentTimeMillis();
   273 
   274         timeLog.put("Pattern Based Hints", patternEnd - patternStart);
   275 
   276         return errors;
   277     }
   278 
   279     Map<HintDescription, List<ErrorDescription>> computeHintsInSpan(CompilationInfo info,
   280                                         Map<Kind, List<HintDescription>> hints,
   281                                         Map<PatternDescription, List<HintDescription>> patternHints,
   282                                         Collection<? super MessageImpl> problems) {
   283 
   284         TreePath path = info.getTreeUtilities().pathFor((from + to) / 2);
   285 
   286         while (path.getLeaf().getKind() != Kind.COMPILATION_UNIT) {
   287             int start = (int) info.getTrees().getSourcePositions().getStartPosition(info.getCompilationUnit(), path.getLeaf());
   288             int end = (int) info.getTrees().getSourcePositions().getEndPosition(info.getCompilationUnit(), path.getLeaf());
   289 
   290             if (start <= from && end >= to) {
   291                 break;
   292             }
   293 
   294             path = path.getParentPath();
   295         }
   296 
   297         Map<HintDescription, List<ErrorDescription>> errors = new HashMap<HintDescription, List<ErrorDescription>>();
   298 
   299         if (!hints.isEmpty()) {
   300             long kindStart = System.currentTimeMillis();
   301 
   302             new ScannerImpl(info, cancel, hints).scan(path, errors);
   303 
   304             long kindEnd = System.currentTimeMillis();
   305 
   306             timeLog.put("Kind Based Hints", kindEnd - kindStart);
   307         }
   308 
   309         if (!patternHints.isEmpty()) {
   310             long patternStart = System.currentTimeMillis();
   311 
   312             Map<String, List<PatternDescription>> patternTests = computePatternTests(patternHints);
   313 
   314             long bulkStart = System.currentTimeMillis();
   315 
   316             BulkPattern bulkPattern = BulkSearch.getDefault().create(info, patternTests.keySet());
   317             Map<String, Collection<TreePath>> occurringPatterns = BulkSearch.getDefault().match(info, path, bulkPattern, timeLog);
   318 
   319             long bulkEnd = System.currentTimeMillis();
   320 
   321             timeLog.put("Bulk Search", bulkEnd - bulkStart);
   322 
   323             mergeAll(errors, doComputeHints(info, occurringPatterns, patternTests, patternHints, problems));
   324 
   325             long patternEnd = System.currentTimeMillis();
   326 
   327             timeLog.put("Pattern Based Hints", patternEnd - patternStart);
   328         }
   329 
   330         return errors;
   331     }
   332 
   333     Map<HintDescription, List<ErrorDescription>> computeSuggestions(CompilationInfo info,
   334                                         TreePath workOn,
   335                                         Map<Kind, List<HintDescription>> hints,
   336                                         Map<PatternDescription, List<HintDescription>> patternHints,
   337                                         Collection<? super MessageImpl> problems) {
   338         Map<HintDescription, List<ErrorDescription>> errors = new HashMap<HintDescription, List<ErrorDescription>>();
   339 
   340         if (!hints.isEmpty()) {
   341             long kindStart = System.currentTimeMillis();
   342 
   343             TreePath proc = workOn;
   344 
   345             while (proc != null) {
   346                 new ScannerImpl(info, cancel, hints).scanDoNotGoDeeper(proc, errors);
   347                 proc = proc.getParentPath();
   348             }
   349 
   350             long kindEnd = System.currentTimeMillis();
   351 
   352             timeLog.put("Kind Based Suggestions", kindEnd - kindStart);
   353         }
   354 
   355         if (!patternHints.isEmpty()) {
   356             long patternStart = System.currentTimeMillis();
   357 
   358             Map<String, List<PatternDescription>> patternTests = computePatternTests(patternHints);
   359 
   360             //pretend that all the patterns occur on all treepaths from the current path
   361             //up (probably faster than using BulkSearch over whole file)
   362             //TODO: what about machint trees under the current path?
   363             Set<TreePath> paths = new HashSet<TreePath>();
   364 
   365             TreePath tp = workOn;
   366 
   367             while (tp != null) {
   368                 paths.add(tp);
   369                 tp = tp.getParentPath();
   370             }
   371 
   372             Map<String, Collection<TreePath>> occurringPatterns = new HashMap<String, Collection<TreePath>>();
   373 
   374             for (String p : patternTests.keySet()) {
   375                 occurringPatterns.put(p, paths);
   376             }
   377 
   378 //            long bulkStart = System.currentTimeMillis();
   379 //
   380 //            BulkPattern bulkPattern = BulkSearch.getDefault().create(info, patternTests.keySet());
   381 //            Map<String, Collection<TreePath>> occurringPatterns = BulkSearch.getDefault().match(info, new TreePath(info.getCompilationUnit()), bulkPattern, timeLog);
   382 //
   383 //            long bulkEnd = System.currentTimeMillis();
   384 //
   385 //            Set<Tree> acceptedLeafs = new HashSet<Tree>();
   386 //
   387 //            TreePath tp = workOn;
   388 //
   389 //            while (tp != null) {
   390 //                acceptedLeafs.add(tp.getLeaf());
   391 //                tp = tp.getParentPath();
   392 //            }
   393 //
   394 //            for (Entry<String, Collection<TreePath>> e : occurringPatterns.entrySet()) {
   395 //                for (Iterator<TreePath> it = e.getValue().iterator(); it.hasNext(); ) {
   396 //                    if (!acceptedLeafs.contains(it.next().getLeaf())) {
   397 //                        it.remove();
   398 //                    }
   399 //                }
   400 //            }
   401 //
   402 //            timeLog.put("Bulk Search", bulkEnd - bulkStart);
   403 
   404             mergeAll(errors, doComputeHints(info, occurringPatterns, patternTests, patternHints, problems));
   405 
   406             long patternEnd = System.currentTimeMillis();
   407 
   408             timeLog.put("Pattern Based Hints", patternEnd - patternStart);
   409         }
   410 
   411         return errors;
   412     }
   413 
   414     public Map<HintDescription, List<ErrorDescription>> doComputeHints(CompilationInfo info, Map<String, Collection<TreePath>> occurringPatterns, Map<String, List<PatternDescription>> patterns, Map<PatternDescription, List<HintDescription>> patternHints) throws IllegalStateException {
   415         return doComputeHints(info, occurringPatterns, patterns, patternHints, new LinkedList<MessageImpl>());
   416     }
   417 
   418     public static Map<String, List<PatternDescription>> computePatternTests(Map<PatternDescription, List<HintDescription>> patternHints) {
   419         Map<String, List<PatternDescription>> patternTests = new HashMap<String, List<PatternDescription>>();
   420         for (Entry<PatternDescription, List<HintDescription>> e : patternHints.entrySet()) {
   421             String p = e.getKey().getPattern();
   422             List<PatternDescription> descs = patternTests.get(p);
   423             if (descs == null) {
   424                 patternTests.put(p, descs = new LinkedList<PatternDescription>());
   425             }
   426             descs.add(e.getKey());
   427         }
   428         return patternTests;
   429     }
   430 
   431     private Map<HintDescription, List<ErrorDescription>> doComputeHints(CompilationInfo info, Map<String, Collection<TreePath>> occurringPatterns, Map<String, List<PatternDescription>> patterns, Map<PatternDescription, List<HintDescription>> patternHints, Collection<? super MessageImpl> problems) throws IllegalStateException {
   432         Map<HintDescription, List<ErrorDescription>> errors = new HashMap<HintDescription, List<ErrorDescription>>();
   433         List<HintEvaluationData> evaluationData = new LinkedList<HintEvaluationData>();
   434 
   435         for (Entry<String, Collection<TreePath>> occ : occurringPatterns.entrySet()) {
   436             for (PatternDescription d : patterns.get(occ.getKey())) {
   437                 Map<String, TypeMirror> constraints = new HashMap<String, TypeMirror>();
   438 
   439                 for (Entry<String, String> e : d.getConstraints().entrySet()) {
   440                     constraints.put(e.getKey(), Hacks.parseFQNType(info, e.getValue()));
   441                 }
   442 
   443                 Pattern p = Pattern.compile(info, occ.getKey(), constraints, d.getImports());
   444                 TreePath toplevel = new TreePath(info.getCompilationUnit());
   445                 TreePath patt = new TreePath(toplevel, p.getPattern());
   446 
   447                 for (TreePath candidate : occ.getValue()) {
   448                     VariableAssignments verified = CopyFinder.computeVariables(info, patt, candidate, cancel, p.getConstraints());
   449 
   450                     if (verified == null) {
   451                         continue;
   452                     }
   453 
   454                     Set<String> suppressedWarnings = new HashSet<String>(Utilities.findSuppressedWarnings(info, candidate));
   455 
   456                     for (HintDescription hd : patternHints.get(d)) {
   457                         HintMetadata hm = hd.getMetadata();
   458                         HintContext c = new HintContext(info, hm, candidate, verified.variables, verified.multiVariables, verified.variables2Names, constraints, problems);
   459 
   460                         if (!Collections.disjoint(suppressedWarnings, hm.suppressWarnings))
   461                             continue;
   462 
   463                         if (hd.getWorker() instanceof MarksWorker) {
   464                             HintContext workerContext = new HintContext(c);
   465 
   466                             workerContext.enterScope();
   467                             
   468                             MarksWorker mw = (MarksWorker) hd.getWorker();
   469                             List<FixEvaluationData> fixData = new LinkedList<FixEvaluationData>();
   470 
   471                             for (DeclarativeFixDescription fd : mw.fixes) {
   472                                 HintContext fixContext = new HintContext(workerContext);
   473 
   474                                 fixContext.enterScope();
   475                                 fixData.add(new FixEvaluationData(fixContext, new LinkedList<Condition>(fd.marks), fd.acceptor));
   476                             }
   477 
   478                             HintEvaluationData data = new HintEvaluationData(workerContext, hd, new LinkedList<Condition>(mw.marks), mw.acceptor, fixData);
   479 
   480                             evaluationData.add(data);
   481                             continue;
   482                         }
   483 
   484                         Collection<? extends ErrorDescription> workerErrors = runHint(hd, c);
   485 
   486                         if (workerErrors != null) {
   487                             merge(errors, hd, workerErrors);
   488                         }
   489                     }
   490                 }
   491             }
   492         }
   493 
   494         Map<Tree, Map<String, Object>> marks = new HashMap<Tree, Map<String, Object>>();
   495 
   496         for (HintEvaluationData hed : evaluationData) {
   497             enterSpeculativeAssignments(hed.ctx, hed.marks, marks);
   498 
   499             for (FixEvaluationData fed : hed.fixDescriptions) {
   500                 //XXX: there is currently no test that would test this: (seee testSpeculativeAssignmentForFixes)
   501                 enterSpeculativeAssignments(hed.ctx, fed.marks, marks);
   502             }
   503         }
   504 
   505         boolean wasChange = true;
   506 
   507         while (wasChange /*!evaluationData.isEmpty()*/) {
   508             boolean currentWasChange = false;
   509 
   510             for (Iterator<HintEvaluationData> it = evaluationData.iterator(); it.hasNext(); ) {
   511                 HintEvaluationData hed = it.next();
   512                 int origMarksSize = hed.marks.size();
   513 
   514                 Boolean hres = processConditions(hed.ctx, marks, hed.marks, -1, -1);
   515 
   516                 currentWasChange |= origMarksSize != hed.marks.size();
   517 
   518                 if (hres == null) {
   519                     //XXX???
   520                     continue;
   521                 }
   522 
   523                 if (hres != null && !hres) {
   524                     currentWasChange = true;
   525                     clearSpeculativeAssignments(hed.ctx, hed.marks, marks);
   526                     for (FixEvaluationData fed : hed.fixDescriptions) {
   527                         clearSpeculativeAssignments(hed.ctx, fed.marks, marks);
   528                     }
   529                     it.remove();
   530                     continue;
   531                 }
   532 
   533                 for (Iterator<FixEvaluationData> fixes = hed.fixDescriptions.iterator(); fixes.hasNext(); ) {
   534                     FixEvaluationData fed = fixes.next();
   535                     int o = fed.marks.size();
   536                     Boolean res = processConditions(fed.ctx, marks, fed.marks, hed.fixDescriptions.size(), hed.createdFixes.size());
   537 
   538                     currentWasChange |= o != fed.marks.size();
   539 
   540                     if (res == null) continue;
   541 
   542                     if (res) {
   543                         Fix fix = fed.acceptor.accept(fed.ctx);
   544 
   545                         if (fix != null) {
   546                             hed.createdFixes.add(fix);
   547                         }
   548                     } else {
   549                         clearSpeculativeAssignments(fed.ctx, fed.marks, marks);
   550                     }
   551 
   552                     fixes.remove();
   553                     currentWasChange = true;
   554                 }
   555 
   556                 if (!wasChange && !currentWasChange) {
   557                     hed.fixDescriptions.clear();
   558                 }
   559 
   560                 if (hed.fixDescriptions.isEmpty()) {
   561                     //XXX: @SuppressWarnings!
   562                     ErrorDescription ed = hed.acceptor.accept(hed.ctx);
   563                     List<String> swKeys = new ArrayList<String>();
   564 
   565                     for (String key : hed.hd.getSuppressWarnings()) {
   566                         if (key == null) break;
   567                         swKeys.add(key);
   568                     }
   569 
   570                     List<Fix> fixes = new ArrayList<Fix>();
   571 
   572                     fixes.addAll(hed.createdFixes);
   573 
   574                     if (!swKeys.isEmpty()) {
   575                         fixes.addAll(FixFactory.createSuppressWarnings(info, hed.ctx.getPath(), swKeys.toArray(new String[0])));
   576                     }
   577 
   578                     ed = ErrorDescriptionFactory.createErrorDescription(ed.getSeverity(), ed.getDescription(), fixes, ed.getFile(), ed.getRange().getBegin().getOffset(), ed.getRange().getEnd().getOffset());
   579 
   580                     if (ed != null) {
   581                         merge(errors, hed.hd, ed);
   582                     }
   583                     it.remove();
   584                 }
   585             }
   586 
   587             wasChange = currentWasChange;
   588         }
   589 
   590         return errors;
   591     }
   592 
   593 //    public static void computeHints(URI file, ProcessingEnvironment env, CompilationUnitTree cut, RulesManager m) {
   594 //        Map<Kind, HintDescription> hints = m.getKindBasedHints();
   595 //
   596 //        if (hints.isEmpty()) {
   597 //            return ;
   598 //        }
   599 //
   600 //        List<ErrorDescription> errors = new  LinkedList<ErrorDescription>();
   601 //
   602 //        File af = new File(file.getPath());
   603 //        FileObject f = FileUtil.toFileObject(af);
   604 //
   605 //        new ScannerImpl(f, env, hints).scan(cut, errors);
   606 //
   607 //        for (ErrorDescription ed : errors) {
   608 //            Diagnostic.Kind k;
   609 //
   610 //            switch (ed.getSeverity()) {
   611 //                case ERROR:
   612 //                    k = Diagnostic.Kind.ERROR;
   613 //                    break;
   614 //                default:
   615 //                    k = Diagnostic.Kind.WARNING;
   616 //                    break;
   617 //            }
   618 //
   619 //            env.getMessager().printMessage(k, ed.getDescription());
   620 //        }
   621 //    }
   622 
   623     public Map<String, Long> getTimeLog() {
   624         return timeLog;
   625     }
   626 
   627     private final class ScannerImpl extends CancellableTreePathScanner<Void, Map<HintDescription, List<ErrorDescription>>> {
   628 
   629         private final Stack<Set<String>> suppresWarnings = new Stack<Set<String>>();
   630         private final CompilationInfo info;
   631         private final FileObject file;
   632         private final ProcessingEnvironment env;
   633         private final Map<Kind, List<HintDescription>> hints;
   634 
   635         public ScannerImpl(CompilationInfo info, AtomicBoolean cancel, Map<Kind, List<HintDescription>> hints) {
   636             super(cancel);
   637             this.info = info;
   638             this.file = null;
   639             this.env  = null;
   640             this.hints = hints;
   641         }
   642 
   643         public ScannerImpl(FileObject file, ProcessingEnvironment env, Map<Kind, List<HintDescription>> hints) {
   644             super(new AtomicBoolean());
   645             this.info = null;
   646             this.file = file;
   647             this.env = env;
   648             this.hints = hints;
   649         }
   650 
   651         private void runAndAdd(TreePath path, List<HintDescription> rules, Map<HintDescription, List<ErrorDescription>> d) {
   652             if (rules != null && !isInGuarded(info, path)) {
   653                 OUTER: for (HintDescription hd : rules) {
   654                     if (isCanceled()) {
   655                         return ;
   656                     }
   657 
   658                     HintMetadata hm = hd.getMetadata();
   659 
   660                     for (String wname : hm.suppressWarnings) {
   661                         if( !suppresWarnings.empty() && suppresWarnings.peek().contains(wname)) {
   662                             continue OUTER;
   663                         }
   664                     }
   665 
   666                     HintContext c = new HintContext(info, hm, path, Collections.<String, TreePath>emptyMap(), Collections.<String, Collection<? extends TreePath>>emptyMap(), Collections.<String, String>emptyMap());
   667                     Collection<? extends ErrorDescription> errors = runHint(hd, c);
   668 
   669                     if (errors != null) {
   670                         merge(d, hd, errors);
   671                     }
   672                 }
   673             }
   674         }
   675 
   676         @Override
   677         public Void scan(Tree tree, Map<HintDescription, List<ErrorDescription>> p) {
   678             if (tree == null)
   679                 return null;
   680 
   681             TreePath tp = new TreePath(getCurrentPath(), tree);
   682             Kind k = tree.getKind();
   683 
   684             boolean b = pushSuppressWarrnings(tp);
   685             try {
   686                 runAndAdd(tp, hints.get(k), p);
   687 
   688                 if (isCanceled()) {
   689                     return null;
   690                 }
   691 
   692                 return super.scan(tree, p);
   693             } finally {
   694                 if (b) {
   695                     suppresWarnings.pop();
   696                 }
   697             }
   698         }
   699 
   700         @Override
   701         public Void scan(TreePath path, Map<HintDescription, List<ErrorDescription>> p) {
   702             Kind k = path.getLeaf().getKind();
   703             boolean b = pushSuppressWarrnings(path);
   704             try {
   705                 runAndAdd(path, hints.get(k), p);
   706 
   707                 if (isCanceled()) {
   708                     return null;
   709                 }
   710 
   711                 return super.scan(path, p);
   712             } finally {
   713                 if (b) {
   714                     suppresWarnings.pop();
   715                 }
   716             }
   717         }
   718 
   719         public void scanDoNotGoDeeper(TreePath path, Map<HintDescription, List<ErrorDescription>> p) {
   720             Kind k = path.getLeaf().getKind();
   721             runAndAdd(path, hints.get(k), p);
   722         }
   723 
   724         private boolean pushSuppressWarrnings(TreePath path) {
   725             switch(path.getLeaf().getKind()) {
   726                 case CLASS:
   727                 case METHOD:
   728                 case VARIABLE:
   729                     Set<String> current = suppresWarnings.size() == 0 ? null : suppresWarnings.peek();
   730                     Set<String> nju = current == null ? new HashSet<String>() : new HashSet<String>(current);
   731 
   732                     Element e = getTrees().getElement(path);
   733 
   734                     if ( e != null) {
   735                         for (AnnotationMirror am : e.getAnnotationMirrors()) {
   736                             String name = ((TypeElement)am.getAnnotationType().asElement()).getQualifiedName().toString();
   737                             if ( "java.lang.SuppressWarnings".equals(name) ) { // NOI18N
   738                                 Map<? extends ExecutableElement, ? extends AnnotationValue> elementValues = am.getElementValues();
   739                                 for (Map.Entry<? extends ExecutableElement, ? extends AnnotationValue> entry : elementValues.entrySet()) {
   740                                     if( "value".equals(entry.getKey().getSimpleName().toString()) ) { // NOI18N
   741                                         Object value = entry.getValue().getValue();
   742                                         if ( value instanceof List) {
   743                                             for (Object av : (List)value) {
   744                                                 if( av instanceof AnnotationValue ) {
   745                                                     Object wname = ((AnnotationValue)av).getValue();
   746                                                     if ( wname instanceof String ) {
   747                                                         nju.add((String)wname);
   748                                                     }
   749                                                 }
   750                                             }
   751                                         }
   752                                     }
   753                                 }
   754                             }
   755                         }
   756                     }
   757 
   758                     suppresWarnings.push(nju);
   759                     return true;
   760             }
   761             return false;
   762         }
   763 
   764         private Trees getTrees() {
   765             return info != null ? info.getTrees() : Trees.instance(env);
   766         }
   767     }
   768 
   769     static boolean isInGuarded(CompilationInfo info, TreePath tree) {
   770         if (info == null) {
   771             return false;
   772         }
   773 
   774         try {
   775             Document doc = info.getDocument();
   776 
   777             if (doc instanceof GuardedDocument) {
   778                 int start = (int) info.getTrees().getSourcePositions().getStartPosition(info.getCompilationUnit(), tree.getLeaf());
   779                 int end = (int) info.getTrees().getSourcePositions().getEndPosition(info.getCompilationUnit(), tree.getLeaf());
   780                 GuardedDocument gdoc = (GuardedDocument) doc;
   781                 MarkBlockChain guardedBlockChain = gdoc.getGuardedBlockChain();
   782                 if (guardedBlockChain.compareBlock(start, end) == MarkBlock.INNER) {
   783                     return true;
   784                 }
   785             }
   786         } catch (IOException ex) {
   787             Exceptions.printStackTrace(ex);
   788         }
   789 
   790         return false;
   791     }
   792 
   793     private Collection<? extends ErrorDescription> runHint(HintDescription hd, HintContext ctx) {
   794         long start = System.nanoTime();
   795 
   796         try {
   797             return hd.getWorker().createErrors(ctx);
   798         } finally {
   799             long end = System.nanoTime();
   800             reportSpentTime(hd.getMetadata().id, end - start);
   801         }
   802     }
   803 
   804     public static <K, V> Map<K, List<V>> merge(Map<K, List<V>> to, K key, V value) {
   805         List<V> toColl = to.get(key);
   806 
   807         if (toColl == null) {
   808             to.put(key, toColl = new LinkedList<V>());
   809         }
   810 
   811         toColl.add(value);
   812 
   813         return to;
   814     }
   815 
   816     public static <K, V> Map<K, List<V>> merge(Map<K, List<V>> to, K key, Collection<? extends V> value) {
   817         List<V> toColl = to.get(key);
   818 
   819         if (toColl == null) {
   820             to.put(key, toColl = new LinkedList<V>());
   821         }
   822 
   823         toColl.addAll(value);
   824 
   825         return to;
   826     }
   827 
   828     public static <K, V> Map<K, List<V>> mergeAll(Map<K, List<V>> to, Map<? extends K, ? extends Collection<? extends V>> what) {
   829         for (Entry<? extends K, ? extends Collection<? extends V>> e : what.entrySet()) {
   830             List<V> toColl = to.get(e.getKey());
   831 
   832             if (toColl == null) {
   833                 to.put(e.getKey(), toColl = new LinkedList<V>());
   834             }
   835 
   836             toColl.addAll(e.getValue());
   837         }
   838 
   839         return to;
   840     }
   841 
   842     public static List<ErrorDescription> join(Map<?, ? extends List<? extends ErrorDescription>> errors) {
   843         List<ErrorDescription> result = new LinkedList<ErrorDescription>();
   844 
   845         for (Entry<?, ? extends Collection<? extends ErrorDescription>> e : errors.entrySet()) {
   846             result.addAll(e.getValue());
   847         }
   848 
   849         return result;
   850     }
   851 
   852     private static final boolean logTimeSpentInHints = Boolean.getBoolean("java.HintsInvoker.time.in.hints");
   853     private final Map<String, Long> hint2SpentTime = new HashMap<String, Long>();
   854 
   855     private void reportSpentTime(String id, long nanoTime) {
   856         if (!logTimeSpentInHints) return;
   857         
   858         Long prev = hint2SpentTime.get(id);
   859 
   860         if (prev == null) {
   861             prev = (long) 0;
   862         }
   863 
   864         hint2SpentTime.put(id, prev + nanoTime);
   865     }
   866 
   867     private void dumpTimeSpentInHints() {
   868         if (!logTimeSpentInHints) return;
   869 
   870         List<Entry<String, Long>> l = new ArrayList<Entry<String, Long>>(hint2SpentTime.entrySet());
   871 
   872         Collections.sort(l, new Comparator<Entry<String, Long>>() {
   873             @Override
   874             public int compare(Entry<String, Long> o1, Entry<String, Long> o2) {
   875                 return (int) Math.signum(o1.getValue() - o2.getValue());
   876             }
   877         });
   878 
   879         for (Entry<String, Long> e : l) {
   880             System.err.println(e.getKey() + "=" + String.format("%3.2f", e.getValue() / 1000000.0));
   881         }
   882     }
   883 
   884 
   885     private void enterSpeculativeAssignments(HintContext ctx, List<Condition> conditions, Map<Tree, Map<String, Object>> marks) {
   886         for (Condition c : conditions) {
   887             if (!(c instanceof MarkCondition)) continue;
   888 
   889             MarkCondition mc = (MarkCondition) c;
   890 
   891             if (mc.op != Operator.ASSIGN) {
   892                 continue;
   893             }
   894 
   895             assert mc.left instanceof Selector;
   896 
   897             Selector s = (Selector) mc.left;
   898             String treeName = s.selected.get(0);
   899             String markName = s.selected.get(1);
   900             TreePath tree = ctx.getVariables().get(treeName);
   901 
   902             assert tree != null;
   903 
   904             System.err.println("speculative assignment to: " + tree.getLeaf());
   905 
   906             Map<String, Object> variables = marks.get(tree.getLeaf());
   907 
   908             if (variables == null) {
   909                 marks.put(tree.getLeaf(), variables = new HashMap<String, Object>());
   910             }
   911 
   912             PossibleValue pv = (PossibleValue) variables.get(markName);
   913 
   914             if (pv == null) {
   915                 variables.put(markName, pv = new PossibleValue());
   916             }
   917 
   918             pv.add(mc);
   919         }
   920     }
   921 
   922     private void clearSpeculativeAssignments(HintContext ctx, List<Condition> conditions, Map<Tree, Map<String, Object>> marks) {
   923         for (Condition c : conditions) {
   924             if (!(c instanceof MarkCondition)) continue;
   925 
   926             MarkCondition mc = (MarkCondition) c;
   927 
   928             if (mc.op != Operator.ASSIGN) {
   929                 continue;
   930             }
   931 
   932             assert mc.left instanceof Selector;
   933 
   934             Selector s = (Selector) mc.left;
   935             String treeName = s.selected.get(0);
   936             String markName = s.selected.get(1);
   937             TreePath tree = ctx.getVariables().get(treeName);
   938 
   939             assert tree != null;
   940 
   941             System.err.println("clearing speculative assignment from: " + tree.getLeaf());
   942 
   943             Map<String, Object> variables = marks.get(tree.getLeaf());
   944 
   945             assert variables != null;
   946 
   947             Object value = variables.get(markName);
   948 
   949             if (!(value instanceof PossibleValue))
   950                 continue;//XXX: correct?
   951 
   952             PossibleValue pv = (PossibleValue) value;
   953 
   954             assert pv != null;
   955 
   956             pv.remove(mc);
   957 
   958             if (pv.isEmpty()) {
   959                 variables.remove(markName);
   960             }
   961         }
   962     }
   963 
   964     /**true==accepted
   965      * false==rejected
   966      * null==nothing
   967      *
   968      * @return
   969      */
   970     private static Boolean processConditions(HintContext ctx, Map<Tree, Map<String, Object>> marks, List<Condition> cond, int candidatesCount, int confirmedCount) {
   971         if (cond.isEmpty()) {
   972             return true; //implicitly accept
   973         }
   974 
   975         for (Iterator<Condition> it = cond.iterator(); it.hasNext(); ) {
   976             Condition c = it.next();
   977 
   978             if (c instanceof CustomCondition) {
   979                 if (!((CustomCondition) c).holds(ctx)) {
   980                     return false;
   981                 }
   982                 it.remove();
   983                 continue;
   984             }
   985 
   986             if (c instanceof OtherwiseCondition) {
   987                 if (candidatesCount > 1) return null;
   988                 if (confirmedCount > 0) return false;
   989                 it.remove();
   990                 continue;
   991             }
   992 
   993             MarkCondition mc = (MarkCondition) c;
   994 
   995             switch (mc.op) {
   996                 case ASSIGN:
   997                     assert mc.left instanceof Selector;
   998 
   999                     Object value = readValue(ctx, marks, mc.right);
  1000 
  1001                     assert value != null;
  1002 
  1003                     Selector s = (Selector) mc.left;
  1004                     String treeName = s.selected.get(0);
  1005                     String markName = s.selected.get(1);
  1006                     TreePath tree = ctx.getVariables().get(treeName);
  1007 
  1008                     assert tree != null; //more gracefull handling, in some case may warn during parsing
  1009 
  1010                     Map<String, Object> variables = marks.get(tree.getLeaf());
  1011 
  1012                     if (variables == null) {
  1013                         marks.put(tree.getLeaf(), variables = new HashMap<String, Object>());
  1014                     }
  1015 
  1016                     variables.put(markName, value);
  1017                     break;
  1018                 case EQUALS: {
  1019                     Object left = readValue(ctx, marks, mc.left);
  1020                     Object right = readValue(ctx, marks, mc.right);
  1021 
  1022                     System.err.println("left=" + left);
  1023                     System.err.println("right=" + right);
  1024 
  1025                     if (left == null || right == null) {
  1026 //                        System.err.println("marks=" + marks);
  1027 //                        System.err.println("left=" + left);
  1028 //                        System.err.println("right=" + right);
  1029                         //can never be true:
  1030                         return false;
  1031                     }
  1032 
  1033                     if (left instanceof PossibleValue || right instanceof PossibleValue) {
  1034                         //nothing to set yet.
  1035                         return null;
  1036                     }
  1037 
  1038                     if (!left.equals(right)) {
  1039                         return false;
  1040                     }
  1041 
  1042                     break;
  1043                 }
  1044                 case NOT_EQUALS: {
  1045                     Object left = readValue(ctx, marks, mc.left);
  1046                     Object right = readValue(ctx, marks, mc.right);
  1047 
  1048                     if (left instanceof PossibleValue || right instanceof PossibleValue) {
  1049                         //nothing to set yet.
  1050                         return null;
  1051                     }
  1052 
  1053                     if (left == right || (left != null && left.equals(right))) {
  1054                         return false;
  1055                     }
  1056 
  1057                     break;
  1058                 }
  1059                 default:
  1060                     throw new UnsupportedOperationException();
  1061             }
  1062 
  1063             it.remove();
  1064         }
  1065 
  1066         assert cond.isEmpty();
  1067 
  1068         return true;
  1069     }
  1070 
  1071     private static Object readValue(HintContext ctx, Map<Tree, Map<String, Object>> marks, Value v) {
  1072         if (v instanceof Selector) {
  1073             Selector s = (Selector) v;
  1074 
  1075             if (s.selected.size() == 1) {
  1076                 String name = s.selected.get(0);
  1077                 TreePath tree = ctx.getVariables().get(name);
  1078 
  1079                 assert tree != null;
  1080 
  1081                 return ctx.getInfo().getTrees().getElement(tree);
  1082             }
  1083 
  1084             if (s.selected.size() == 2) {
  1085                 String treeName = s.selected.get(0);
  1086                 String markName = s.selected.get(1);
  1087                 TreePath tree = ctx.getVariables().get(treeName);
  1088 
  1089                 assert tree != null; //more gracefull handling, in some case may warn during parsing
  1090 
  1091                 System.err.println("reading=" + tree.getLeaf() + "." + markName);
  1092 
  1093                 Map<String, Object> variables = marks.get(tree.getLeaf());
  1094 
  1095                 if (variables == null) return null;
  1096 
  1097                 return variables.get(markName);
  1098             }
  1099 
  1100             assert false;
  1101         }
  1102 
  1103         //XXX: not tested!
  1104         if (v instanceof Literal) {
  1105             return ((Literal) v).value;
  1106         }
  1107 
  1108         assert false;
  1109 
  1110         return null;
  1111     }
  1112 
  1113     private static final class PossibleValue extends HashSet<MarkCondition> {}
  1114 
  1115     private static final class HintEvaluationData {
  1116         public final HintContext ctx;
  1117         public final HintDescription hd;
  1118         public final List<Condition> marks;
  1119         public final ErrorDescriptionAcceptor acceptor;
  1120         public final List<FixEvaluationData> fixDescriptions;
  1121         public final List<Fix> createdFixes = new LinkedList<Fix>();
  1122         public HintEvaluationData(HintContext ctx, HintDescription hd, List<Condition> marks, ErrorDescriptionAcceptor acceptor, List<FixEvaluationData> fixDescriptions) {
  1123             this.ctx = ctx;
  1124             this.hd = hd;
  1125             this.marks = marks;
  1126             this.acceptor = acceptor;
  1127             this.fixDescriptions = fixDescriptions;
  1128         }
  1129     }
  1130 
  1131     private static final class FixEvaluationData {
  1132         public final HintContext ctx;
  1133         public final List<Condition> marks;
  1134         public final FixAcceptor acceptor;
  1135         public FixEvaluationData(HintContext ctx, List<Condition> marks, FixAcceptor acceptor) {
  1136             this.ctx = ctx;
  1137             this.marks = marks;
  1138             this.acceptor = acceptor;
  1139         }
  1140     }
  1141 
  1142 }