You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@netbeans.apache.org by po...@apache.org on 2017/09/03 17:33:36 UTC

[43/51] [partial] incubator-netbeans-jackpot30 git commit: INFRA-15006 Import for http://bits.netbeans.org/download/apache-donation

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-jackpot30/blob/9ed0a377/cmdline/tool/src/org/netbeans/modules/jackpot30/cmdline/Main.java
----------------------------------------------------------------------
diff --git a/cmdline/tool/src/org/netbeans/modules/jackpot30/cmdline/Main.java b/cmdline/tool/src/org/netbeans/modules/jackpot30/cmdline/Main.java
new file mode 100644
index 0000000..dc761d6
--- /dev/null
+++ b/cmdline/tool/src/org/netbeans/modules/jackpot30/cmdline/Main.java
@@ -0,0 +1,1090 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2010-2011 Sun Microsystems, Inc. All rights reserved.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ *
+ * Contributor(s):
+ *
+ * Portions Copyrighted 2010-2011 Sun Microsystems, Inc.
+ */
+
+package org.netbeans.modules.jackpot30.cmdline;
+
+import java.awt.BorderLayout;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.io.PrintStream;
+import java.io.Writer;
+import java.lang.reflect.InvocationTargetException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.TreeSet;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import java.util.prefs.AbstractPreferences;
+import java.util.prefs.BackingStoreException;
+import java.util.prefs.Preferences;
+import java.util.regex.Pattern;
+import javax.swing.JCheckBox;
+import javax.swing.JDialog;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+import javax.swing.SwingUtilities;
+import javax.swing.event.ChangeListener;
+import joptsimple.ArgumentAcceptingOptionSpec;
+import joptsimple.OptionException;
+import joptsimple.OptionParser;
+import joptsimple.OptionSet;
+import org.netbeans.api.java.classpath.ClassPath;
+import org.netbeans.api.java.classpath.GlobalPathRegistry;
+import org.netbeans.api.java.source.CompilationController;
+import org.netbeans.api.java.source.ModificationResult;
+import org.netbeans.core.startup.MainLookup;
+import org.netbeans.modules.jackpot30.cmdline.lib.Utils;
+import org.netbeans.modules.jackpot30.ui.settings.XMLHintPreferences;
+import org.netbeans.modules.java.hints.declarative.DeclarativeHintRegistry;
+import org.netbeans.modules.java.hints.declarative.test.TestParser;
+import org.netbeans.modules.java.hints.declarative.test.TestParser.TestCase;
+import org.netbeans.modules.java.hints.declarative.test.TestPerformer;
+import org.netbeans.modules.java.hints.jackpot.spi.PatternConvertor;
+import org.netbeans.modules.java.hints.providers.spi.HintDescription;
+import org.netbeans.modules.java.hints.providers.spi.HintMetadata;
+import org.netbeans.modules.java.hints.spiimpl.MessageImpl;
+import org.netbeans.modules.java.hints.spiimpl.RulesManager;
+import org.netbeans.modules.java.hints.spiimpl.batch.BatchSearch;
+import org.netbeans.modules.java.hints.spiimpl.batch.BatchSearch.BatchResult;
+import org.netbeans.modules.java.hints.spiimpl.batch.BatchSearch.Folder;
+import org.netbeans.modules.java.hints.spiimpl.batch.BatchSearch.Resource;
+import org.netbeans.modules.java.hints.spiimpl.batch.BatchSearch.VerifiedSpansCallBack;
+import org.netbeans.modules.java.hints.spiimpl.batch.BatchUtilities;
+import org.netbeans.modules.java.hints.spiimpl.batch.ProgressHandleWrapper;
+import org.netbeans.modules.java.hints.spiimpl.batch.ProgressHandleWrapper.ProgressHandleAbstraction;
+import org.netbeans.modules.java.hints.spiimpl.batch.Scopes;
+import org.netbeans.modules.java.hints.spiimpl.options.HintsPanel;
+import org.netbeans.modules.java.hints.spiimpl.options.HintsSettings;
+import org.netbeans.modules.java.hints.spiimpl.refactoring.Utilities.ClassPathBasedHintWrapper;
+import org.netbeans.modules.java.source.parsing.JavaPathRecognizer;
+import org.netbeans.modules.parsing.impl.indexing.CacheFolder;
+import org.netbeans.modules.parsing.impl.indexing.RepositoryUpdater;
+import org.netbeans.spi.editor.hints.ErrorDescription;
+import org.netbeans.spi.editor.hints.ErrorDescriptionFactory;
+import org.netbeans.spi.editor.hints.Fix;
+import org.netbeans.spi.editor.hints.Severity;
+import org.netbeans.spi.java.classpath.ClassPathProvider;
+import org.netbeans.spi.java.classpath.support.ClassPathSupport;
+import org.netbeans.spi.java.hints.Hint.Kind;
+import org.netbeans.spi.java.queries.SourceLevelQueryImplementation2;
+import org.openide.filesystems.FileObject;
+import org.openide.filesystems.FileStateInvalidException;
+import org.openide.filesystems.FileUtil;
+import org.openide.util.Exceptions;
+import org.openide.util.Lookup;
+import org.openide.util.Pair;
+import org.openide.util.RequestProcessor;
+import org.openide.util.lookup.Lookups;
+import org.openide.util.lookup.ProxyLookup;
+import org.openide.util.lookup.ServiceProvider;
+
+/**
+ *
+ * @author lahvac
+ */
+public class Main {
+
+    private static final String OPTION_APPLY = "apply";
+    private static final String OPTION_NO_APPLY = "no-apply";
+    private static final String OPTION_FAIL_ON_WARNINGS = "fail-on-warnings";
+    private static final String RUN_TESTS = "run-tests";
+    private static final String SOURCE_LEVEL_DEFAULT = "1.7";
+    private static final String ACCEPTABLE_SOURCE_LEVEL_PATTERN = "(1\\.)?[2-9][0-9]*";
+    
+    public static void main(String... args) throws IOException, ClassNotFoundException {
+        System.exit(compile(args));
+    }
+
+    public static int compile(String... args) throws IOException, ClassNotFoundException {
+        System.setProperty("netbeans.user", "/tmp/tmp-foo");
+
+        OptionParser parser = new OptionParser();
+//        ArgumentAcceptingOptionSpec<File> projects = parser.accepts("project", "project(s) to refactor").withRequiredArg().withValuesSeparatedBy(File.pathSeparatorChar).ofType(File.class);
+        GroupOptions globalGroupOptions = setupGroupParser(parser);
+        ArgumentAcceptingOptionSpec<File> cache = parser.accepts("cache", "a cache directory to store working data").withRequiredArg().ofType(File.class);
+        ArgumentAcceptingOptionSpec<File> out = parser.accepts("out", "output diff").withRequiredArg().ofType(File.class);
+        ArgumentAcceptingOptionSpec<File> configFile = parser.accepts("config-file", "configuration file").withRequiredArg().ofType(File.class);
+        ArgumentAcceptingOptionSpec<String> hint = parser.accepts("hint", "hint name").withRequiredArg().ofType(String.class);
+        ArgumentAcceptingOptionSpec<String> config = parser.accepts("config", "configurations").withRequiredArg().ofType(String.class);
+        ArgumentAcceptingOptionSpec<File> hintFile = parser.accepts("hint-file", "file with rules that should be performed").withRequiredArg().ofType(File.class);
+        ArgumentAcceptingOptionSpec<String> group = parser.accepts("group", "specify roots to process alongside with their classpath").withRequiredArg().ofType(String.class);
+
+        parser.accepts("list", "list all known hints");
+        parser.accepts("progress", "show progress");
+        parser.accepts("debug", "enable debugging loggers");
+        parser.accepts("help", "prints this help");
+        parser.accepts(OPTION_NO_APPLY, "do not apply changes - only print locations were the hint would be applied");
+        parser.accepts(OPTION_APPLY, "apply changes");
+        parser.accepts("show-gui", "show configuration dialog");
+        parser.accepts(OPTION_FAIL_ON_WARNINGS, "fail when warnings are detected");
+        parser.accepts(RUN_TESTS, "run tests for declarative rules that were used");
+
+        OptionSet parsed;
+
+        try {
+            parsed = parser.parse(args);
+        } catch (OptionException ex) {
+            System.err.println(ex.getLocalizedMessage());
+            parser.printHelpOn(System.out);
+            return 1;
+        }
+
+        if (!parsed.has("debug")) {
+            prepareLoggers();
+        }
+
+        if (parsed.has("help")) {
+            parser.printHelpOn(System.out);
+            return 0;
+        }
+
+        List<FileObject> roots = new ArrayList<FileObject>();
+        List<Folder> rootFolders = new ArrayList<Folder>();
+
+        for (String sr : parsed.nonOptionArguments()) {
+            File r = new File(sr);
+            FileObject root = FileUtil.toFileObject(r);
+
+            if (root != null) {
+                roots.add(root);
+                rootFolders.add(new Folder(root));
+            }
+        }
+
+        final List<RootConfiguration> groups = new ArrayList<>();
+
+        groups.add(new RootConfiguration(parsed, globalGroupOptions));
+
+        for (String groupValue : parsed.valuesOf(group)) {
+            OptionParser groupParser = new OptionParser();
+            GroupOptions groupOptions = setupGroupParser(groupParser);
+            OptionSet parsedGroup = groupParser.parse(splitGroupArg(groupValue));
+
+            groups.add(new RootConfiguration(parsedGroup, groupOptions));
+        }
+
+        if (parsed.has("show-gui")) {
+            if (parsed.has(configFile)) {
+                final File settingsFile = parsed.valueOf(configFile);
+                try {
+                    SwingUtilities.invokeAndWait(new Runnable() {
+                        @Override public void run() {
+                            try {
+                                Pair<ClassPath, ClassPath> sourceAndBinaryCP = jointSourceAndBinaryCP(groups);
+                                showGUICustomizer(settingsFile, sourceAndBinaryCP.second(), sourceAndBinaryCP.first());
+                            } catch (IOException ex) {
+                                Exceptions.printStackTrace(ex);
+                            } catch (BackingStoreException ex) {
+                                Exceptions.printStackTrace(ex);
+                            }
+                        }
+                    });
+                } catch (InterruptedException ex) {
+                    Exceptions.printStackTrace(ex);
+                } catch (InvocationTargetException ex) {
+                    Exceptions.printStackTrace(ex);
+                }
+
+                return 0;
+            } else {
+                System.err.println("show-gui requires config-file");
+                return 1;
+            }
+        }
+
+        File cacheDir = parsed.valueOf(cache);
+        boolean deleteCacheDir = false;
+
+        try {
+            if (cacheDir == null) {
+                cacheDir = File.createTempFile("jackpot", "cache");
+                cacheDir.delete();
+                if (!(deleteCacheDir = cacheDir.mkdirs())) {
+                    System.err.println("cannot create temporary cache");
+                    return 1;
+                }
+            }
+
+            if (cacheDir.isFile()) {
+                System.err.println("cache directory exists and is a file");
+                return 1;
+            }
+
+            String[] cacheDirContent = cacheDir.list();
+
+            if (cacheDirContent != null && cacheDirContent.length > 0 && !new File(cacheDir, "segments").exists()) {
+                System.err.println("cache directory is not empty, but was not created by this tool");
+                return 1;
+            }
+
+            cacheDir.mkdirs();
+
+            CacheFolder.setCacheFolder(FileUtil.toFileObject(FileUtil.normalizeFile(cacheDir)));
+
+            org.netbeans.api.project.ui.OpenProjects.getDefault().getOpenProjects();
+            RepositoryUpdater.getDefault().start(false);
+
+            if (parsed.has("list")) {
+                Pair<ClassPath, ClassPath> sourceAndBinaryCP = jointSourceAndBinaryCP(groups);
+                printHints(sourceAndBinaryCP.first(),
+                           sourceAndBinaryCP.second());
+                return 0;
+            }
+
+            int totalGroups = 0;
+
+            for (RootConfiguration groupConfig : groups) {
+                if (!groupConfig.rootFolders.isEmpty()) totalGroups++;
+            }
+
+            ProgressHandleWrapper progress = parsed.has("progress") ? new ProgressHandleWrapper(new ConsoleProgressHandleAbstraction(), ProgressHandleWrapper.prepareParts(totalGroups)) : new ProgressHandleWrapper(1);
+
+            Preferences hintSettingsPreferences;
+            boolean apply;
+            boolean runDeclarative;
+            boolean runDeclarativeTests;
+
+            if (parsed.has(configFile)) {
+                Preferences settingsFromConfigFile;
+                settingsFromConfigFile = XMLHintPreferences.from(parsed.valueOf(configFile));
+                hintSettingsPreferences = settingsFromConfigFile.node("settings");
+                apply = settingsFromConfigFile.getBoolean("apply", false);
+                runDeclarative = settingsFromConfigFile.getBoolean("runDeclarative", true);
+                runDeclarativeTests = settingsFromConfigFile.getBoolean("runDeclarativeTests", false);
+                if (parsed.has(hint)) {
+                    System.err.println("cannot specify --hint and --config-file together");
+                    return 1;
+                } else if (parsed.has(hintFile)) {
+                    System.err.println("cannot specify --hint-file and --config-file together");
+                    return 1;
+                }
+            } else {
+                hintSettingsPreferences = null;
+                apply = false;
+                runDeclarative = true;
+                runDeclarativeTests = parsed.has(RUN_TESTS);
+            }
+
+            if (parsed.has(config) && !parsed.has(hint)) {
+                System.err.println("--config cannot specified when no hint is specified");
+                return 1;
+            }
+
+            if (parsed.has(OPTION_NO_APPLY)) {
+                apply = false;
+            } else if (parsed.has(OPTION_APPLY)) {
+                apply = true;
+            }
+
+            GroupResult result = GroupResult.NOTHING_TO_DO;
+
+            try (Writer outS = parsed.has(out) ? new BufferedWriter(new OutputStreamWriter(new FileOutputStream(parsed.valueOf(out)))) : null) {
+                GlobalConfiguration globalConfig = new GlobalConfiguration(hintSettingsPreferences, apply, runDeclarative, runDeclarativeTests, parsed.valueOf(hint), parsed.valueOf(hintFile), outS, parsed.has(OPTION_FAIL_ON_WARNINGS));
+
+                for (RootConfiguration groupConfig : groups) {
+                    result = result.join(handleGroup(groupConfig, progress, globalConfig, parsed.valuesOf(config)));
+                }
+            }
+
+            progress.finish();
+
+            if (result == GroupResult.NOTHING_TO_DO) {
+                System.err.println("no source roots to work on");
+                return 1;
+            }
+
+            if (result == GroupResult.NO_HINTS_FOUND) {
+                System.err.println("no hints specified");
+                return 1;
+            }
+
+            return result == GroupResult.SUCCESS ? 0 : 1;
+        } catch (Throwable e) {
+            e.printStackTrace();
+            throw new IllegalStateException(e);
+        } finally {
+            if (deleteCacheDir) {
+                FileObject cacheDirFO = FileUtil.toFileObject(cacheDir);
+
+                if (cacheDirFO != null) {
+                    //TODO: would be better to do j.i.File.delete():
+                    cacheDirFO.delete();
+                }
+            }
+        }
+    }
+
+    private static Pair<ClassPath, ClassPath> jointSourceAndBinaryCP(List<RootConfiguration> groups) {
+        Set<FileObject> sourceRoots = new HashSet<>();
+        Set<FileObject> binaryRoots = new HashSet<>();
+        for (RootConfiguration groupConfig : groups) {
+            sourceRoots.addAll(Arrays.asList(groupConfig.sourceCP.getRoots()));
+            binaryRoots.addAll(Arrays.asList(groupConfig.binaryCP.getRoots()));
+        }
+        return Pair.of(ClassPathSupport.createClassPath(sourceRoots.toArray(new FileObject[0])),
+                       ClassPathSupport.createClassPath(binaryRoots.toArray(new FileObject[0])));
+    }
+
+    private static GroupOptions setupGroupParser(OptionParser parser) {
+        return new GroupOptions(parser.accepts("classpath", "classpath").withRequiredArg().withValuesSeparatedBy(File.pathSeparatorChar).ofType(File.class),
+                                parser.accepts("bootclasspath", "bootclasspath").withRequiredArg().withValuesSeparatedBy(File.pathSeparatorChar).ofType(File.class),
+                                parser.accepts("sourcepath", "sourcepath").withRequiredArg().withValuesSeparatedBy(File.pathSeparatorChar).ofType(File.class),
+                                parser.accepts("source", "source level").withRequiredArg().ofType(String.class).defaultsTo(SOURCE_LEVEL_DEFAULT));
+    }
+
+    private static final class GroupOptions {
+        private final ArgumentAcceptingOptionSpec<File> classpath;
+        private final ArgumentAcceptingOptionSpec<File> bootclasspath;
+        private final ArgumentAcceptingOptionSpec<File> sourcepath;
+        private final ArgumentAcceptingOptionSpec<String> source;
+
+        public GroupOptions(ArgumentAcceptingOptionSpec<File> classpath, ArgumentAcceptingOptionSpec<File> bootclasspath, ArgumentAcceptingOptionSpec<File> sourcepath, ArgumentAcceptingOptionSpec<String> source) {
+            this.classpath = classpath;
+            this.bootclasspath = bootclasspath;
+            this.sourcepath = sourcepath;
+            this.source = source;
+        }
+
+    }
+
+    private static Map<HintMetadata, Collection<? extends HintDescription>> listHints(ClassPath sourceFrom, ClassPath binaryFrom) {
+        Map<HintMetadata, Collection<? extends HintDescription>> result = new HashMap<HintMetadata, Collection<? extends HintDescription>>();
+
+        for (Entry<HintMetadata, ? extends Collection<? extends HintDescription>> entry: RulesManager.getInstance().readHints(null, Arrays.asList(sourceFrom, binaryFrom), null).entrySet()) {
+            result.put(entry.getKey(), entry.getValue());
+        }
+
+        return result;
+    }
+
+    private static GroupResult handleGroup(RootConfiguration rootConfiguration, ProgressHandleWrapper w, GlobalConfiguration globalConfig, List<String> config) throws IOException {
+        Iterable<? extends HintDescription> hints;
+
+        if (rootConfiguration.rootFolders.isEmpty()) {
+            return GroupResult.NOTHING_TO_DO;
+        }
+
+        WarningsAndErrors wae = new WarningsAndErrors();
+
+        ProgressHandleWrapper progress = w.startNextPartWithEmbedding(1);
+        Preferences settings = globalConfig.configurationPreferences != null ? globalConfig.configurationPreferences : new MemoryPreferences();
+        HintsSettings hintSettings = HintsSettings.createPreferencesBasedHintsSettings(settings, false, null);
+
+        if (globalConfig.hint != null) {
+            hints = findHints(rootConfiguration.sourceCP, rootConfiguration.binaryCP, globalConfig.hint, hintSettings);
+        } else if (globalConfig.hintFile != null) {
+            FileObject hintFileFO = FileUtil.toFileObject(globalConfig.hintFile);
+            assert hintFileFO != null;
+            hints = PatternConvertor.create(hintFileFO.asText());
+            for (HintDescription hd : hints) {
+                hintSettings.setEnabled(hd.getMetadata(), true);
+            }
+        } else {
+            hints = readHints(rootConfiguration.sourceCP, rootConfiguration.binaryCP, hintSettings, settings, globalConfig.runDeclarative);
+            if (globalConfig.runDeclarativeTests) {
+                Set<String> enabledHints = new HashSet<>();
+                for (HintDescription desc : hints) {
+                    enabledHints.add(desc.getMetadata().id);
+                }
+                ClassPath combined = ClassPathSupport.createProxyClassPath(rootConfiguration.sourceCP, rootConfiguration.binaryCP);
+                Map<FileObject, FileObject> testFiles = new HashMap<>();
+                for (FileObject upgrade : combined.findAllResources("META-INF/upgrade")) {
+                    for (FileObject c : upgrade.getChildren()) {
+                        if (c.getExt().equals("test")) {
+                            FileObject hintFile = FileUtil.findBrother(c, "hint");
+
+                            for (HintMetadata hm : DeclarativeHintRegistry.parseHintFile(hintFile).keySet()) {
+                                if (enabledHints.contains(hm.id)) {
+                                    testFiles.put(c, hintFile);
+                                    break;
+                                }
+                            }
+                        }
+                    }
+                }
+                for (Entry<FileObject, FileObject> e : testFiles.entrySet()) {
+                    TestCase[] testCases = TestParser.parse(e.getKey().asText()); //XXX: encoding
+                    try {
+                        Map<TestCase, Collection<String>> testResult = TestPerformer.performTest(e.getValue(), e.getKey(), testCases, new AtomicBoolean());
+                        for (TestCase tc : testCases) {
+                            List<String> expected = Arrays.asList(tc.getResults());
+                            List<String> actual = new ArrayList<>(testResult.get(tc));
+                            if (!expected.equals(actual)) {
+                                int pos = tc.getTestCaseStart();
+                                String id = "test-failure";
+                                ErrorDescription ed = ErrorDescriptionFactory.createErrorDescription(id, Severity.ERROR, "Actual results did not match the expected test results. Actual results: " + expected, null, ErrorDescriptionFactory.lazyListForFixes(Collections.<Fix>emptyList()), e.getKey(), pos, pos);
+                                print(ed, wae, Collections.singletonMap(id, id));
+                            }
+                        }
+                    } catch (Exception ex) {
+                        ex.printStackTrace();
+                    }
+                }
+            }
+        }
+
+        if (config != null && !config.isEmpty()) {
+            Iterator<? extends HintDescription> hit = hints.iterator();
+            HintDescription hd = hit.next();
+
+            if (hit.hasNext()) {
+                System.err.println("--config cannot specified when more than one hint is specified");
+
+                return GroupResult.FAILURE;
+            }
+
+            Preferences prefs = hintSettings.getHintPreferences(hd.getMetadata());
+
+            boolean stop = false;
+
+            for (String c : config) {
+                int assign = c.indexOf('=');
+
+                if (assign == (-1)) {
+                    System.err.println("configuration option is missing '=' (" + c + ")");
+                    stop = true;
+                    continue;
+                }
+
+                prefs.put(c.substring(0, assign), c.substring(assign + 1));
+            }
+
+            if (stop) {
+                return GroupResult.FAILURE;
+            }
+        }
+
+        String sourceLevel = rootConfiguration.sourceLevel;
+
+        if (!Pattern.compile(ACCEPTABLE_SOURCE_LEVEL_PATTERN).matcher(sourceLevel).matches()) {
+            System.err.println("unrecognized source level specification: " + sourceLevel);
+            return GroupResult.FAILURE;
+        }
+
+        if (globalConfig.apply && !hints.iterator().hasNext()) {
+            return GroupResult.NO_HINTS_FOUND;
+        }
+
+        Object[] register2Lookup = new Object[] {
+            new ClassPathProviderImpl(rootConfiguration.bootCP, rootConfiguration.compileCP, rootConfiguration.sourceCP),
+            new JavaPathRecognizer(),
+            new SourceLevelQueryImpl(rootConfiguration.sourceCP, sourceLevel)
+        };
+
+        try {
+            for (Object toRegister : register2Lookup) {
+                MainLookup.register(toRegister);
+            }
+
+            if (globalConfig.apply) {
+                apply(hints, rootConfiguration.rootFolders.toArray(new Folder[0]), progress, hintSettings, globalConfig.out);
+
+                return GroupResult.SUCCESS; //TODO: WarningsAndErrors?
+            } else {
+                findOccurrences(hints, rootConfiguration.rootFolders.toArray(new Folder[0]), progress, hintSettings, wae);
+
+                if (wae.errors != 0 || (wae.warnings != 0 && globalConfig.failOnWarnings)) {
+                    return GroupResult.FAILURE;
+                } else {
+                    return GroupResult.SUCCESS;
+                }
+            }
+        } finally {
+            for (Object toUnRegister : register2Lookup) {
+                MainLookup.unregister(toUnRegister);
+            }
+        }
+    }
+
+    private static class MemoryPreferences extends AbstractPreferences {
+
+        private final Map<String, String> values = new HashMap<>();
+        private final Map<String, MemoryPreferences> nodes = new HashMap<>();
+
+        public MemoryPreferences() {
+            this(null, "");
+        }
+
+        public MemoryPreferences(MemoryPreferences parent, String name) {
+            super(parent, name);
+        }
+        @Override
+        protected void putSpi(String key, String value) {
+            values.put(key, value);
+        }
+
+        @Override
+        protected String getSpi(String key) {
+            return values.get(key);
+        }
+
+        @Override
+        protected void removeSpi(String key) {
+            values.remove(key);
+        }
+
+        @Override
+        protected void removeNodeSpi() throws BackingStoreException {
+            ((MemoryPreferences) parent()).nodes.remove(name());
+        }
+
+        @Override
+        protected String[] keysSpi() throws BackingStoreException {
+            return values.keySet().toArray(new String[0]);
+        }
+
+        @Override
+        protected String[] childrenNamesSpi() throws BackingStoreException {
+            return nodes.keySet().toArray(new String[0]);
+        }
+
+        @Override
+        protected AbstractPreferences childSpi(String name) {
+            MemoryPreferences result = nodes.get(name);
+
+            if (result == null) {
+                nodes.put(name, result = new MemoryPreferences(this, name));
+            }
+
+            return result;
+        }
+
+        @Override
+        protected void syncSpi() throws BackingStoreException {
+        }
+
+        @Override
+        protected void flushSpi() throws BackingStoreException {
+        }
+    }
+
+    private enum GroupResult {
+        NOTHING_TO_DO {
+            @Override
+            public GroupResult join(GroupResult other) {
+                return other;
+            }
+        },
+        NO_HINTS_FOUND {
+            @Override
+            public GroupResult join(GroupResult other) {
+                if (other == NOTHING_TO_DO) return this;
+                return other;
+            }
+        },
+        SUCCESS {
+            @Override
+            public GroupResult join(GroupResult other) {
+                if (other == FAILURE) return other;
+                return this;
+            }
+        },
+        FAILURE {
+            @Override
+            public GroupResult join(GroupResult other) {
+                return this;
+            }
+        };
+
+        public abstract GroupResult join(GroupResult other);
+    }
+    
+    private static Iterable<? extends HintDescription> findHints(ClassPath sourceFrom, ClassPath binaryFrom, String name, HintsSettings toEnableIn) {
+        List<HintDescription> descs = new LinkedList<HintDescription>();
+
+        for (Entry<HintMetadata, Collection<? extends HintDescription>> e : listHints(sourceFrom, binaryFrom).entrySet()) {
+            if (e.getKey().displayName.equals(name)) {
+                descs.addAll(e.getValue());
+                toEnableIn.setEnabled(e.getKey(), true);
+            }
+        }
+
+        return descs;
+    }
+
+    private static Iterable<? extends HintDescription> allHints(ClassPath sourceFrom, ClassPath binaryFrom, HintsSettings toEnableIn) {
+        List<HintDescription> descs = new LinkedList<HintDescription>();
+
+        for (Entry<HintMetadata, Collection<? extends HintDescription>> e : listHints(sourceFrom, binaryFrom).entrySet()) {
+            if (e.getKey().kind != Kind.INSPECTION) continue;
+            if (!e.getKey().enabled) continue;
+            descs.addAll(e.getValue());
+            toEnableIn.setEnabled(e.getKey(), true);
+        }
+
+        return descs;
+    }
+
+    private static Iterable<? extends HintDescription> readHints(ClassPath sourceFrom, ClassPath binaryFrom, HintsSettings toEnableIn, Preferences toEnableInPreferencesHack, boolean declarativeEnabledByDefault) {
+        Map<HintMetadata, ? extends Collection<? extends HintDescription>> hardcoded = RulesManager.getInstance().readHints(null, Arrays.<ClassPath>asList(), null);
+        Map<HintMetadata, ? extends Collection<? extends HintDescription>> all = RulesManager.getInstance().readHints(null, Arrays.asList(sourceFrom, binaryFrom), null);
+        List<HintDescription> descs = new LinkedList<HintDescription>();
+
+        for (Entry<HintMetadata, ? extends Collection<? extends HintDescription>> entry: all.entrySet()) {
+            if (hardcoded.containsKey(entry.getKey())) {
+                if (toEnableIn.isEnabled(entry.getKey())) {
+                    descs.addAll(entry.getValue());
+                }
+            } else {
+                if (/*XXX: hack*/toEnableInPreferencesHack.node(entry.getKey().id).getBoolean("enabled", declarativeEnabledByDefault)) {
+                    descs.addAll(entry.getValue());
+                }
+            }
+        }
+
+        return descs;
+    }
+
+    private static final Logger TOP_LOGGER = Logger.getLogger("");
+
+    private static void prepareLoggers() {
+        TOP_LOGGER.setLevel(Level.OFF);
+        System.setProperty("RepositoryUpdate.increasedLogLevel", "OFF");
+    }
+    
+    private static void findOccurrences(Iterable<? extends HintDescription> descs, Folder[] sourceRoot, ProgressHandleWrapper progress, HintsSettings settings, final WarningsAndErrors wae) throws IOException {
+        final Map<String, String> id2DisplayName = Utils.computeId2DisplayName(descs);
+        ProgressHandleWrapper w = progress.startNextPartWithEmbedding(1, 1);
+        BatchResult occurrences = BatchSearch.findOccurrences(descs, Scopes.specifiedFoldersScope(sourceRoot), w, settings);
+
+        List<MessageImpl> problems = new LinkedList<MessageImpl>();
+        BatchSearch.getVerifiedSpans(occurrences, w, new VerifiedSpansCallBack() {
+            @Override public void groupStarted() {}
+            @Override public boolean spansVerified(CompilationController wc, Resource r, Collection<? extends ErrorDescription> hints) throws Exception {
+                for (ErrorDescription ed : hints) {
+                    print(ed, wae, id2DisplayName);
+                }
+                return true;
+            }
+            @Override public void groupFinished() {}
+            @Override public void cannotVerifySpan(Resource r) {
+                //TODO: ignored - what to do?
+            }
+        }, problems, new AtomicBoolean());
+    }
+
+    private static void print(ErrorDescription error, WarningsAndErrors wae, Map<String, String> id2DisplayName) throws IOException {
+        int lineNumber = error.getRange().getBegin().getLine();
+        String line = error.getFile().asLines().get(lineNumber);
+        int column = error.getRange().getBegin().getColumn();
+        StringBuilder b = new StringBuilder();
+
+        for (int i = 0; i < column; i++) {
+            if (Character.isWhitespace(line.charAt(i))) {
+                b.append(line.charAt(i));
+            } else {
+                b.append(' ');
+            }
+        }
+
+        b.append('^');
+
+        String idDisplayName = Utils.categoryName(error.getId(), id2DisplayName);
+        String severity;
+        if (error.getSeverity() == Severity.ERROR) {
+            severity = "error";
+            wae.errors++;
+        } else {
+            severity = "warning";
+            wae.warnings++;
+        }
+        System.out.println(FileUtil.getFileDisplayName(error.getFile()) + ":" + (lineNumber + 1) + ": " + severity + ": " + idDisplayName + error.getDescription());
+        System.out.println(line);
+        System.out.println(b);
+    }
+
+    private static void apply(Iterable<? extends HintDescription> descs, Folder[] sourceRoot, ProgressHandleWrapper progress, HintsSettings settings, Writer out) throws IOException {
+        ProgressHandleWrapper w = progress.startNextPartWithEmbedding(1, 1);
+        BatchResult occurrences = BatchSearch.findOccurrences(descs, Scopes.specifiedFoldersScope(sourceRoot), w, settings);
+
+        List<MessageImpl> problems = new LinkedList<MessageImpl>();
+        Collection<ModificationResult> diffs = BatchUtilities.applyFixes(occurrences, w, new AtomicBoolean(), problems);
+
+        if (out != null) {
+            for (ModificationResult mr : diffs) {
+                org.netbeans.modules.jackpot30.indexing.batch.BatchUtilities.exportDiff(mr, null, out);
+            }
+        } else {
+            for (ModificationResult mr : diffs) {
+                mr.commit();
+            }
+        }
+    }
+
+    private static void printHints(ClassPath sourceFrom, ClassPath binaryFrom) throws IOException {
+        Set<String> hints = new TreeSet<String>();
+
+        for (Entry<HintMetadata, Collection<? extends HintDescription>> e : listHints(sourceFrom, binaryFrom).entrySet()) {
+            hints.add(e.getKey().displayName);
+        }
+
+        for (String h : hints) {
+            System.out.println(h);
+        }
+    }
+
+    private static ClassPath createDefaultBootClassPath() throws IOException {
+        try {
+            String cp = System.getProperty("sun.boot.class.path");
+            List<URL> urls = new ArrayList<URL>();
+            String[] paths = cp.split(Pattern.quote(System.getProperty("path.separator")));
+
+            for (String path : paths) {
+                File f = new File(path);
+
+                if (!f.canRead())
+                    continue;
+
+                FileObject fo = FileUtil.toFileObject(FileUtil.normalizeFile(f));
+
+                if (FileUtil.isArchiveFile(fo)) {
+                    fo = FileUtil.getArchiveRoot(fo);
+                }
+
+                if (fo != null) {
+                    urls.add(fo.getURL());
+                }
+            }
+
+            return ClassPathSupport.createClassPath(urls.toArray(new URL[0]));
+        } catch (FileStateInvalidException e) {
+            throw e;
+        }
+    }
+
+    private static ClassPath createClassPath(Iterable<? extends File> roots, ClassPath def) {
+        if (roots == null) return def;
+
+        List<URL> rootURLs = new ArrayList<URL>();
+
+        for (File r : roots) {
+            rootURLs.add(FileUtil.urlForArchiveOrDir(r));
+        }
+
+        return ClassPathSupport.createClassPath(rootURLs.toArray(new URL[0]));
+    }
+
+    private static void showGUICustomizer(File settingsFile, ClassPath binaryCP, ClassPath sourceCP) throws IOException, BackingStoreException {
+        GlobalPathRegistry.getDefault().register(ClassPath.COMPILE, new ClassPath[] {binaryCP});
+        GlobalPathRegistry.getDefault().register(ClassPath.SOURCE, new ClassPath[] {sourceCP});
+        ClassPathBasedHintWrapper hints = new ClassPathBasedHintWrapper();
+        final Preferences p = XMLHintPreferences.from(settingsFile);
+        JPanel hintPanel = new HintsPanel(p.node("settings"), hints, true);
+        final JCheckBox runDeclarativeHints = new JCheckBox("Always Run Declarative Rules");
+
+        runDeclarativeHints.setToolTipText("Always run the declarative rules found on classpath? (Only those selected above will be run when unchecked.)");
+        runDeclarativeHints.setSelected(p.getBoolean("runDeclarative", true));
+        runDeclarativeHints.addActionListener(new ActionListener() {
+            @Override public void actionPerformed(ActionEvent e) {
+                p.putBoolean("runDeclarative", runDeclarativeHints.isSelected());
+            }
+        });
+
+        JPanel customizer = new JPanel(new BorderLayout());
+
+        customizer.add(hintPanel, BorderLayout.CENTER);
+        customizer.add(runDeclarativeHints, BorderLayout.SOUTH);
+        JOptionPane jop = new JOptionPane(customizer, JOptionPane.PLAIN_MESSAGE);
+        JDialog dialog = jop.createDialog("Select Hints");
+
+        jop.selectInitialValue();
+        dialog.setVisible(true);
+        dialog.dispose();
+
+        Object result = jop.getValue();
+
+        if (result.equals(JOptionPane.OK_OPTION)) {
+            p.flush();
+        }
+    }
+
+    static String[] splitGroupArg(String arg) {
+        List<String> result = new ArrayList<>();
+        StringBuilder currentPart = new StringBuilder();
+
+        for (int i = 0; i < arg.length(); i++) {
+            switch (arg.charAt(i)) {
+                case '\\':
+                    if (++i < arg.length()) {
+                        currentPart.append(arg.charAt(i));
+                    }
+                    break;
+                case ' ':
+                    if (currentPart.length() > 0) {
+                        result.add(currentPart.toString());
+                        currentPart.delete(0, currentPart.length());
+                    }
+                    break;
+                default:
+                    currentPart.append(arg.charAt(i));
+                    break;
+            }
+        }
+
+        if (currentPart.length() > 0) {
+            result.add(currentPart.toString());
+        }
+
+        return result.toArray(new String[0]);
+    }
+
+    private static final class WarningsAndErrors {
+        private int warnings;
+        private int errors;
+    }
+
+    private static final class RootConfiguration {
+        private final List<Folder> rootFolders;
+        private final ClassPath bootCP;
+        private final ClassPath compileCP;
+        private final ClassPath sourceCP;
+        private final ClassPath binaryCP;
+        private final String    sourceLevel;
+
+        public RootConfiguration(OptionSet parsed, GroupOptions groupOptions) throws IOException {
+            this.rootFolders = new ArrayList<>();
+
+            List<FileObject> roots = new ArrayList<>();
+
+            for (String sr : parsed.nonOptionArguments()) {
+                File r = new File(sr);
+                FileObject root = FileUtil.toFileObject(r);
+
+                if (root != null) {
+                    roots.add(root);
+                    rootFolders.add(new Folder(root));
+                }
+            }
+
+            this.bootCP = createClassPath(parsed.has(groupOptions.bootclasspath) ? parsed.valuesOf(groupOptions.bootclasspath) : null, createDefaultBootClassPath());
+            this.compileCP = createClassPath(parsed.has(groupOptions.classpath) ? parsed.valuesOf(groupOptions.classpath) : null, ClassPath.EMPTY);
+            this.sourceCP = createClassPath(parsed.has(groupOptions.sourcepath) ? parsed.valuesOf(groupOptions.sourcepath) : null, ClassPathSupport.createClassPath(roots.toArray(new FileObject[0])));
+            this.binaryCP = ClassPathSupport.createProxyClassPath(bootCP, compileCP);
+            this.sourceLevel = parsed.valueOf(groupOptions.source);
+        }
+
+    }
+
+    private static final class GlobalConfiguration {
+        private final Preferences configurationPreferences;
+        private final boolean apply;
+        private final boolean runDeclarative;
+        private final boolean runDeclarativeTests;
+        private final String hint;
+        private final File hintFile;
+        private final Writer out;
+        private final boolean failOnWarnings;
+
+        public GlobalConfiguration(Preferences configurationPreferences, boolean apply, boolean runDeclarative, boolean runDeclarativeTests, String hint, File hintFile, Writer out, boolean failOnWarnings) {
+            this.configurationPreferences = configurationPreferences;
+            this.apply = apply;
+            this.runDeclarative = runDeclarative;
+            this.runDeclarativeTests = runDeclarativeTests;
+            this.hint = hint;
+            this.hintFile = hintFile;
+            this.out = out;
+            this.failOnWarnings = failOnWarnings;
+        }
+
+    }
+
+    @ServiceProvider(service=Lookup.class)
+    public static final class LookupProviderImpl extends ProxyLookup {
+
+        public LookupProviderImpl() {
+            super(Lookups.forPath("Services/AntBasedProjectTypes"));
+        }
+    }
+
+    public static final class ClassPathProviderImpl implements ClassPathProvider {
+        private final ClassPath boot;
+        private final ClassPath compile;
+        private final ClassPath source;
+
+        public ClassPathProviderImpl(ClassPath boot, ClassPath compile, ClassPath source) {
+            this.boot = boot;
+            this.compile = compile;
+            this.source = source;
+        }
+
+        @Override
+        public ClassPath findClassPath(FileObject file, String type) {
+            if (source.findOwnerRoot(file) != null) {
+                if (ClassPath.BOOT.equals(type)) {
+                    return boot;
+                } else if (ClassPath.COMPILE.equals(type)) {
+                    return compile;
+                } else  if (ClassPath.SOURCE.equals(type)) {
+                    return source;
+                }
+            }
+
+            return null;
+        }
+    }
+
+    public static final class SourceLevelQueryImpl implements SourceLevelQueryImplementation2 {
+        private final ClassPath sourceCP;
+        private final Result sourceLevel;
+
+        public SourceLevelQueryImpl(ClassPath sourceCP, final String sourceLevel) {
+            this.sourceCP = sourceCP;
+            this.sourceLevel = new Result() {
+                @Override public String getSourceLevel() {
+                    return sourceLevel;
+                }
+                @Override public void addChangeListener(ChangeListener listener) {}
+                @Override public void removeChangeListener(ChangeListener listener) {}
+            };
+        }
+
+        @Override
+        public Result getSourceLevel(FileObject javaFile) {
+            if (sourceCP.findOwnerRoot(javaFile) != null) {
+                return sourceLevel;
+            } else {
+                return null;
+            }
+        }
+
+    }
+
+    private static final class ConsoleProgressHandleAbstraction implements ProgressHandleAbstraction {
+
+        private final int width = 80 - 2;
+
+        private int total = -1;
+        private int current = 0;
+
+        public ConsoleProgressHandleAbstraction() {
+        }
+
+        @Override
+        public synchronized void start(int totalWork) {
+            if (total != (-1)) throw new UnsupportedOperationException();
+            total = totalWork;
+            update();
+        }
+
+        @Override
+        public synchronized void progress(int currentWorkDone) {
+            current = currentWorkDone;
+            update();
+        }
+
+        @Override
+        public void progress(String message) {
+        }
+
+        @Override
+        public synchronized void finish() {
+            current = total;
+            RequestProcessor.getDefault().post(new Runnable() {
+                @Override
+                public void run() {
+                    doUpdate(false);
+                    System.out.println();
+                }
+            });
+        }
+
+        private void update() {
+            RequestProcessor.getDefault().post(new Runnable() {
+                @Override
+                public void run() {
+                    doUpdate(true);
+                }
+            });
+        }
+
+        private int currentShownDone = -1;
+
+        private void doUpdate(boolean moveCaret) {
+            int done;
+
+            synchronized(this) {
+                done = (int) ((((double) width) / total) * current);
+
+                if (done == currentShownDone) {
+                    return;
+                }
+
+                currentShownDone = done;
+            }
+            
+            int todo = width - done;
+            PrintStream pw = System.out;
+
+            pw.print("[");
+
+
+            while (done-- > 0) {
+                pw.print("=");
+            }
+
+            while (todo-- > 0) {
+                pw.print(" ");
+            }
+
+            pw.print("]");
+
+            if (moveCaret)
+                pw.print("\r");
+        }
+
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-jackpot30/blob/9ed0a377/cmdline/tool/test/scripted/config
----------------------------------------------------------------------
diff --git a/cmdline/tool/test/scripted/config b/cmdline/tool/test/scripted/config
new file mode 100755
index 0000000..5ad1957
--- /dev/null
+++ b/cmdline/tool/test/scripted/config
@@ -0,0 +1,48 @@
+#!/bin/bash
+#
+# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+#
+# Copyright 2010 Sun Microsystems, Inc. All rights reserved.
+#
+# The contents of this file are subject to the terms of either the GNU
+# General Public License Version 2 only ("GPL") or the Common
+# Development and Distribution License("CDDL") (collectively, the
+# "License"). You may not use this file except in compliance with the
+# License. You can obtain a copy of the License at
+# http://www.netbeans.org/cddl-gplv2.html
+# or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+# specific language governing permissions and limitations under the
+# License.  When distributing the software, include this License Header
+# Notice in each file and include the License file at
+# nbbuild/licenses/CDDL-GPL-2-CP.  Sun designates this
+# particular file as subject to the "Classpath" exception as provided
+# by Sun in the GPL Version 2 section of the License file that
+# accompanied this code. If applicable, add the following below the
+# License Header, with the fields enclosed by brackets [] replaced by
+# your own identifying information:
+# "Portions Copyrighted [year] [name of copyright owner]"
+#
+# If you wish your version of this file to be governed by only the CDDL
+# or only the GPL Version 2, indicate your decision by adding
+# "[Contributor] elects to include this software in this distribution
+# under the [CDDL or GPL Version 2] license." If you do not indicate a
+# single choice of license, a recipient has the option to distribute
+# your version of this file under either the CDDL, the GPL Version 2 or
+# to extend the choice of license to its licensees as provided above.
+# However, if you add GPL Version 2 code and therefore, elected the GPL
+# Version 2 license, then the option applies only if the new code is
+# made subject to such option by the copyright holder.
+#
+# Contributor(s):
+#
+# Portions Copyrighted 2010 Sun Microsystems, Inc.
+
+TOOL_NAME=jackpot
+
+run_tool() {
+    "${TOOL_NAME}"/"${TOOL_NAME}" "$@"
+}
+
+run_ant() {
+    ant -Djackpot.home="$PWD"/"$TOOL_NAME" "$@"
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-jackpot30/blob/9ed0a377/cmdline/tool/test/scripted/test-ant
----------------------------------------------------------------------
diff --git a/cmdline/tool/test/scripted/test-ant b/cmdline/tool/test/scripted/test-ant
new file mode 100755
index 0000000..01878e7
--- /dev/null
+++ b/cmdline/tool/test/scripted/test-ant
@@ -0,0 +1,74 @@
+#!/bin/bash
+#
+# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+#
+# Copyright 2013 Sun Microsystems, Inc. All rights reserved.
+#
+# The contents of this file are subject to the terms of either the GNU
+# General Public License Version 2 only ("GPL") or the Common
+# Development and Distribution License("CDDL") (collectively, the
+# "License"). You may not use this file except in compliance with the
+# License. You can obtain a copy of the License at
+# http://www.netbeans.org/cddl-gplv2.html
+# or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+# specific language governing permissions and limitations under the
+# License.  When distributing the software, include this License Header
+# Notice in each file and include the License file at
+# nbbuild/licenses/CDDL-GPL-2-CP.  Sun designates this
+# particular file as subject to the "Classpath" exception as provided
+# by Sun in the GPL Version 2 section of the License file that
+# accompanied this code. If applicable, add the following below the
+# License Header, with the fields enclosed by brackets [] replaced by
+# your own identifying information:
+# "Portions Copyrighted [year] [name of copyright owner]"
+#
+# If you wish your version of this file to be governed by only the CDDL
+# or only the GPL Version 2, indicate your decision by adding
+# "[Contributor] elects to include this software in this distribution
+# under the [CDDL or GPL Version 2] license." If you do not indicate a
+# single choice of license, a recipient has the option to distribute
+# your version of this file under either the CDDL, the GPL Version 2 or
+# to extend the choice of license to its licensees as provided above.
+# However, if you add GPL Version 2 code and therefore, elected the GPL
+# Version 2 license, then the option applies only if the new code is
+# made subject to such option by the copyright holder.
+#
+# Contributor(s):
+#
+# Portions Copyrighted 2013 Sun Microsystems, Inc.
+
+perform_test() {
+    create_file src/test/Test.java <<EOF
+package test;
+public class Test {
+    private void test() {
+        String s = "foo".intern();
+    }
+}
+EOF
+    create_file build.xml <<"EOF"
+<?xml version="1.0" encoding="UTF-8"?>
+<project name="test" default="run" basedir=".">
+    <target name="run">
+        <fail unless="jackpot.home">${jackpot.home} must be specified</fail>
+        <taskdef name="jackpot" classname="org.netbeans.modules.jackpot30.cmdline.ant.JackpotTask" classpath="${jackpot.home}/jackpot-ant.jar"/>
+        <jackpot jackpotHome="${jackpot.home}">
+            <src>
+                <pathelement path="src" />
+            </src>
+        </jackpot>
+    </target>
+</project>
+EOF
+    create_file src/META-INF/upgrade/test.hint <<"EOF"
+$1.intern();;
+EOF
+
+    run_ant >output
+
+    if grep <output 'warning: \[test\] test' >/dev/null 2>/dev/null; then
+        fail "does not contain required output";
+    fi;
+}
+
+. `dirname $0`/harness

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-jackpot30/blob/9ed0a377/cmdline/tool/test/scripted/test-cmdline-1
----------------------------------------------------------------------
diff --git a/cmdline/tool/test/scripted/test-cmdline-1 b/cmdline/tool/test/scripted/test-cmdline-1
new file mode 100755
index 0000000..3a46c8f
--- /dev/null
+++ b/cmdline/tool/test/scripted/test-cmdline-1
@@ -0,0 +1,62 @@
+#!/bin/bash
+#
+# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+#
+# Copyright 2010 Sun Microsystems, Inc. All rights reserved.
+#
+# The contents of this file are subject to the terms of either the GNU
+# General Public License Version 2 only ("GPL") or the Common
+# Development and Distribution License("CDDL") (collectively, the
+# "License"). You may not use this file except in compliance with the
+# License. You can obtain a copy of the License at
+# http://www.netbeans.org/cddl-gplv2.html
+# or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+# specific language governing permissions and limitations under the
+# License.  When distributing the software, include this License Header
+# Notice in each file and include the License file at
+# nbbuild/licenses/CDDL-GPL-2-CP.  Sun designates this
+# particular file as subject to the "Classpath" exception as provided
+# by Sun in the GPL Version 2 section of the License file that
+# accompanied this code. If applicable, add the following below the
+# License Header, with the fields enclosed by brackets [] replaced by
+# your own identifying information:
+# "Portions Copyrighted [year] [name of copyright owner]"
+#
+# If you wish your version of this file to be governed by only the CDDL
+# or only the GPL Version 2, indicate your decision by adding
+# "[Contributor] elects to include this software in this distribution
+# under the [CDDL or GPL Version 2] license." If you do not indicate a
+# single choice of license, a recipient has the option to distribute
+# your version of this file under either the CDDL, the GPL Version 2 or
+# to extend the choice of license to its licensees as provided above.
+# However, if you add GPL Version 2 code and therefore, elected the GPL
+# Version 2 license, then the option applies only if the new code is
+# made subject to such option by the copyright holder.
+#
+# Contributor(s):
+#
+# Portions Copyrighted 2010 Sun Microsystems, Inc.
+
+perform_test() {
+    create_file src/test/Test.java <<EOF
+package test;
+public class Test {
+    private void test() {
+        String s = "foo".intern();
+    }
+}
+EOF
+
+    run_tool --apply --hint "String.intern() called on constant" src
+
+    assert_file_content  src/test/Test.java <<EOF
+package test;
+public class Test {
+    private void test() {
+        String s = "foo";
+    }
+}
+EOF
+}
+
+. `dirname $0`/harness

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-jackpot30/blob/9ed0a377/cmdline/tool/test/scripted/test-config-diamond
----------------------------------------------------------------------
diff --git a/cmdline/tool/test/scripted/test-config-diamond b/cmdline/tool/test/scripted/test-config-diamond
new file mode 100755
index 0000000..1b913a5
--- /dev/null
+++ b/cmdline/tool/test/scripted/test-config-diamond
@@ -0,0 +1,79 @@
+#!/bin/bash -x
+#
+# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+#
+# Copyright 2010 Sun Microsystems, Inc. All rights reserved.
+#
+# The contents of this file are subject to the terms of either the GNU
+# General Public License Version 2 only ("GPL") or the Common
+# Development and Distribution License("CDDL") (collectively, the
+# "License"). You may not use this file except in compliance with the
+# License. You can obtain a copy of the License at
+# http://www.netbeans.org/cddl-gplv2.html
+# or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+# specific language governing permissions and limitations under the
+# License.  When distributing the software, include this License Header
+# Notice in each file and include the License file at
+# nbbuild/licenses/CDDL-GPL-2-CP.  Sun designates this
+# particular file as subject to the "Classpath" exception as provided
+# by Sun in the GPL Version 2 section of the License file that
+# accompanied this code. If applicable, add the following below the
+# License Header, with the fields enclosed by brackets [] replaced by
+# your own identifying information:
+# "Portions Copyrighted [year] [name of copyright owner]"
+#
+# If you wish your version of this file to be governed by only the CDDL
+# or only the GPL Version 2, indicate your decision by adding
+# "[Contributor] elects to include this software in this distribution
+# under the [CDDL or GPL Version 2] license." If you do not indicate a
+# single choice of license, a recipient has the option to distribute
+# your version of this file under either the CDDL, the GPL Version 2 or
+# to extend the choice of license to its licensees as provided above.
+# However, if you add GPL Version 2 code and therefore, elected the GPL
+# Version 2 license, then the option applies only if the new code is
+# made subject to such option by the copyright holder.
+#
+# Contributor(s):
+#
+# Portions Copyrighted 2010 Sun Microsystems, Inc.
+
+perform_test() {
+    create_file src/test/Test.java <<EOF
+package test;
+import java.util.LinkedList;
+import java.util.List;
+public class Test {
+    private void test() {
+        List<String> l = new LinkedList<String>();
+    }
+}
+EOF
+
+    run_tool --apply --hint "Can Use Diamond" --config enabledVariants="" src
+
+    assert_file_content  src/test/Test.java <<EOF
+package test;
+import java.util.LinkedList;
+import java.util.List;
+public class Test {
+    private void test() {
+        List<String> l = new LinkedList<String>();
+    }
+}
+EOF
+
+    run_tool --apply --hint "Can Use Diamond" --config enabledVariants="initializer" src
+
+    assert_file_content  src/test/Test.java <<EOF
+package test;
+import java.util.LinkedList;
+import java.util.List;
+public class Test {
+    private void test() {
+        List<String> l = new LinkedList<>();
+    }
+}
+EOF
+}
+
+. `dirname $0`/harness

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-jackpot30/blob/9ed0a377/cmdline/tool/test/scripted/test-source-path
----------------------------------------------------------------------
diff --git a/cmdline/tool/test/scripted/test-source-path b/cmdline/tool/test/scripted/test-source-path
new file mode 100755
index 0000000..6050648
--- /dev/null
+++ b/cmdline/tool/test/scripted/test-source-path
@@ -0,0 +1,68 @@
+#!/bin/bash -x
+#
+# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+#
+# Copyright 2010 Sun Microsystems, Inc. All rights reserved.
+#
+# The contents of this file are subject to the terms of either the GNU
+# General Public License Version 2 only ("GPL") or the Common
+# Development and Distribution License("CDDL") (collectively, the
+# "License"). You may not use this file except in compliance with the
+# License. You can obtain a copy of the License at
+# http://www.netbeans.org/cddl-gplv2.html
+# or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+# specific language governing permissions and limitations under the
+# License.  When distributing the software, include this License Header
+# Notice in each file and include the License file at
+# nbbuild/licenses/CDDL-GPL-2-CP.  Sun designates this
+# particular file as subject to the "Classpath" exception as provided
+# by Sun in the GPL Version 2 section of the License file that
+# accompanied this code. If applicable, add the following below the
+# License Header, with the fields enclosed by brackets [] replaced by
+# your own identifying information:
+# "Portions Copyrighted [year] [name of copyright owner]"
+#
+# If you wish your version of this file to be governed by only the CDDL
+# or only the GPL Version 2, indicate your decision by adding
+# "[Contributor] elects to include this software in this distribution
+# under the [CDDL or GPL Version 2] license." If you do not indicate a
+# single choice of license, a recipient has the option to distribute
+# your version of this file under either the CDDL, the GPL Version 2 or
+# to extend the choice of license to its licensees as provided above.
+# However, if you add GPL Version 2 code and therefore, elected the GPL
+# Version 2 license, then the option applies only if the new code is
+# made subject to such option by the copyright holder.
+#
+# Contributor(s):
+#
+# Portions Copyrighted 2010 Sun Microsystems, Inc.
+
+perform_test() {
+    create_file src/test/Test.java <<EOF
+package test;
+public class Test {
+    private void test() {
+        String s = test2.Test2.C.intern();
+    }
+}
+EOF
+    create_file src/test2/Test2.java <<EOF
+package test2;
+public class Test2 {
+    public static final String C = "a";
+}
+EOF
+
+    run_tool --apply --hint "String.intern() called on constant" --sourcepath src src/test
+
+    assert_file_content  src/test/Test.java <<EOF
+package test;
+public class Test {
+    private void test() {
+        String s = test2.Test2.C;
+    }
+}
+EOF
+}
+
+. `dirname $0`/harness

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-jackpot30/blob/9ed0a377/cmdline/tool/test/unit/src/org/netbeans/modules/jackpot30/cmdline/CreateTool.java
----------------------------------------------------------------------
diff --git a/cmdline/tool/test/unit/src/org/netbeans/modules/jackpot30/cmdline/CreateTool.java b/cmdline/tool/test/unit/src/org/netbeans/modules/jackpot30/cmdline/CreateTool.java
new file mode 100644
index 0000000..20a24f0
--- /dev/null
+++ b/cmdline/tool/test/unit/src/org/netbeans/modules/jackpot30/cmdline/CreateTool.java
@@ -0,0 +1,72 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2009-2011 Sun Microsystems, Inc. All rights reserved.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ *
+ * Contributor(s):
+ *
+ * Portions Copyrighted 2009-2011 Sun Microsystems, Inc.
+ */
+package org.netbeans.modules.jackpot30.cmdline;
+
+import java.util.regex.Pattern;
+import org.netbeans.modules.jackpot30.cmdline.lib.CreateStandaloneJar;
+import org.netbeans.modules.jackpot30.cmdline.lib.CreateStandaloneJar.Info;
+import org.netbeans.modules.java.hints.declarative.PatternConvertorImpl;
+import org.netbeans.modules.java.hints.declarative.test.api.DeclarativeHintsTestBase;
+import org.netbeans.modules.java.hints.jackpot.spi.PatternConvertor;
+import org.netbeans.modules.java.j2seproject.J2SEProject;
+import org.netbeans.modules.java.platform.DefaultJavaPlatformProvider;
+import org.netbeans.modules.project.ui.OpenProjectsTrampolineImpl;
+
+/**
+ *
+ * @author lahvac
+ */
+public class CreateTool extends CreateStandaloneJar {
+
+    public CreateTool(String name) {
+        super(name, "jackpot");
+    }
+
+    @Override
+    protected Info computeInfo() {
+        return new Info().addAdditionalRoots(Main.class.getName(), DeclarativeHintsTestBase.class.getName(), OpenProjectsTrampolineImpl.class.getName(), J2SEProject.class.getName(), DefaultJavaPlatformProvider.class.getName(), PatternConvertorImpl.class.getName())
+                         .addAdditionalResources("org/netbeans/modules/java/hints/resources/Bundle.properties", "org/netbeans/modules/java/hints/declarative/resources/Bundle.properties")
+                         .addAdditionalLayers("org/netbeans/modules/java/hints/resources/layer.xml", "org/netbeans/modules/java/hints/declarative/resources/layer.xml")
+                         .addMetaInfRegistrations(new MetaInfRegistration(org.netbeans.modules.project.uiapi.OpenProjectsTrampoline.class, OpenProjectsTrampolineImpl.class))
+                         .addMetaInfRegistrationToCopy(PatternConvertor.class.getName())
+                         .addExcludePattern(Pattern.compile("junit\\.framework\\..*"))
+                         .setEscapeJavaxLang();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-jackpot30/blob/9ed0a377/cmdline/tool/test/unit/src/org/netbeans/modules/jackpot30/cmdline/CreateToolTest.java
----------------------------------------------------------------------
diff --git a/cmdline/tool/test/unit/src/org/netbeans/modules/jackpot30/cmdline/CreateToolTest.java b/cmdline/tool/test/unit/src/org/netbeans/modules/jackpot30/cmdline/CreateToolTest.java
new file mode 100644
index 0000000..ad92613
--- /dev/null
+++ b/cmdline/tool/test/unit/src/org/netbeans/modules/jackpot30/cmdline/CreateToolTest.java
@@ -0,0 +1,212 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2010-2011 Sun Microsystems, Inc. All rights reserved.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ *
+ * Contributor(s):
+ *
+ * Portions Copyrighted 2010-2011 Sun Microsystems, Inc.
+ */
+
+package org.netbeans.modules.jackpot30.cmdline;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Set;
+import junit.extensions.TestSetup;
+import junit.framework.Test;
+import org.hamcrest.Condition;
+import org.junit.runner.JUnitCore;
+import org.netbeans.junit.NbTestSuite;
+import org.openide.filesystems.FileUtil;
+import org.openide.util.Exceptions;
+
+/**
+ *
+ * @author lahvac
+ */
+public class CreateToolTest extends MainTest {
+
+    public CreateToolTest(String name) {
+        super(name);
+    }
+
+    private static File compiler;
+
+    @Override
+    protected void reallyRunCompiler(File workingDir, int exitcode, String[] output, String... params) throws Exception {
+        assertNotNull(compiler);
+        List<String> ll = new LinkedList<String>();
+        ll.add("java");
+        ll.add("-Xbootclasspath/p:" + compiler.getAbsolutePath());
+
+//        ll.add("-Xdebug");
+//        ll.add("-Xrunjdwp:transport=dt_socket,suspend=y,server=y,address=8889");
+
+        ll.add("org.netbeans.modules.jackpot30.cmdline.Main");
+        ll.addAll(Arrays.asList(params));
+        try {
+            Process p = Runtime.getRuntime().exec(ll.toArray(new String[0]), null, workingDir);
+            CopyStream outCopy = new CopyStream(p.getInputStream(), output, 0);
+            CopyStream errCopy = new CopyStream(p.getErrorStream(), output, 1);
+
+            outCopy.start();
+            errCopy.start();
+
+            assertEquals(exitcode, p.waitFor());
+
+            outCopy.doJoin();
+            errCopy.doJoin();
+        } catch (Throwable t) {
+            throw new IOException(t);
+        }
+    }
+    
+    private static final Set<String> MUST_CONTAIN = new HashSet<String>(Arrays.asList(" 1 failure", "/h.test/neg", "Tests run: 2,  Failures: 1"));
+    private static final Set<String> MUST_NOT_CONTAIN = new HashSet<String>(Arrays.asList("/h.test/pos"));
+
+    @Override
+    protected void runAndTest(File classes) throws Exception {
+        List<String> ll = new LinkedList<String>();
+        ll.add("java");
+        ll.add("-classpath");
+        ll.add(compiler.getAbsolutePath() + File.pathSeparator + classes.getAbsolutePath() + File.pathSeparator + new File(JUnitCore.class.getProtectionDomain().getCodeSource().getLocation().toURI()).getAbsolutePath() + File.pathSeparator + new File(Condition.class.getProtectionDomain().getCodeSource().getLocation().toURI()).getAbsolutePath());
+
+//        ll.add("-Xdebug");
+//        ll.add("-Xrunjdwp:transport=dt_socket,suspend=y,server=y,address=8889");
+
+        ll.add("org.junit.runner.JUnitCore");
+        ll.add("org.netbeans.modules.jackpot30.cmdline.testtool.DoRunTests");
+
+        System.err.println("ll=" + ll);
+        Process p = Runtime.getRuntime().exec(ll.toArray(new String[0]), null, getWorkDir());
+        String[] output = new String[2];
+        CopyStream outCopy = new CopyStream(p.getInputStream(), output, 0);
+        CopyStream errCopy = new CopyStream(p.getErrorStream(), output, 1);
+
+        outCopy.start();
+        errCopy.start();
+
+        p.waitFor();
+
+        outCopy.doJoin();
+        errCopy.doJoin();
+
+        Set<String> mustContainCopy = new HashSet<String>(MUST_CONTAIN);
+
+        System.err.println(output[0]);
+        System.err.println(output[1]);
+
+        verify(output[0], mustContainCopy);
+        verify(output[1], mustContainCopy);
+    }
+
+    private void verify(String output, Set<String> mustContainCopy) {
+        for (Iterator<String> it = mustContainCopy.iterator(); it.hasNext();) {
+            assertTrue(output.contains(it.next()));
+            it.remove();
+        }
+        for (String nc : MUST_NOT_CONTAIN) {
+            assertFalse(output.contains(nc));
+        }
+    }
+
+    private static final class CopyStream extends Thread {
+        private final InputStream ins;
+        private final ByteArrayOutputStream out;
+        private final String[] target;
+        private final int targetIndex;
+
+        public CopyStream(InputStream ins, String[] target, int targetIndex) {
+            this.ins = ins;
+            this.out = new ByteArrayOutputStream();
+            this.target = target;
+            this.targetIndex = targetIndex;
+        }
+
+        @Override
+        public void run() {
+            try {
+                FileUtil.copy(ins, out);
+            } catch (IOException ex) {
+                Exceptions.printStackTrace(ex);
+            } finally {
+                try {
+                    ins.close();
+                } catch (IOException ex) {
+                    Exceptions.printStackTrace(ex);
+                }
+            }
+        }
+
+        public void doJoin() throws InterruptedException, IOException {
+            join(60000);
+            out.close();
+            target[targetIndex] = new String(out.toByteArray());
+        }
+
+    }
+
+    public static Test suite() {
+        NbTestSuite suite = new NbTestSuite();
+        
+        suite.addTestSuite(CreateToolTest.class);
+
+        return new TestSetup(suite) {
+            private File hintsList;
+            protected void setUp() throws Exception {
+                compiler = File.createTempFile("jackpot", ".jar");
+//                compiler = new File("/tmp/jackpot.jar");
+                hintsList = File.createTempFile("hints", "list");
+
+//                if (!compiler.canRead()) {
+                    new CreateTool("").createCompiler(compiler, hintsList);
+//                }
+            }
+            protected void tearDown() {
+                compiler.delete();
+                hintsList.delete();
+                compiler = null;
+                hintsList = null;
+            }
+        };
+    }
+
+}