cmdline/tool/src/org/netbeans/modules/jackpot30/cmdline/Main.java
author Jan Lahoda <jlahoda@netbeans.org>
Thu, 19 Dec 2013 15:43:00 +0100
branchrelease74
changeset 985 fb9da7d66c9f
parent 976 f2cfa4472600
child 996 5c2ff9a779a8
permissions -rw-r--r--
Suppress the loggers as soon as possible
     1 /*
     2  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
     3  *
     4  * Copyright 2010-2011 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 2010-2011 Sun Microsystems, Inc.
    38  */
    39 
    40 package org.netbeans.modules.jackpot30.cmdline;
    41 
    42 import java.awt.BorderLayout;
    43 import java.awt.event.ActionEvent;
    44 import java.awt.event.ActionListener;
    45 import java.io.BufferedWriter;
    46 import java.io.File;
    47 import java.io.FileOutputStream;
    48 import java.io.IOException;
    49 import java.io.OutputStreamWriter;
    50 import java.io.PrintStream;
    51 import java.io.Writer;
    52 import java.lang.reflect.InvocationTargetException;
    53 import java.net.URL;
    54 import java.util.ArrayList;
    55 import java.util.Arrays;
    56 import java.util.Collection;
    57 import java.util.HashMap;
    58 import java.util.Iterator;
    59 import java.util.LinkedList;
    60 import java.util.List;
    61 import java.util.Map;
    62 import java.util.Map.Entry;
    63 import java.util.Set;
    64 import java.util.TreeSet;
    65 import java.util.concurrent.atomic.AtomicBoolean;
    66 import java.util.logging.Level;
    67 import java.util.logging.Logger;
    68 import java.util.prefs.BackingStoreException;
    69 import java.util.prefs.Preferences;
    70 import java.util.regex.Pattern;
    71 import javax.swing.JCheckBox;
    72 import javax.swing.JDialog;
    73 import javax.swing.JOptionPane;
    74 import javax.swing.JPanel;
    75 import javax.swing.SwingUtilities;
    76 import javax.swing.event.ChangeListener;
    77 import joptsimple.ArgumentAcceptingOptionSpec;
    78 import joptsimple.OptionException;
    79 import joptsimple.OptionParser;
    80 import joptsimple.OptionSet;
    81 import org.netbeans.api.java.classpath.ClassPath;
    82 import org.netbeans.api.java.classpath.GlobalPathRegistry;
    83 import org.netbeans.api.java.source.CompilationController;
    84 import org.netbeans.api.java.source.ModificationResult;
    85 import org.netbeans.core.startup.MainLookup;
    86 import org.netbeans.modules.jackpot30.ui.settings.XMLHintPreferences;
    87 import org.netbeans.modules.java.hints.jackpot.spi.PatternConvertor;
    88 import org.netbeans.modules.java.hints.providers.spi.HintDescription;
    89 import org.netbeans.modules.java.hints.providers.spi.HintMetadata;
    90 import org.netbeans.modules.java.hints.spiimpl.MessageImpl;
    91 import org.netbeans.modules.java.hints.spiimpl.RulesManager;
    92 import org.netbeans.modules.java.hints.spiimpl.batch.BatchSearch;
    93 import org.netbeans.modules.java.hints.spiimpl.batch.BatchSearch.BatchResult;
    94 import org.netbeans.modules.java.hints.spiimpl.batch.BatchSearch.Folder;
    95 import org.netbeans.modules.java.hints.spiimpl.batch.BatchSearch.Resource;
    96 import org.netbeans.modules.java.hints.spiimpl.batch.BatchSearch.VerifiedSpansCallBack;
    97 import org.netbeans.modules.java.hints.spiimpl.batch.BatchUtilities;
    98 import org.netbeans.modules.java.hints.spiimpl.batch.ProgressHandleWrapper;
    99 import org.netbeans.modules.java.hints.spiimpl.batch.ProgressHandleWrapper.ProgressHandleAbstraction;
   100 import org.netbeans.modules.java.hints.spiimpl.batch.Scopes;
   101 import org.netbeans.modules.java.hints.spiimpl.options.HintsPanel;
   102 import org.netbeans.modules.java.hints.spiimpl.options.HintsSettings;
   103 import org.netbeans.modules.java.hints.spiimpl.refactoring.Utilities.ClassPathBasedHintWrapper;
   104 import org.netbeans.modules.java.source.parsing.JavaPathRecognizer;
   105 import org.netbeans.modules.parsing.impl.indexing.CacheFolder;
   106 import org.netbeans.modules.parsing.impl.indexing.RepositoryUpdater;
   107 import org.netbeans.spi.editor.hints.ErrorDescription;
   108 import org.netbeans.spi.java.classpath.ClassPathProvider;
   109 import org.netbeans.spi.java.classpath.support.ClassPathSupport;
   110 import org.netbeans.spi.java.hints.Hint.Kind;
   111 import org.netbeans.spi.java.queries.SourceLevelQueryImplementation2;
   112 import org.openide.filesystems.FileObject;
   113 import org.openide.filesystems.FileStateInvalidException;
   114 import org.openide.filesystems.FileUtil;
   115 import org.openide.util.Exceptions;
   116 import org.openide.util.Lookup;
   117 import org.openide.util.NbPreferences;
   118 import org.openide.util.RequestProcessor;
   119 import org.openide.util.lookup.Lookups;
   120 import org.openide.util.lookup.ProxyLookup;
   121 import org.openide.util.lookup.ServiceProvider;
   122 
   123 /**
   124  *
   125  * @author lahvac
   126  */
   127 public class Main {
   128 
   129     private static final String OPTION_APPLY = "apply";
   130     private static final String OPTION_NO_APPLY = "no-apply";
   131     private static final String SOURCE_LEVEL_DEFAULT = "1.7";
   132     private static final String ACCEPTABLE_SOURCE_LEVEL_PATTERN = "(1\\.)?[2-9][0-9]*";
   133     
   134     public static void main(String... args) throws IOException, ClassNotFoundException {
   135         System.exit(compile(args));
   136     }
   137 
   138     public static int compile(String... args) throws IOException, ClassNotFoundException {
   139         System.setProperty("netbeans.user", "/tmp/tmp-foo");
   140 
   141         OptionParser parser = new OptionParser();
   142 //        ArgumentAcceptingOptionSpec<File> projects = parser.accepts("project", "project(s) to refactor").withRequiredArg().withValuesSeparatedBy(File.pathSeparatorChar).ofType(File.class);
   143         ArgumentAcceptingOptionSpec<File> classpath = parser.accepts("classpath", "classpath").withRequiredArg().withValuesSeparatedBy(File.pathSeparatorChar).ofType(File.class);
   144         ArgumentAcceptingOptionSpec<File> bootclasspath = parser.accepts("bootclasspath", "bootclasspath").withRequiredArg().withValuesSeparatedBy(File.pathSeparatorChar).ofType(File.class);
   145         ArgumentAcceptingOptionSpec<File> sourcepath = parser.accepts("sourcepath", "sourcepath").withRequiredArg().withValuesSeparatedBy(File.pathSeparatorChar).ofType(File.class);
   146         ArgumentAcceptingOptionSpec<File> cache = parser.accepts("cache", "a cache directory to store working data").withRequiredArg().ofType(File.class);
   147         ArgumentAcceptingOptionSpec<File> out = parser.accepts("out", "output diff").withRequiredArg().ofType(File.class);
   148         ArgumentAcceptingOptionSpec<File> configFile = parser.accepts("config-file", "configuration file").withRequiredArg().ofType(File.class);
   149         ArgumentAcceptingOptionSpec<String> hint = parser.accepts("hint", "hint name").withRequiredArg().ofType(String.class);
   150         ArgumentAcceptingOptionSpec<String> config = parser.accepts("config", "configurations").withRequiredArg().ofType(String.class);
   151         ArgumentAcceptingOptionSpec<String> source = parser.accepts("source", "source level").withRequiredArg().ofType(String.class).defaultsTo(SOURCE_LEVEL_DEFAULT);
   152         ArgumentAcceptingOptionSpec<File> hintFile = parser.accepts("hint-file", "file with rules that should be performed").withRequiredArg().ofType(File.class);
   153 
   154         parser.accepts("list", "list all known hints");
   155         parser.accepts("progress", "show progress");
   156         parser.accepts("debug", "enable debugging loggers");
   157         parser.accepts("help", "prints this help");
   158         parser.accepts(OPTION_NO_APPLY, "do not apply changes - only print locations were the hint would be applied");
   159         parser.accepts(OPTION_APPLY, "apply changes");
   160         parser.accepts("show-gui", "show configuration dialog");
   161 
   162         OptionSet parsed;
   163 
   164         try {
   165             parsed = parser.parse(args);
   166         } catch (OptionException ex) {
   167             System.err.println(ex.getLocalizedMessage());
   168             parser.printHelpOn(System.out);
   169             return 1;
   170         }
   171 
   172         if (!parsed.has("debug")) {
   173             prepareLoggers();
   174         }
   175 
   176         if (parsed.has("help")) {
   177             parser.printHelpOn(System.out);
   178             return 0;
   179         }
   180 
   181         List<FileObject> roots = new ArrayList<FileObject>();
   182         List<Folder> rootFolders = new ArrayList<Folder>();
   183 
   184         for (String sr : parsed.nonOptionArguments()) {
   185             File r = new File(sr);
   186             FileObject root = FileUtil.toFileObject(r);
   187 
   188             if (root != null) {
   189                 roots.add(root);
   190                 rootFolders.add(new Folder(root));
   191             }
   192         }
   193 
   194         ClassPath bootCP = createClassPath(parsed.has(bootclasspath) ? parsed.valuesOf(bootclasspath) : null, createDefaultBootClassPath());
   195         ClassPath compileCP = createClassPath(parsed.has(classpath) ? parsed.valuesOf(classpath) : null, ClassPath.EMPTY);
   196         final ClassPath sourceCP = createClassPath(parsed.has(sourcepath) ? parsed.valuesOf(sourcepath) : null, ClassPathSupport.createClassPath(roots.toArray(new FileObject[0])));
   197         final ClassPath binaryCP = ClassPathSupport.createProxyClassPath(bootCP, compileCP);
   198 
   199         if (parsed.has("show-gui")) {
   200             if (parsed.has(configFile)) {
   201                 final File settingsFile = parsed.valueOf(configFile);
   202                 try {
   203                     SwingUtilities.invokeAndWait(new Runnable() {
   204                         @Override public void run() {
   205                             try {
   206                                 showGUICustomizer(settingsFile, binaryCP, sourceCP);
   207                             } catch (IOException ex) {
   208                                 Exceptions.printStackTrace(ex);
   209                             } catch (BackingStoreException ex) {
   210                                 Exceptions.printStackTrace(ex);
   211                             }
   212                         }
   213                     });
   214                 } catch (InterruptedException ex) {
   215                     Exceptions.printStackTrace(ex);
   216                 } catch (InvocationTargetException ex) {
   217                     Exceptions.printStackTrace(ex);
   218                 }
   219 
   220                 return 0;
   221             } else {
   222                 System.err.println("show-gui requires config-file");
   223                 return 1;
   224             }
   225         }
   226 
   227         File cacheDir = parsed.valueOf(cache);
   228         boolean deleteCacheDir = false;
   229 
   230         try {
   231             if (cacheDir == null) {
   232                 cacheDir = File.createTempFile("jackpot", "cache");
   233                 cacheDir.delete();
   234                 if (!(deleteCacheDir = cacheDir.mkdirs())) {
   235                     System.err.println("cannot create temporary cache");
   236                     return 1;
   237                 }
   238             }
   239 
   240             if (cacheDir.isFile()) {
   241                 System.err.println("cache directory exists and is a file");
   242                 return 1;
   243             }
   244 
   245             String[] cacheDirContent = cacheDir.list();
   246 
   247             if (cacheDirContent != null && cacheDirContent.length > 0 && !new File(cacheDir, "segments").exists()) {
   248                 System.err.println("cache directory is not empty, but was not created by this tool");
   249                 return 1;
   250             }
   251 
   252             cacheDir.mkdirs();
   253 
   254             CacheFolder.setCacheFolder(FileUtil.toFileObject(FileUtil.normalizeFile(cacheDir)));
   255 
   256             org.netbeans.api.project.ui.OpenProjects.getDefault().getOpenProjects();
   257             RepositoryUpdater.getDefault().start(false);
   258 
   259             if (roots.isEmpty() && !parsed.has("list")) {
   260                 System.err.println("no source roots to work on");
   261                 return 1;
   262             }
   263 
   264             Iterable<? extends HintDescription> hints;
   265             
   266             if (parsed.has("list")) {
   267                 printHints(sourceCP, binaryCP);
   268                 return 0;
   269             }
   270 
   271             Preferences settingsFromConfigFile;
   272             Preferences hintSettingsPreferences;
   273             HintsSettings hintSettings;
   274             boolean apply;
   275 
   276             if (parsed.has(configFile)) {
   277                 settingsFromConfigFile = XMLHintPreferences.from(parsed.valueOf(configFile));
   278                 hintSettings = HintsSettings.createPreferencesBasedHintsSettings(hintSettingsPreferences = settingsFromConfigFile.node("settings"), false, null);
   279                 apply = settingsFromConfigFile.getBoolean("apply", false);
   280             } else {
   281                 settingsFromConfigFile = null;
   282                 hintSettings = HintsSettings.createPreferencesBasedHintsSettings(hintSettingsPreferences = NbPreferences.root().node("tempSettings"), false, null);
   283                 apply = false;
   284             }
   285 
   286             if (parsed.has(hint)) {
   287                 if (settingsFromConfigFile != null) {
   288                     System.err.println("cannot specify --hint and --config-file together");
   289                     return 1;
   290                 }
   291                 hints = findHints(sourceCP, binaryCP, parsed.valueOf(hint), hintSettings);
   292             } else if (parsed.has(hintFile)) {
   293                 if (settingsFromConfigFile != null) {
   294                     System.err.println("cannot specify --hint-file and --config-file together");
   295                     return 1;
   296                 }
   297                 FileObject hintFileFO = FileUtil.toFileObject(parsed.valueOf(hintFile));
   298                 assert hintFileFO != null;
   299                 hints = PatternConvertor.create(hintFileFO.asText());
   300                 for (HintDescription hd : hints) {
   301                     hintSettings.setEnabled(hd.getMetadata(), true);
   302                 }
   303             } else {
   304                 hints = readHints(sourceCP, binaryCP, hintSettings, hintSettingsPreferences, settingsFromConfigFile != null ? settingsFromConfigFile.getBoolean("runDeclarative", true) : true);
   305             }
   306 
   307             if (parsed.has(config) && !parsed.has(hint)) {
   308                 System.err.println("--config cannot specified when no hint is specified");
   309                 return 1;
   310             }
   311 
   312             if (parsed.has(config)) {
   313                 Iterator<? extends HintDescription> hit = hints.iterator();
   314                 HintDescription hd = hit.next();
   315 
   316                 if (hit.hasNext()) {
   317                     System.err.println("--config cannot specified when more than one hint is specified");
   318 
   319                     return 1;
   320                 }
   321 
   322                 Preferences prefs = hintSettings.getHintPreferences(hd.getMetadata());
   323 
   324                 boolean stop = false;
   325 
   326                 for (String c : parsed.valuesOf(config)) {
   327                     int assign = c.indexOf('=');
   328 
   329                     if (assign == (-1)) {
   330                         System.err.println("configuration option is missing '=' (" + c + ")");
   331                         stop = true;
   332                         continue;
   333                     }
   334 
   335                     prefs.put(c.substring(0, assign), c.substring(assign + 1));
   336                 }
   337 
   338                 if (stop) {
   339                     return 1;
   340                 }
   341             }
   342 
   343             String sourceLevel = parsed.valueOf(source);
   344 
   345             if (!Pattern.compile(ACCEPTABLE_SOURCE_LEVEL_PATTERN).matcher(sourceLevel).matches()) {
   346                 System.err.println("unrecognized source level specification: " + sourceLevel);
   347                 return 1;
   348             }
   349 
   350             if (parsed.has(OPTION_NO_APPLY)) {
   351                 apply = false;
   352             } else if (parsed.has(OPTION_APPLY)) {
   353                 apply = true;
   354             }
   355             
   356             if (apply && !hints.iterator().hasNext()) {
   357                 System.err.println("no hints specified");
   358                 return 1;
   359             }
   360 
   361             Object[] register2Lookup = new Object[] {
   362                 new ClassPathProviderImpl(bootCP, compileCP, sourceCP),
   363                 new JavaPathRecognizer(),
   364                 new SourceLevelQueryImpl(sourceCP, sourceLevel)
   365             };
   366 
   367             try {
   368                 for (Object toRegister : register2Lookup) {
   369                     MainLookup.register(toRegister);
   370                 }
   371 
   372                 ProgressHandleWrapper progress = parsed.has("progress") ? new ProgressHandleWrapper(new ConsoleProgressHandleAbstraction(), 1) : new ProgressHandleWrapper(1);
   373 
   374                 if (apply) {
   375                     apply(hints, rootFolders.toArray(new Folder[0]), progress, hintSettings, parsed.valueOf(out));
   376                 } else {
   377                     findOccurrences(hints, rootFolders.toArray(new Folder[0]), progress, hintSettings, parsed.valueOf(out));
   378                 }
   379             } catch (Throwable e) {
   380                 e.printStackTrace();
   381             } finally {
   382                 for (Object toUnRegister : register2Lookup) {
   383                     MainLookup.unregister(toUnRegister);
   384                 }
   385             }
   386         } finally {
   387             if (deleteCacheDir) {
   388                 FileObject cacheDirFO = FileUtil.toFileObject(cacheDir);
   389 
   390                 if (cacheDirFO != null) {
   391                     //TODO: would be better to do j.i.File.delete():
   392                     cacheDirFO.delete();
   393                 }
   394             }
   395         }
   396 
   397         return 0;
   398     }
   399 
   400     private static Map<HintMetadata, Collection<? extends HintDescription>> listHints(ClassPath sourceFrom, ClassPath binaryFrom) {
   401         Map<HintMetadata, Collection<? extends HintDescription>> result = new HashMap<HintMetadata, Collection<? extends HintDescription>>();
   402 
   403         for (Entry<HintMetadata, ? extends Collection<? extends HintDescription>> entry: RulesManager.getInstance().readHints(null, Arrays.asList(sourceFrom, binaryFrom), null).entrySet()) {
   404             result.put(entry.getKey(), entry.getValue());
   405         }
   406 
   407         return result;
   408     }
   409     
   410     private static Iterable<? extends HintDescription> findHints(ClassPath sourceFrom, ClassPath binaryFrom, String name, HintsSettings toEnableIn) {
   411         List<HintDescription> descs = new LinkedList<HintDescription>();
   412 
   413         for (Entry<HintMetadata, Collection<? extends HintDescription>> e : listHints(sourceFrom, binaryFrom).entrySet()) {
   414             if (e.getKey().displayName.equals(name)) {
   415                 descs.addAll(e.getValue());
   416                 toEnableIn.setEnabled(e.getKey(), true);
   417             }
   418         }
   419 
   420         return descs;
   421     }
   422 
   423     private static Iterable<? extends HintDescription> allHints(ClassPath sourceFrom, ClassPath binaryFrom, HintsSettings toEnableIn) {
   424         List<HintDescription> descs = new LinkedList<HintDescription>();
   425 
   426         for (Entry<HintMetadata, Collection<? extends HintDescription>> e : listHints(sourceFrom, binaryFrom).entrySet()) {
   427             if (e.getKey().kind != Kind.INSPECTION) continue;
   428             if (!e.getKey().enabled) continue;
   429             descs.addAll(e.getValue());
   430             toEnableIn.setEnabled(e.getKey(), true);
   431         }
   432 
   433         return descs;
   434     }
   435 
   436     private static Iterable<? extends HintDescription> readHints(ClassPath sourceFrom, ClassPath binaryFrom, HintsSettings toEnableIn, Preferences toEnableInPreferencesHack, boolean declarativeEnabledByDefault) {
   437         Map<HintMetadata, ? extends Collection<? extends HintDescription>> hardcoded = RulesManager.getInstance().readHints(null, Arrays.<ClassPath>asList(), null);
   438         Map<HintMetadata, ? extends Collection<? extends HintDescription>> all = RulesManager.getInstance().readHints(null, Arrays.asList(sourceFrom, binaryFrom), null);
   439         List<HintDescription> descs = new LinkedList<HintDescription>();
   440 
   441         for (Entry<HintMetadata, ? extends Collection<? extends HintDescription>> entry: all.entrySet()) {
   442             if (hardcoded.containsKey(entry.getKey())) {
   443                 if (toEnableIn.isEnabled(entry.getKey())) {
   444                     descs.addAll(entry.getValue());
   445                 }
   446             } else {
   447                 if (/*XXX: hack*/toEnableInPreferencesHack.node(entry.getKey().id).getBoolean("enabled", declarativeEnabledByDefault)) {
   448                     descs.addAll(entry.getValue());
   449                 }
   450             }
   451         }
   452 
   453         return descs;
   454     }
   455 
   456     private static final Logger TOP_LOGGER = Logger.getLogger("");
   457 
   458     private static void prepareLoggers() {
   459         TOP_LOGGER.setLevel(Level.OFF);
   460         System.setProperty("RepositoryUpdate.increasedLogLevel", "OFF");
   461     }
   462     
   463     private static void findOccurrences(Iterable<? extends HintDescription> descs, Folder[] sourceRoot, ProgressHandleWrapper progress, HintsSettings settings, File out) throws IOException {
   464         final Map<String, String> id2DisplayName = new HashMap<String, String>();
   465 
   466         for (HintDescription hd : descs) {
   467             if (hd.getMetadata() != null) {
   468                 id2DisplayName.put(hd.getMetadata().id, hd.getMetadata().displayName);
   469             }
   470         }
   471 
   472         ProgressHandleWrapper w = progress.startNextPartWithEmbedding(1, 1);
   473         BatchResult occurrences = BatchSearch.findOccurrences(descs, Scopes.specifiedFoldersScope(sourceRoot), w, settings);
   474 
   475         List<MessageImpl> problems = new LinkedList<MessageImpl>();
   476         BatchSearch.getVerifiedSpans(occurrences, progress, new VerifiedSpansCallBack() {
   477             @Override public void groupStarted() {}
   478             @Override public boolean spansVerified(CompilationController wc, Resource r, Collection<? extends ErrorDescription> hints) throws Exception {
   479                 for (ErrorDescription ed : hints) {
   480                     print(ed, id2DisplayName);
   481                 }
   482                 return true;
   483             }
   484             @Override public void groupFinished() {}
   485             @Override public void cannotVerifySpan(Resource r) {
   486                 //TODO: ignored - what to do?
   487             }
   488         }, problems, new AtomicBoolean());
   489     }
   490 
   491     private static void print(ErrorDescription error, Map<String, String> id2DisplayName) throws IOException {
   492         int lineNumber = error.getRange().getBegin().getLine();
   493         String line = error.getFile().asLines().get(lineNumber);
   494         int column = error.getRange().getBegin().getColumn();
   495         StringBuilder b = new StringBuilder();
   496 
   497         for (int i = 0; i < column; i++) {
   498             if (Character.isWhitespace(line.charAt(i))) {
   499                 b.append(line.charAt(i));
   500             } else {
   501                 b.append(' ');
   502             }
   503         }
   504 
   505         b.append('^');
   506 
   507         String id = error.getId();
   508 
   509         if (id != null && id.startsWith("text/x-java:")) {
   510             id = id.substring("text/x-java:".length());
   511         }
   512 
   513         String idDisplayName = id2DisplayName.get(id);
   514 
   515         if (idDisplayName == null) {
   516             idDisplayName = "unknown";
   517         }
   518 
   519         for (Entry<String, String> remap : toIdRemap.entrySet()) {
   520             idDisplayName = idDisplayName.replace(remap.getKey(), remap.getValue());
   521         }
   522 
   523         idDisplayName = idDisplayName.replaceAll("[^A-Za-z0-9]", "_").replaceAll("_+", "_");
   524 
   525         idDisplayName = "[" + idDisplayName + "] ";
   526 
   527         System.out.println(FileUtil.getFileDisplayName(error.getFile()) + ":" + (lineNumber + 1) + ": warning: " + idDisplayName + error.getDescription());
   528         System.out.println(line);
   529         System.out.println(b);
   530     }
   531 
   532     private static final Map<String, String> toIdRemap = new HashMap<String, String>() {{
   533         put("==", "equals");
   534         put("!=", "not_equals");
   535     }};
   536 
   537     private static void apply(Iterable<? extends HintDescription> descs, Folder[] sourceRoot, ProgressHandleWrapper progress, HintsSettings settings, File out) throws IOException {
   538         ProgressHandleWrapper w = progress.startNextPartWithEmbedding(1, 1);
   539         BatchResult occurrences = BatchSearch.findOccurrences(descs, Scopes.specifiedFoldersScope(sourceRoot), w, settings);
   540 
   541         List<MessageImpl> problems = new LinkedList<MessageImpl>();
   542         Collection<ModificationResult> diffs = BatchUtilities.applyFixes(occurrences, w, new AtomicBoolean(), problems);
   543 
   544         if (out != null) {
   545             Writer outS = null;
   546 
   547             try {
   548                 outS = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(out)));
   549 
   550                 for (ModificationResult mr : diffs) {
   551                     org.netbeans.modules.jackpot30.indexing.batch.BatchUtilities.exportDiff(mr, null, outS);
   552                 }
   553             } finally {
   554                 try {
   555                     outS.close();
   556                 } catch (IOException ex) {
   557                     Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
   558                 }
   559             }
   560         } else {
   561             for (ModificationResult mr : diffs) {
   562                 mr.commit();
   563             }
   564         }
   565     }
   566 
   567     private static void printHints(ClassPath sourceFrom, ClassPath binaryFrom) throws IOException {
   568         Set<String> hints = new TreeSet<String>();
   569 
   570         for (Entry<HintMetadata, Collection<? extends HintDescription>> e : listHints(sourceFrom, binaryFrom).entrySet()) {
   571             hints.add(e.getKey().displayName);
   572         }
   573 
   574         for (String h : hints) {
   575             System.out.println(h);
   576         }
   577     }
   578 
   579     private static ClassPath createDefaultBootClassPath() throws IOException {
   580         try {
   581             String cp = System.getProperty("sun.boot.class.path");
   582             List<URL> urls = new ArrayList<URL>();
   583             String[] paths = cp.split(Pattern.quote(System.getProperty("path.separator")));
   584 
   585             for (String path : paths) {
   586                 File f = new File(path);
   587 
   588                 if (!f.canRead())
   589                     continue;
   590 
   591                 FileObject fo = FileUtil.toFileObject(FileUtil.normalizeFile(f));
   592 
   593                 if (FileUtil.isArchiveFile(fo)) {
   594                     fo = FileUtil.getArchiveRoot(fo);
   595                 }
   596 
   597                 if (fo != null) {
   598                     urls.add(fo.getURL());
   599                 }
   600             }
   601 
   602             return ClassPathSupport.createClassPath(urls.toArray(new URL[0]));
   603         } catch (FileStateInvalidException e) {
   604             throw e;
   605         }
   606     }
   607 
   608     private static ClassPath createClassPath(Iterable<? extends File> roots, ClassPath def) {
   609         if (roots == null) return def;
   610 
   611         List<URL> rootURLs = new ArrayList<URL>();
   612 
   613         for (File r : roots) {
   614             rootURLs.add(FileUtil.urlForArchiveOrDir(r));
   615         }
   616 
   617         return ClassPathSupport.createClassPath(rootURLs.toArray(new URL[0]));
   618     }
   619 
   620     private static void showGUICustomizer(File settingsFile, ClassPath binaryCP, ClassPath sourceCP) throws IOException, BackingStoreException {
   621         GlobalPathRegistry.getDefault().register(ClassPath.COMPILE, new ClassPath[] {binaryCP});
   622         GlobalPathRegistry.getDefault().register(ClassPath.SOURCE, new ClassPath[] {sourceCP});
   623         ClassPathBasedHintWrapper hints = new ClassPathBasedHintWrapper();
   624         final Preferences p = XMLHintPreferences.from(settingsFile);
   625         JPanel hintPanel = new HintsPanel(p.node("settings"), hints, true);
   626         final JCheckBox runDeclarativeHints = new JCheckBox("Always Run Declarative Rules");
   627 
   628         runDeclarativeHints.setToolTipText("Always run the declarative rules found on classpath? (Only those selected above will be run when unchecked.)");
   629         runDeclarativeHints.setSelected(p.getBoolean("runDeclarative", true));
   630         runDeclarativeHints.addActionListener(new ActionListener() {
   631             @Override public void actionPerformed(ActionEvent e) {
   632                 p.putBoolean("runDeclarative", runDeclarativeHints.isSelected());
   633             }
   634         });
   635 
   636         JPanel customizer = new JPanel(new BorderLayout());
   637 
   638         customizer.add(hintPanel, BorderLayout.CENTER);
   639         customizer.add(runDeclarativeHints, BorderLayout.SOUTH);
   640         JOptionPane jop = new JOptionPane(customizer, JOptionPane.PLAIN_MESSAGE);
   641         JDialog dialog = jop.createDialog("Select Hints");
   642 
   643         jop.selectInitialValue();
   644         dialog.setVisible(true);
   645         dialog.dispose();
   646 
   647         Object result = jop.getValue();
   648 
   649         if (result.equals(JOptionPane.OK_OPTION)) {
   650             p.flush();
   651         }
   652     }
   653 
   654     @ServiceProvider(service=Lookup.class)
   655     public static final class LookupProviderImpl extends ProxyLookup {
   656 
   657         public LookupProviderImpl() {
   658             super(Lookups.forPath("Services/AntBasedProjectTypes"));
   659         }
   660     }
   661 
   662     public static final class ClassPathProviderImpl implements ClassPathProvider {
   663         private final ClassPath boot;
   664         private final ClassPath compile;
   665         private final ClassPath source;
   666 
   667         public ClassPathProviderImpl(ClassPath boot, ClassPath compile, ClassPath source) {
   668             this.boot = boot;
   669             this.compile = compile;
   670             this.source = source;
   671         }
   672 
   673         @Override
   674         public ClassPath findClassPath(FileObject file, String type) {
   675             if (source.findOwnerRoot(file) != null) {
   676                 if (ClassPath.BOOT.equals(type)) {
   677                     return boot;
   678                 } else if (ClassPath.COMPILE.equals(type)) {
   679                     return compile;
   680                 } else  if (ClassPath.SOURCE.equals(type)) {
   681                     return source;
   682                 }
   683             }
   684 
   685             return null;
   686         }
   687     }
   688 
   689     public static final class SourceLevelQueryImpl implements SourceLevelQueryImplementation2 {
   690         private final ClassPath sourceCP;
   691         private final Result sourceLevel;
   692 
   693         public SourceLevelQueryImpl(ClassPath sourceCP, final String sourceLevel) {
   694             this.sourceCP = sourceCP;
   695             this.sourceLevel = new Result() {
   696                 @Override public String getSourceLevel() {
   697                     return sourceLevel;
   698                 }
   699                 @Override public void addChangeListener(ChangeListener listener) {}
   700                 @Override public void removeChangeListener(ChangeListener listener) {}
   701             };
   702         }
   703 
   704         @Override
   705         public Result getSourceLevel(FileObject javaFile) {
   706             if (sourceCP.findOwnerRoot(javaFile) != null) {
   707                 return sourceLevel;
   708             } else {
   709                 return null;
   710             }
   711         }
   712 
   713     }
   714 
   715     private static final class ConsoleProgressHandleAbstraction implements ProgressHandleAbstraction {
   716 
   717         private final int width = 80;
   718 
   719         private int total = -1;
   720         private int current = 0;
   721 
   722         public ConsoleProgressHandleAbstraction() {
   723         }
   724 
   725         @Override
   726         public synchronized void start(int totalWork) {
   727             if (total != (-1)) throw new UnsupportedOperationException();
   728             total = totalWork;
   729             update();
   730         }
   731 
   732         @Override
   733         public synchronized void progress(int currentWorkDone) {
   734             current = currentWorkDone;
   735             update();
   736         }
   737 
   738         @Override
   739         public void progress(String message) {
   740         }
   741 
   742         @Override
   743         public void finish() {
   744         }
   745 
   746         private void update() {
   747             RequestProcessor.getDefault().post(new Runnable() {
   748 
   749                 @Override
   750                 public void run() {
   751                     doUpdate();
   752                 }
   753             });
   754         }
   755 
   756         private int currentShownDone = -1;
   757 
   758         private void doUpdate() {
   759             int done;
   760 
   761             synchronized(this) {
   762                 done = (int) ((((double) width - 2) / total) * current);
   763 
   764                 if (done == currentShownDone) {
   765                     return;
   766                 }
   767 
   768                 currentShownDone = done;
   769             }
   770             
   771             int todo = width - done;
   772             PrintStream pw = System.out;
   773 
   774             pw.print("[");
   775 
   776 
   777             while (done-- > 0) {
   778                 pw.print("=");
   779             }
   780 
   781             while (todo-- > 0) {
   782                 pw.print(" ");
   783             }
   784 
   785             pw.print("]\r");
   786         }
   787 
   788     }
   789 
   790 }