Commits

Jan Lahoda committed 3a13891

Splitting the hints module into more manageable parts.

Comments (0)

Files changed (124)

+<?xml version="1.0" encoding="UTF-8"?>
+<!-- You may freely edit this file. See harness/README in the NetBeans platform -->
+<!-- for some information on what you could do (e.g. targets to override). -->
+<!-- If you delete this file and reopen the project it will be recreated. -->
+<project name="org.netbeans.modules.jackpot30.api" default="netbeans" basedir=".">
+    <description>Builds, tests, and runs the project org.netbeans.modules.jackpot30.api.</description>
+    <import file="nbproject/build-impl.xml"/>
+</project>
+Manifest-Version: 1.0
+OpenIDE-Module: org.netbeans.modules.jackpot30.api
+OpenIDE-Module-Implementation-Version: 1
+OpenIDE-Module-Layer: org/netbeans/modules/jackpot30/impl/resources/layer.xml
+OpenIDE-Module-Localizing-Bundle: org/netbeans/modules/jackpot30/impl/resources/Bundle.properties
+

api/nbproject/build-impl.xml

+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+*** GENERATED FROM project.xml - DO NOT EDIT  ***
+***         EDIT ../build.xml INSTEAD         ***
+-->
+<project name="org.netbeans.modules.jackpot30.api-impl" basedir="..">
+    <fail message="Please build using Ant 1.7.1 or higher.">
+        <condition>
+            <not>
+                <antversion atleast="1.7.1"/>
+            </not>
+        </condition>
+    </fail>
+    <property file="nbproject/private/suite-private.properties"/>
+    <property file="nbproject/suite.properties"/>
+    <fail unless="suite.dir">You must set 'suite.dir' to point to your containing module suite</fail>
+    <property file="${suite.dir}/nbproject/private/platform-private.properties"/>
+    <property file="${suite.dir}/nbproject/platform.properties"/>
+    <macrodef name="property" uri="http://www.netbeans.org/ns/nb-module-project/2">
+        <attribute name="name"/>
+        <attribute name="value"/>
+        <sequential>
+            <property name="@{name}" value="${@{value}}"/>
+        </sequential>
+    </macrodef>
+    <macrodef name="evalprops" uri="http://www.netbeans.org/ns/nb-module-project/2">
+        <attribute name="property"/>
+        <attribute name="value"/>
+        <sequential>
+            <property name="@{property}" value="@{value}"/>
+        </sequential>
+    </macrodef>
+    <property file="${user.properties.file}"/>
+    <nbmproject2:property name="harness.dir" value="nbplatform.${nbplatform.active}.harness.dir" xmlns:nbmproject2="http://www.netbeans.org/ns/nb-module-project/2"/>
+    <nbmproject2:property name="nbplatform.active.dir" value="nbplatform.${nbplatform.active}.netbeans.dest.dir" xmlns:nbmproject2="http://www.netbeans.org/ns/nb-module-project/2"/>
+    <nbmproject2:evalprops property="cluster.path.evaluated" value="${cluster.path}" xmlns:nbmproject2="http://www.netbeans.org/ns/nb-module-project/2"/>
+    <fail message="Path to 'platform' cluster missing in $${cluster.path} property or using corrupt Netbeans Platform (missing harness).">
+        <condition>
+            <not>
+                <contains string="${cluster.path.evaluated}" substring="platform"/>
+            </not>
+        </condition>
+    </fail>
+    <import file="${harness.dir}/build.xml"/>
+</project>

api/nbproject/genfiles.properties

+build.xml.data.CRC32=f9c1509c
+build.xml.script.CRC32=0abcaef9
+build.xml.stylesheet.CRC32=79c3b980@1.28.0.7
+# This file is used by a NetBeans-based IDE to track changes in generated files such as build-impl.xml.
+# Do not edit this file. You may delete it but then the IDE will never regenerate such files for you.
+nbproject/build-impl.xml.data.CRC32=f9c1509c
+nbproject/build-impl.xml.script.CRC32=c4574e66
+nbproject/build-impl.xml.stylesheet.CRC32=261c4bef@1.28.0.7

api/nbproject/project.properties

+javac.source=1.5
+javac.compilerargs=-Xlint -Xlint:-serial
+spec.version.base=1.0

api/nbproject/project.xml

+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://www.netbeans.org/ns/project/1">
+    <type>org.netbeans.modules.apisupport.project</type>
+    <configuration>
+        <data xmlns="http://www.netbeans.org/ns/nb-module-project/3">
+            <code-name-base>org.netbeans.modules.jackpot30.api</code-name-base>
+            <suite-component/>
+            <module-dependencies>
+                <dependency>
+                    <code-name-base>org.jdesktop.layout</code-name-base>
+                    <build-prerequisite/>
+                    <compile-dependency/>
+                    <run-dependency>
+                        <release-version>1</release-version>
+                        <specification-version>1.7.0.103</specification-version>
+                    </run-dependency>
+                </dependency>
+                <dependency>
+                    <code-name-base>org.netbeans.api.java.classpath</code-name-base>
+                    <build-prerequisite/>
+                    <compile-dependency/>
+                    <run-dependency>
+                        <release-version>1</release-version>
+                        <specification-version>1.19</specification-version>
+                    </run-dependency>
+                </dependency>
+                <dependency>
+                    <code-name-base>org.netbeans.api.progress</code-name-base>
+                    <build-prerequisite/>
+                    <compile-dependency/>
+                    <run-dependency>
+                        <release-version>1</release-version>
+                        <specification-version>1.13.0.1</specification-version>
+                    </run-dependency>
+                </dependency>
+                <dependency>
+                    <code-name-base>org.netbeans.libs.javacapi</code-name-base>
+                    <build-prerequisite/>
+                    <compile-dependency/>
+                    <run-dependency>
+                        <specification-version>0.8.0.1</specification-version>
+                    </run-dependency>
+                </dependency>
+                <dependency>
+                    <code-name-base>org.netbeans.libs.javacimpl</code-name-base>
+                    <build-prerequisite/>
+                    <compile-dependency/>
+                    <run-dependency>
+                        <release-version>1</release-version>
+                        <implementation-version/>
+                    </run-dependency>
+                </dependency>
+                <dependency>
+                    <code-name-base>org.netbeans.modules.apisupport.project</code-name-base>
+                    <build-prerequisite/>
+                    <compile-dependency/>
+                    <run-dependency>
+                        <implementation-version/>
+                    </run-dependency>
+                </dependency>
+                <dependency>
+                    <code-name-base>org.netbeans.modules.editor.lib</code-name-base>
+                    <build-prerequisite/>
+                    <compile-dependency/>
+                    <run-dependency>
+                        <release-version>1</release-version>
+                        <specification-version>1.38.0.9.2</specification-version>
+                    </run-dependency>
+                </dependency>
+                <dependency>
+                    <code-name-base>org.netbeans.modules.java.editor</code-name-base>
+                    <build-prerequisite/>
+                    <compile-dependency/>
+                    <run-dependency>
+                        <release-version>1</release-version>
+                        <implementation-version/>
+                    </run-dependency>
+                </dependency>
+                <dependency>
+                    <code-name-base>org.netbeans.modules.java.hints</code-name-base>
+                    <build-prerequisite/>
+                    <compile-dependency/>
+                    <run-dependency>
+                        <release-version>1</release-version>
+                        <implementation-version/>
+                    </run-dependency>
+                </dependency>
+                <dependency>
+                    <code-name-base>org.netbeans.modules.java.source</code-name-base>
+                    <build-prerequisite/>
+                    <compile-dependency/>
+                    <run-dependency>
+                        <implementation-version/>
+                    </run-dependency>
+                </dependency>
+                <dependency>
+                    <code-name-base>org.netbeans.modules.parsing.api</code-name-base>
+                    <build-prerequisite/>
+                    <compile-dependency/>
+                    <run-dependency>
+                        <specification-version>1.12.1</specification-version>
+                    </run-dependency>
+                </dependency>
+                <dependency>
+                    <code-name-base>org.netbeans.modules.projectapi</code-name-base>
+                    <build-prerequisite/>
+                    <compile-dependency/>
+                    <run-dependency>
+                        <release-version>1</release-version>
+                        <specification-version>1.24</specification-version>
+                    </run-dependency>
+                </dependency>
+                <dependency>
+                    <code-name-base>org.netbeans.modules.projectuiapi</code-name-base>
+                    <build-prerequisite/>
+                    <compile-dependency/>
+                    <run-dependency>
+                        <release-version>1</release-version>
+                        <specification-version>1.35.0.6</specification-version>
+                    </run-dependency>
+                </dependency>
+                <dependency>
+                    <code-name-base>org.netbeans.modules.sendopts</code-name-base>
+                    <build-prerequisite/>
+                    <compile-dependency/>
+                    <run-dependency>
+                        <release-version>2</release-version>
+                        <specification-version>2.4</specification-version>
+                    </run-dependency>
+                </dependency>
+                <dependency>
+                    <code-name-base>org.netbeans.spi.editor.hints</code-name-base>
+                    <build-prerequisite/>
+                    <compile-dependency/>
+                    <run-dependency>
+                        <release-version>0-1</release-version>
+                        <specification-version>1.7.0.7.2</specification-version>
+                    </run-dependency>
+                </dependency>
+                <dependency>
+                    <code-name-base>org.openide.awt</code-name-base>
+                    <build-prerequisite/>
+                    <compile-dependency/>
+                    <run-dependency>
+                        <specification-version>7.7.0.1</specification-version>
+                    </run-dependency>
+                </dependency>
+                <dependency>
+                    <code-name-base>org.openide.dialogs</code-name-base>
+                    <build-prerequisite/>
+                    <compile-dependency/>
+                    <run-dependency>
+                        <specification-version>7.10</specification-version>
+                    </run-dependency>
+                </dependency>
+                <dependency>
+                    <code-name-base>org.openide.filesystems</code-name-base>
+                    <build-prerequisite/>
+                    <compile-dependency/>
+                    <run-dependency>
+                        <specification-version>7.21.0.1.1</specification-version>
+                    </run-dependency>
+                </dependency>
+                <dependency>
+                    <code-name-base>org.openide.loaders</code-name-base>
+                    <build-prerequisite/>
+                    <compile-dependency/>
+                    <run-dependency>
+                        <specification-version>7.5</specification-version>
+                    </run-dependency>
+                </dependency>
+                <dependency>
+                    <code-name-base>org.openide.modules</code-name-base>
+                    <build-prerequisite/>
+                    <compile-dependency/>
+                    <run-dependency>
+                        <specification-version>7.10</specification-version>
+                    </run-dependency>
+                </dependency>
+                <dependency>
+                    <code-name-base>org.openide.nodes</code-name-base>
+                    <build-prerequisite/>
+                    <compile-dependency/>
+                    <run-dependency>
+                        <specification-version>7.9.0.1</specification-version>
+                    </run-dependency>
+                </dependency>
+                <dependency>
+                    <code-name-base>org.openide.text</code-name-base>
+                    <build-prerequisite/>
+                    <compile-dependency/>
+                    <run-dependency>
+                        <specification-version>6.22</specification-version>
+                    </run-dependency>
+                </dependency>
+                <dependency>
+                    <code-name-base>org.openide.util</code-name-base>
+                    <build-prerequisite/>
+                    <compile-dependency/>
+                    <run-dependency>
+                        <specification-version>7.22.0.1</specification-version>
+                    </run-dependency>
+                </dependency>
+            </module-dependencies>
+            <test-dependencies>
+                <test-type>
+                    <name>unit</name>
+                    <test-dependency>
+                        <code-name-base>org.netbeans.bootstrap</code-name-base>
+                    </test-dependency>
+                    <test-dependency>
+                        <code-name-base>org.netbeans.core.startup</code-name-base>
+                    </test-dependency>
+                    <test-dependency>
+                        <code-name-base>org.netbeans.modules.classfile</code-name-base>
+                    </test-dependency>
+                    <test-dependency>
+                        <code-name-base>org.netbeans.modules.defaults</code-name-base>
+                    </test-dependency>
+                    <test-dependency>
+                        <code-name-base>org.netbeans.modules.editor</code-name-base>
+                    </test-dependency>
+                    <test-dependency>
+                        <code-name-base>org.netbeans.modules.editor.mimelookup</code-name-base>
+                    </test-dependency>
+                    <test-dependency>
+                        <code-name-base>org.netbeans.modules.editor.settings</code-name-base>
+                    </test-dependency>
+                    <test-dependency>
+                        <code-name-base>org.netbeans.modules.editor.settings.storage</code-name-base>
+                    </test-dependency>
+                    <test-dependency>
+                        <code-name-base>org.netbeans.modules.editor.util</code-name-base>
+                    </test-dependency>
+                    <test-dependency>
+                        <code-name-base>org.netbeans.modules.jackpot30.test.borrowed</code-name-base>
+                        <recursive/>
+                        <compile-dependency/>
+                        <test/>
+                    </test-dependency>
+                    <test-dependency>
+                        <code-name-base>org.netbeans.modules.java.lexer</code-name-base>
+                    </test-dependency>
+                    <test-dependency>
+                        <code-name-base>org.netbeans.modules.masterfs</code-name-base>
+                    </test-dependency>
+                    <test-dependency>
+                        <code-name-base>org.netbeans.modules.progress.ui</code-name-base>
+                    </test-dependency>
+                    <test-dependency>
+                        <code-name-base>org.netbeans.modules.projectui</code-name-base>
+                    </test-dependency>
+                    <test-dependency>
+                        <code-name-base>org.openidex.util</code-name-base>
+                    </test-dependency>
+                </test-type>
+            </test-dependencies>
+            <public-packages>
+                <package>org.netbeans.modules.jackpot30.spi</package>
+                <package>org.netbeans.modules.jackpot30.spi.support</package>
+            </public-packages>
+        </data>
+    </configuration>
+</project>

api/nbproject/suite.properties

+suite.dir=${basedir}/..

api/src/org/netbeans/modules/jackpot30/impl/RulesManager.java

+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2008-2009 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 2008-2009 Sun Microsystems, Inc.
+ */
+
+package org.netbeans.modules.jackpot30.impl;
+
+import com.sun.source.tree.Tree.Kind;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import org.netbeans.modules.jackpot30.spi.HintDescription;
+import org.netbeans.modules.jackpot30.spi.HintDescription.PatternDescription;
+import org.netbeans.modules.jackpot30.spi.HintProvider;
+import org.openide.util.Lookup;
+
+/**
+ *
+ * @author lahvac
+ */
+public class RulesManager {
+
+    private final Map<Kind, List<HintDescription>> kind2Hints = new HashMap<Kind, List<HintDescription>>();
+    private final Map<PatternDescription, List<HintDescription>> pattern2Hint = new HashMap<PatternDescription, List<HintDescription>>();
+
+    private static final RulesManager INSTANCE = new RulesManager();
+
+    public static RulesManager getInstance() {
+        return INSTANCE;
+    }
+
+    private RulesManager() {
+        for (HintProvider p : Lookup.getDefault().lookupAll(HintProvider.class)) {
+            for (HintDescription d : p.computeHints()) {
+                if (d.getTriggerKind() != null) {
+                    List<HintDescription> l = kind2Hints.get(d.getTriggerKind());
+
+                    if (l == null) {
+                        kind2Hints.put(d.getTriggerKind(), l = new LinkedList<HintDescription>());
+                    }
+
+                    l.add(d);
+                }
+                if (d.getTriggerPattern() != null) {
+                    List<HintDescription> l = pattern2Hint.get(d.getTriggerPattern());
+
+                    if (l == null) {
+                        pattern2Hint.put(d.getTriggerPattern(), l = new LinkedList<HintDescription>());
+                    }
+                    
+                    l.add(d);
+                }
+            }
+        }
+    }
+
+    public Map<Kind, List<HintDescription>> getKindBasedHints() {
+        return kind2Hints;
+    }
+
+    public Map<PatternDescription, List<HintDescription>> getPatternBasedHints() {
+        return pattern2Hint;
+    }
+
+}

api/src/org/netbeans/modules/jackpot30/impl/Utilities.java

+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2008-2009 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 2008-2009 Sun Microsystems, Inc.
+ */
+
+package org.netbeans.modules.jackpot30.impl;
+
+import com.sun.source.tree.AnnotationTree;
+import com.sun.source.tree.AssignmentTree;
+import com.sun.source.tree.ExpressionTree;
+import com.sun.source.tree.IdentifierTree;
+import com.sun.source.tree.LiteralTree;
+import com.sun.source.tree.MemberSelectTree;
+import com.sun.source.tree.NewArrayTree;
+import com.sun.source.tree.Tree;
+import com.sun.source.tree.Tree.Kind;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.type.TypeMirror;
+import org.netbeans.api.java.source.TreeMaker;
+import org.netbeans.api.java.source.WorkingCopy;
+import org.openide.util.NbCollections;
+
+/**
+ *
+ * @author Jan Lahoda
+ */
+public class Utilities {
+
+    private Utilities() {}
+
+    public static <E> Iterable<E> checkedIterableByFilter(final Iterable raw, final Class<E> type, final boolean strict) {
+        return new Iterable<E>() {
+            public Iterator<E> iterator() {
+                return NbCollections.checkedIteratorByFilter(raw.iterator(), type, strict);
+            }
+        };
+    }
+    
+//    public static AnnotationTree constructConstraint(WorkingCopy wc, String name, TypeMirror tm) {
+//        TreeMaker make = wc.getTreeMaker();
+//        ExpressionTree variable = prepareAssignment(make, "variable", make.Literal(name));
+//        ExpressionTree type     = prepareAssignment(make, "type", make.MemberSelect((ExpressionTree) make.Type(wc.getTypes().erasure(tm)), "class"));
+//        TypeElement constraint  = wc.getElements().getTypeElement(Annotations.CONSTRAINT.toFQN());
+//
+//        return make.Annotation(make.QualIdent(constraint), Arrays.asList(variable, type));
+//    }
+
+    public static ExpressionTree prepareAssignment(TreeMaker make, String name, ExpressionTree value) {
+        return make.Assignment(make.Identifier(name), value);
+    }
+
+    public static ExpressionTree findValue(AnnotationTree m, String name) {
+        for (ExpressionTree et : m.getArguments()) {
+            if (et.getKind() == Kind.ASSIGNMENT) {
+                AssignmentTree at = (AssignmentTree) et;
+                String varName = ((IdentifierTree) at.getVariable()).getName().toString();
+
+                if (varName.equals(name)) {
+                    return at.getExpression();
+                }
+            }
+
+            if (et instanceof LiteralTree/*XXX*/ && "value".equals(name)) {
+                return et;
+            }
+        }
+
+        return null;
+    }
+
+    public static List<AnnotationTree> findArrayValue(AnnotationTree at, String name) {
+        ExpressionTree fixesArray = findValue(at, name);
+        List<AnnotationTree> fixes = new LinkedList<AnnotationTree>();
+
+        if (fixesArray != null && fixesArray.getKind() == Kind.NEW_ARRAY) {
+            NewArrayTree trees = (NewArrayTree) fixesArray;
+
+            for (ExpressionTree fix : trees.getInitializers()) {
+                if (fix.getKind() == Kind.ANNOTATION) {
+                    fixes.add((AnnotationTree) fix);
+                }
+            }
+        }
+
+        if (fixesArray != null && fixesArray.getKind() == Kind.ANNOTATION) {
+            fixes.add((AnnotationTree) fixesArray);
+        }
+        
+        return fixes;
+    }
+
+    public static boolean isPureMemberSelect(Tree mst, boolean allowVariables) {
+        switch (mst.getKind()) {
+            case IDENTIFIER: return allowVariables || ((IdentifierTree) mst).getName().charAt(0) != '$';
+            case MEMBER_SELECT: return isPureMemberSelect(((MemberSelectTree) mst).getExpression(), allowVariables);
+            default: return false;
+        }
+    }
+
+}

api/src/org/netbeans/modules/jackpot30/impl/batch/BatchApply.java

+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2008-2009 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 2008-2009 Sun Microsystems, Inc.
+ */
+
+package org.netbeans.modules.jackpot30.impl.batch;
+
+import com.sun.source.tree.CompilationUnitTree;
+import com.sun.source.tree.ImportTree;
+import com.sun.source.util.TreePath;
+import java.awt.Dialog;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.IdentityHashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Queue;
+import java.util.Set;
+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.SwingUtilities;
+import javax.swing.text.Document;
+import org.netbeans.api.java.classpath.ClassPath;
+import org.netbeans.api.java.source.ClasspathInfo;
+import org.netbeans.api.java.source.CompilationController;
+import org.netbeans.api.java.source.JavaSource;
+import org.netbeans.api.java.source.JavaSource.Phase;
+import org.netbeans.api.java.source.ModificationResult;
+import org.netbeans.api.java.source.Task;
+import org.netbeans.api.java.source.TreePathHandle;
+import org.netbeans.api.java.source.WorkingCopy;
+import org.netbeans.api.progress.ProgressHandle;
+import org.netbeans.api.progress.ProgressHandleFactory;
+import org.netbeans.api.project.Project;
+import org.netbeans.api.project.ProjectUtils;
+import org.netbeans.api.project.SourceGroup;
+import org.netbeans.api.project.Sources;
+//import org.netbeans.modules.jackpot30.hints.pm.AnnotationBasedHintsRunner;
+import org.netbeans.modules.jackpot30.spi.JavaFix;
+import org.netbeans.modules.java.editor.semantic.SemanticHighlighter;
+import org.netbeans.modules.java.hints.errors.SuppressWarningsFixer;
+import org.netbeans.modules.java.hints.infrastructure.HintsTask;
+import org.netbeans.modules.java.hints.infrastructure.RulesManager;
+import org.netbeans.modules.java.hints.options.HintsSettings;
+import org.netbeans.modules.java.hints.spi.AbstractHint;
+import org.netbeans.modules.java.hints.spi.AbstractHint.HintSeverity;
+import org.netbeans.modules.java.hints.spi.TreeRule;
+import org.netbeans.spi.editor.hints.ErrorDescription;
+import org.netbeans.spi.editor.hints.Fix;
+import org.openide.DialogDescriptor;
+import org.openide.DialogDisplayer;
+import org.openide.LifecycleManager;
+import org.openide.cookies.EditorCookie;
+import org.openide.cookies.SaveCookie;
+import org.openide.filesystems.FileObject;
+import org.openide.filesystems.FileUtil;
+import org.openide.loaders.DataObject;
+import org.openide.util.Cancellable;
+import org.openide.util.Exceptions;
+import org.openide.util.Lookup;
+import org.openide.util.RequestProcessor;
+
+/**
+ *
+ * @author Jan Lahoda
+ */
+public class BatchApply {
+
+    private static final RequestProcessor WORKER = new RequestProcessor("Batch Hint Apply");
+    
+    public static String applyFixes(final Lookup context, final String enabledHints, boolean progress) {
+        assert !progress || SwingUtilities.isEventDispatchThread();
+
+        if (progress) {
+            final AtomicBoolean cancel = new AtomicBoolean();
+            final ProgressHandle handle = ProgressHandleFactory.createHandle("Batch Hint Apply", new Cancellable() {
+                public boolean cancel() {
+                    cancel.set(true);
+
+                    return true;
+                }
+            });
+            
+            try {
+                DialogDescriptor dd = new DialogDescriptor(ProgressHandleFactory.createProgressComponent(handle),
+                                                           "Batch Hint Apply",
+                                                           true,
+                                                           new Object[] {DialogDescriptor.CANCEL_OPTION},
+                                                           DialogDescriptor.CANCEL_OPTION,
+                                                           DialogDescriptor.DEFAULT_ALIGN,
+                                                           null,
+                                                           new ActionListener() {
+                    public void actionPerformed(ActionEvent e) {
+                        cancel.set(true);
+                    }
+                });
+                final Dialog d = DialogDisplayer.getDefault().createDialog(dd);
+                final String[] result = new String[1];
+
+                Runnable exec = new Runnable() {
+
+                    public void run() {
+                        result[0] = applyFixesImpl(context, enabledHints, handle, cancel);
+
+                        SwingUtilities.invokeLater(new Runnable() {
+                            public void run() {
+                                d.setVisible(false);
+                            }
+                        });
+                    }
+                };
+
+                WORKER.post(exec);
+
+                d.setVisible(true);
+
+                return result[0];
+            } finally {
+                handle.finish();
+            }
+        } else {
+            return applyFixesImpl(context, enabledHints, null, new AtomicBoolean());
+        }
+    }
+
+    private static String applyFixesImpl(Lookup context, String enabledHints, ProgressHandle h, AtomicBoolean cancel) {
+        ProgressHandleWrapper handle = new ProgressHandleWrapper(h, new int[] {20, 40, 40});
+
+        Pattern hints = Pattern.compile(enabledHints);
+        List<ErrorDescription> eds = new LinkedList<ErrorDescription>();
+        Collection<FileObject> toProcess = toProcess(context);
+
+        handle.startNextPart(toProcess.size());
+        
+        Collection<FileObject> allSources = findAllSources(toProcess);
+        Map<ClasspathInfo, Collection<FileObject>> sortedFiles = sortFiles(allSources);
+
+        handle.startNextPart(allSources.size());
+
+        for (Entry<ClasspathInfo, Collection<FileObject>> e: sortedFiles.entrySet()) {
+            if (cancel.get()) return null;
+            
+            eds.addAll(processFiles(e.getKey(), e.getValue(), hints, handle, cancel));
+        }
+
+        Map<ErrorDescription, Fix> fixes = new IdentityHashMap<ErrorDescription, Fix>();
+        Map<FileObject, List<JavaFix>> fastFixes = new HashMap<FileObject, List<JavaFix>>();
+        List<ErrorDescription> edsWithSlowsFixes = new LinkedList<ErrorDescription>();
+
+        //verify that there is exactly one fix for each ED:
+        for (ErrorDescription ed : eds) {
+            if (cancel.get()) return null;
+            
+            if (!ed.getFixes().isComputed()) {
+                return "Not computed fixes for: " + ed.getDescription();
+            }
+
+            Fix fix = null;
+
+            for (Fix f : ed.getFixes().getFixes()) {
+//                if (!(f instanceof SuppressWarningsFixer.FixImpl)) {
+                    if (fix != null) {
+                        fix = null;
+                        break;
+                    }
+
+                    fix = f;
+//                }
+            }
+
+            if (fix == null) {
+                return "Not exactly one fix for: " + ed.getDescription() + ", fixes=" + ed.getFixes().getFixes();
+            }
+
+            if (fix instanceof JavaFixImpl) {
+                JavaFixImpl ajf = (JavaFixImpl) fix;
+                FileObject file = JavaFixImpl.Accessor.INSTANCE.getFile(ajf.jf);
+                List<JavaFix> fs = fastFixes.get(file);
+
+                if (fs == null) {
+                    fastFixes.put(file, fs = new LinkedList<JavaFix>());
+                }
+
+                fs.add(ajf.jf);
+            } else {
+                fixes.put(ed, fix);
+                edsWithSlowsFixes.add(ed);
+            }
+        }
+
+        handle.startNextPart(eds.size());
+
+        try {
+            List<ModificationResult> results = performFastFixes(fastFixes, handle, cancel);
+
+            if (cancel.get()) return null;
+
+            performSlowFixes(edsWithSlowsFixes, fixes, handle);
+
+            if (cancel.get()) return null;
+
+            List<FileObject> fastFiles = new LinkedList<FileObject>();
+
+            for (ModificationResult r : results) {
+                r.commit();
+                fastFiles.addAll(r.getModifiedFileObjects());
+            }
+
+            Map<ClasspathInfo, Collection<FileObject>> sortedFastFiles = sortFiles(fastFiles);
+
+            for (Entry<ClasspathInfo, Collection<FileObject>> e : sortedFastFiles.entrySet()) {
+                JavaSource.create(e.getKey(), e.getValue()).runModificationTask(new RemoveUnusedImports()).commit();
+            }
+
+            LifecycleManager.getDefault().saveAll();
+        } catch (Exception ex) {
+            Exceptions.printStackTrace(ex);
+            return ex.getLocalizedMessage();
+        }
+        
+        return null;
+    }
+
+    private static List<ErrorDescription> processFiles(ClasspathInfo cpInfo, Collection<FileObject> toProcess, final Pattern hints, final ProgressHandleWrapper handle, final AtomicBoolean cancel) {
+        final List<ErrorDescription> eds = new LinkedList<ErrorDescription>();
+        //XXX: workarounding NB issues #154252 and #152534:
+        for (FileObject file : toProcess) {
+//        JavaSource js = JavaSource.create(cpInfo, toProcess);
+        try {
+            DataObject d = DataObject.find(file);
+            EditorCookie ec = d.getLookup().lookup(EditorCookie.class);
+            Document doc = ec.openDocument();
+            JavaSource js = JavaSource.create(cpInfo, file);
+
+            js.runUserActionTask(new Task<CompilationController>() {
+                public void run(CompilationController cc) throws Exception {
+                    if (cancel.get()) return ;
+                    
+//                    DataObject d = DataObject.find(cc.getFileObject());
+//                    EditorCookie ec = d.getLookup().lookup(EditorCookie.class);
+//                    Document doc = ec.openDocument();
+
+                    try {
+                        if (cc.toPhase(JavaSource.Phase.RESOLVED).compareTo(JavaSource.Phase.RESOLVED) < 0) {
+                            return;
+                        }
+
+                        //XXX:
+//                        eds.addAll(new AnnotationBasedHintsRunner().findWarnings(cc, hints));
+                    } finally {
+                        HintsSettings.setPreferencesOverride(null);
+                    }
+
+                    handle.tick();
+                }
+            }, true);
+        } catch (IOException ex) {
+            Exceptions.printStackTrace(ex);
+        }
+        }
+
+        return eds;
+    }
+
+    private static String performSlowFixes(List<ErrorDescription> edsWithSlowsFixes, Map<ErrorDescription, Fix> fixes, ProgressHandleWrapper handle) throws Exception {
+        for (ErrorDescription ed : edsWithSlowsFixes) {
+            try {
+                DataObject d = DataObject.find(ed.getFile());
+                EditorCookie ec = d.getLookup().lookup(EditorCookie.class);
+                Document doc = ec.openDocument();
+
+                fixes.get(ed).implement();
+
+                JavaSource.forFileObject(ed.getFile()).runModificationTask(new RemoveUnusedImports()).commit();
+                
+                SaveCookie sc = d.getLookup().lookup(SaveCookie.class);
+
+                if (sc != null) {
+                    sc.save();
+                }
+            } catch (Exception ex) {
+                Exceptions.attachMessage(ex, FileUtil.getFileDisplayName(ed.getFile()));
+                
+                throw ex;
+            }
+
+            handle.tick();
+        }
+        return null;
+    }
+
+    private static final class RemoveUnusedImports implements Task<WorkingCopy> {
+        public void run(WorkingCopy wc) throws IOException {
+            if (wc.toPhase(Phase.RESOLVED).compareTo(Phase.RESOLVED) < 0) {
+                return;
+            }
+
+            //compute imports to remove:
+            List<TreePathHandle> unusedImports = SemanticHighlighter.computeUnusedImports(wc);
+            CompilationUnitTree cut = wc.getCompilationUnit();
+            // make the changes to the source
+            for (TreePathHandle handle : unusedImports) {
+                TreePath path = handle.resolve(wc);
+                assert path != null;
+                cut = wc.getTreeMaker().removeCompUnitImport(cut,
+                        (ImportTree) path.getLeaf());
+            }
+
+            if (!unusedImports.isEmpty()) {
+                wc.rewrite(wc.getCompilationUnit(), cut);
+            }
+        }
+    }
+
+    private static List<ModificationResult> performFastFixes(Map<FileObject, List<JavaFix>> fastFixes, ProgressHandleWrapper handle, AtomicBoolean cancel) {
+        Map<ClasspathInfo, Collection<FileObject>> sortedFilesForFixes = sortFiles(fastFixes.keySet());
+        List<ModificationResult> results = new LinkedList<ModificationResult>();
+
+        for (Entry<ClasspathInfo, Collection<FileObject>> e : sortedFilesForFixes.entrySet()) {
+            if (cancel.get()) return null;
+            
+            Map<FileObject, List<JavaFix>> filtered = new HashMap<FileObject, List<JavaFix>>();
+
+            for (FileObject f : e.getValue()) {
+                filtered.put(f, fastFixes.get(f));
+            }
+
+            ModificationResult r = performFastFixes(e.getKey(), filtered, handle, cancel);
+            
+            if (r != null) {
+                results.add(r);
+            }
+        }
+
+        return results;
+    }
+
+    private static ModificationResult performFastFixes(ClasspathInfo cpInfo, final Map<FileObject, List<JavaFix>> toProcess, final ProgressHandleWrapper handle, final AtomicBoolean cancel) {
+        JavaSource js = JavaSource.create(cpInfo, toProcess.keySet());
+
+        try {
+            return js.runModificationTask(new Task<WorkingCopy>() {
+                public void run(WorkingCopy wc) throws Exception {
+                    if (cancel.get()) return ;
+                    
+                    if (wc.toPhase(Phase.RESOLVED).compareTo(Phase.RESOLVED) < 0)
+                        return ;
+
+                    for (JavaFix f : toProcess.get(wc.getFileObject())) {
+                        if (cancel.get()) return ;
+                        
+                        JavaFixImpl.Accessor.INSTANCE.process(f, wc, new JavaFix.UpgradeUICallback() {
+                            public boolean shouldUpgrade(String comment) {
+                                return true;
+                            }
+                        });
+                    }
+
+                    handle.tick();
+                }
+            });
+        } catch (IOException ex) {
+            Exceptions.printStackTrace(ex);
+            return null;
+        }
+    }
+
+    private static Map<String, Preferences> prepareOverlay(Set<String> enabledHints) {
+        Map<String, Preferences> preferencesOverlay = new HashMap<String, Preferences>();
+        for (List<TreeRule> rules : RulesManager.getInstance().getHints().values()) {
+            for (TreeRule r : rules) {
+                String id = r.getId();
+
+                if (r instanceof AbstractHint && !preferencesOverlay.containsKey(id)) {
+                    OverridePreferences prefs = new OverridePreferences(((AbstractHint) r).getPreferences(null));
+
+                    preferencesOverlay.put(r.getId(), prefs);
+                    HintsSettings.setEnabled(prefs, enabledHints.contains(id));
+                    HintsSettings.setSeverity(prefs, HintSeverity.WARNING);
+                }
+            }
+        }
+
+        return preferencesOverlay;
+    }
+
+    private static class OverridePreferences extends AbstractPreferences {
+
+        private Preferences delegateTo;
+        private Map<String, String> data;
+        private Set<String> removed;
+
+        public OverridePreferences(Preferences delegateTo) {
+            super(null, "");
+            this.data = new HashMap<String, String>();
+            this.removed = new HashSet<String>();
+        }
+
+        protected void putSpi(String key, String value) {
+            data.put(key, value);
+            removed.remove(key);
+        }
+
+        protected String getSpi(String key) {
+            if (data.containsKey(key)) {
+                return data.get(key);
+            } else {
+                if (removed.contains(key)) {
+                    return null;
+                } else {
+                    return delegateTo.get(key, null);
+                }
+            }
+        }
+
+        protected void removeSpi(String key) {
+            removed.add(key);
+        }
+
+        protected void removeNodeSpi() throws BackingStoreException {
+            throw new UnsupportedOperationException("Not supported yet.");
+        }
+
+        protected String[] keysSpi() throws BackingStoreException {
+            Set<String> keys = new HashSet<String>(Arrays.asList(delegateTo.keys()));
+
+            keys.removeAll(removed);
+            keys.addAll(data.keySet());
+
+            return keys.toArray(new String[0]);
+        }
+
+        protected String[] childrenNamesSpi() throws BackingStoreException {
+            throw new UnsupportedOperationException("Not supported yet.");
+        }
+
+        protected AbstractPreferences childSpi(String name) {
+            throw new UnsupportedOperationException("Not supported yet.");
+        }
+
+        protected void syncSpi() throws BackingStoreException {
+            throw new UnsupportedOperationException("Not supported yet.");
+        }
+
+        protected void flushSpi() throws BackingStoreException {
+            throw new UnsupportedOperationException("Not supported yet.");
+        }
+    }
+
+    public static Collection<FileObject> toProcess(Lookup l) {
+        List<FileObject> result = new LinkedList<FileObject>();
+
+        result.addAll(l.lookupAll(FileObject.class));
+
+        for (SourceGroup sg : l.lookupAll(SourceGroup.class)) {
+            result.add(sg.getRootFolder());
+        }
+        
+        for (Project p : l.lookupAll(Project.class)) {
+            Sources s = ProjectUtils.getSources(p);
+
+            for (SourceGroup sg : s.getSourceGroups("java")) {
+                result.add(sg.getRootFolder());
+            }
+        }
+
+        return result;
+    }
+
+    private static Collection<FileObject> findAllSources(Collection<FileObject> from) {
+        List<FileObject> result = new LinkedList<FileObject>();
+        Queue<FileObject> q = new LinkedList<FileObject>();
+
+        q.addAll(from);
+
+        while (!q.isEmpty()) {
+            FileObject f = q.poll();
+
+            if (f.isData() && "text/x-java".equals(FileUtil.getMIMEType(f))) {
+                result.add(f);
+            }
+
+            if (f.isFolder()) {
+                q.addAll(Arrays.asList(f.getChildren()));
+            }
+        }
+
+        return result;
+    }
+
+    private static Map<ClasspathInfo, Collection<FileObject>> sortFiles(Collection<FileObject> from) {
+        Map<List<ClassPath>, Collection<FileObject>> m = new HashMap<List<ClassPath>, Collection<FileObject>>();
+
+        for (FileObject f : from) {
+            List<ClassPath> cps = new ArrayList<ClassPath>(3);
+
+            cps.add(ClassPath.getClassPath(f, ClassPath.BOOT));
+            cps.add(ClassPath.getClassPath(f, ClassPath.COMPILE));
+            cps.add(ClassPath.getClassPath(f, ClassPath.SOURCE));
+
+            Collection<FileObject> files = m.get(cps);
+
+            if (files == null) {
+                m.put(cps, files = new LinkedList<FileObject>());
+            }
+
+            files.add(f);
+        }
+
+        Map<ClasspathInfo, Collection<FileObject>> result = new HashMap<ClasspathInfo, Collection<FileObject>>();
+
+        for (Entry<List<ClassPath>, Collection<FileObject>> e : m.entrySet()) {
+            result.put(ClasspathInfo.create(e.getKey().get(0), e.getKey().get(1), e.getKey().get(2)), e.getValue());
+        }
+
+        return result;
+    }
+
+    private static final class ProgressHandleWrapper {
+
+        private static final int TOTAL = 1000;
+        
+        private final ProgressHandle handle;
+        private final int[]          parts;
+
+        private       int            currentPart = (-1);
+        private       int            currentPartTotalWork;
+        private       int            currentPartWorkDone;
+
+        private       int            currentOffset;
+
+        public ProgressHandleWrapper(int[] parts) {
+            this(null, parts);
+        }
+        
+        public ProgressHandleWrapper(ProgressHandle handle, int[] parts) {
+            this.handle = handle;
+
+            if (handle == null) {
+                this.parts = null;
+            } else {
+                int total = 0;
+
+                for (int i : parts) {
+                    total += i;
+                }
+
+                this.parts = new int[parts.length];
+
+                for (int cntr = 0; cntr < parts.length; cntr++) {
+                    this.parts[cntr] = (TOTAL * parts[cntr]) / total;
+                }
+            }
+        }
+
+        public void startNextPart(int totalWork) {
+            if (handle == null) return ;
+            
+            if (currentPart == (-1)) {
+                handle.start(TOTAL);
+            } else {
+                currentOffset += parts[currentPart];
+            }
+
+            currentPart++;
+
+            currentPartTotalWork = totalWork;
+            currentPartWorkDone  = 0;
+        }
+
+        public void tick() {
+            if (handle == null) return ;
+
+            currentPartWorkDone++;
+
+            handle.progress(currentOffset + (parts[currentPart] * currentPartWorkDone) / currentPartTotalWork);
+        }
+
+        public void setMessage(String message) {
+            if (handle == null) return ;
+
+            handle.progress(message);
+        }
+    }
+}

api/src/org/netbeans/modules/jackpot30/impl/batch/BatchApplyAction.java

+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2008-2009 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 2008-2009 Sun Microsystems, Inc.
+ */
+package org.netbeans.modules.jackpot30.impl.batch;
+
+import java.awt.Image;
+import java.awt.Toolkit;
+import java.awt.event.ActionEvent;
+import java.net.URL;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import javax.swing.AbstractAction;
+import javax.swing.Action;
+import javax.swing.Icon;
+import javax.swing.KeyStroke;
+import javax.swing.text.Keymap;
+import org.openide.DialogDescriptor;
+import org.openide.DialogDisplayer;
+import org.openide.NotifyDescriptor;
+import org.openide.awt.Mnemonics;
+import org.openide.util.ContextAwareAction;
+import org.openide.util.ImageUtilities;
+import org.openide.util.Lookup;
+import org.openide.util.LookupEvent;
+import org.openide.util.LookupListener;
+import org.openide.util.NbBundle;
+import org.openide.util.Utilities;
+
+/**
+ *
+ * @author Jan Lahoda
+ */
+public final class BatchApplyAction extends AbstractAction implements ContextAwareAction, LookupListener {
+
+    private static final String HINT = "hint";
+    
+    private final Lookup context;
+    private final Lookup.Result<Object> r;
+    private final Map attributes;
+    
+    public BatchApplyAction() {
+        this(prepareDefaultMap());
+    }
+
+    private BatchApplyAction(Map attributes) {
+        this(Utilities.actionsGlobalContext(), attributes);
+    }
+
+    private BatchApplyAction(Lookup context, Map attributes) {
+        this.context = context;
+
+        this.r = context.lookupResult(Object.class); //XXX
+        this.r.addLookupListener(this);
+        this.r.allInstances();
+        updateEnabled();
+
+        this.attributes = attributes;
+    }
+
+    public void actionPerformed(ActionEvent e) {
+        String hintToExecute = (String) getValue(HINT);
+
+        if (hintToExecute == null) {
+            NotifyDescriptor.InputLine nd = new NotifyDescriptor.InputLine("Select Hint (regexp)", "Select Hint");
+
+            if (DialogDisplayer.getDefault().notify(nd) != DialogDescriptor.OK_OPTION) {
+                return ;
+            }
+            
+            hintToExecute = nd.getInputText();
+        }
+
+        String error = BatchApply.applyFixes(context, hintToExecute, true);
+
+        if (error != null) {
+            DialogDisplayer.getDefault().notifyLater(new NotifyDescriptor.Message(error, NotifyDescriptor.ERROR_MESSAGE));
+        }
+    }
+
+    private void updateEnabled() {
+        setEnabled(!BatchApply.toProcess(context).isEmpty());
+    }
+
+    public void resultChanged(LookupEvent ev) {
+        updateEnabled();
+    }
+
+    public Action createContextAwareInstance(Lookup actionContext) {
+        return new BatchApplyAction(context, attributes);
+    }
+
+    private static Map prepareDefaultMap() {
+        Map<String, Object> m = new HashMap<String, Object>();
+
+        m.put("displayName", NbBundle.getMessage(BatchApplyAction.class, "CTL_BatchApplyAction"));
+        m.put("noIconInMenu", Boolean.TRUE);
+
+        return m;
+    }
+    
+    @Override
+    public Object getValue(String name) {
+        Object o = super.getValue(name);
+
+        if (o != null) {
+            return o;
+        }
+
+        return extractCommonAttribute(attributes, this, name);
+    }
+
+    static final Object extractCommonAttribute(Map fo, Action action, String name) {
+        if (Action.NAME.equals(name)) {
+            String actionName = (String) fo.get("displayName"); // NOI18N
+            // NOI18N
+            //return Actions.cutAmpersand(actionName);
+            return actionName;
+        }
+        if (Action.MNEMONIC_KEY.equals(name)) {
+            String actionName = (String) fo.get("displayName"); // NOI18N
+            // NOI18N
+            int position = Mnemonics.findMnemonicAmpersand(actionName);
+
+            return position == -1 ? null : Character.valueOf(actionName.charAt(position + 1));
+        }
+        if (Action.SMALL_ICON.equals(name)) {
+            Object icon = fo == null ? null : fo.get("iconBase"); // NOI18N
+            if (icon instanceof Icon) {
+                return (Icon) icon;
+            }
+            if (icon instanceof Image) {
+                return ImageUtilities.image2Icon((Image)icon);
+            }
+            if (icon instanceof String) {
+                return ImageUtilities.loadImage((String)icon);
+            }
+            if (icon instanceof URL) {
+                return Toolkit.getDefaultToolkit().getImage((URL) icon);
+            }
+        }
+        if ("iconBase".equals(name)) { // NOI18N
+            return fo == null ? null : fo.get("iconBase"); // NOI18N
+        }
+        if ("noIconInMenu".equals(name)) { // NOI18N
+            return fo == null ? null : fo.get("noIconInMenu"); // NOI18N
+        }
+        if (Action.ACCELERATOR_KEY.equals(name)) {
+            Keymap map = Lookup.getDefault().lookup(Keymap.class);
+            if (map != null) {
+                KeyStroke[] arr = map.getKeyStrokesForAction(action);
+                return arr.length > 0 ? arr[0] : null;
+            }
+        }
+        if (HINT.equals(name)) {
+            return fo.get("hint"); //NOI18N
+        }
+
+        return null;
+    }
+
+    public static Action createBatchHintAction(Map attributes) {
+        return new BatchApplyAction(attributes);
+    }
+
+}

api/src/org/netbeans/modules/jackpot30/impl/batch/Bundle.properties

+# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+#
+# Copyright 2008-2009 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]"
+#
+# Contributor(s):
+#
+#The Original Software is NetBeans. The Initial Developer of the Original
+# Software is Sun Microsystems, Inc. Portions Copyright 2008-2009 Sun
+#Microsystems, Inc. All Rights Reserved.
+#
+# 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.
+
+CTL_BatchApplyAction=Batch Apply Hint
+SD_ApplyTransformations=applies specified transformation(s) on specified project or all opened projects
+SD_ApplyTransformationsProject=projects on which the transformations should be applied
+SelectHint.jLabel1.text=Select Hint:

api/src/org/netbeans/modules/jackpot30/impl/batch/JavaFixImpl.java

+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2008-2009 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 2008-2009 Sun Microsystems, Inc.
+ */
+
+package org.netbeans.modules.jackpot30.impl.batch;
+
+import org.netbeans.api.java.source.JavaSource;
+import org.netbeans.api.java.source.JavaSource.Phase;
+import org.netbeans.api.java.source.Task;
+import org.netbeans.api.java.source.WorkingCopy;
+import org.netbeans.modules.jackpot30.spi.JavaFix;
+import org.netbeans.modules.jackpot30.spi.JavaFix.UpgradeUICallback;
+import org.netbeans.spi.editor.hints.ChangeInfo;
+import org.netbeans.spi.editor.hints.Fix;
+import org.openide.DialogDisplayer;
+import org.openide.NotifyDescriptor;
+import org.openide.filesystems.FileObject;
+
+/**TODO: move to better package
+ *
+ * @author Jan Lahoda
+ */
+public final class JavaFixImpl implements Fix {
+
+    final JavaFix jf;
+
+    public JavaFixImpl(JavaFix jf) {
+        this.jf = jf;
+    }
+
+    public String getText() {
+        return Accessor.INSTANCE.getText(jf);
+    }
+
+    public ChangeInfo implement() throws Exception {
+        JavaSource js = JavaSource.forFileObject(Accessor.INSTANCE.getFile(jf));
+
+        js.runModificationTask(new Task<WorkingCopy>() {
+            public void run(WorkingCopy wc) throws Exception {
+                if (wc.toPhase(Phase.RESOLVED).compareTo(Phase.RESOLVED) < 0) {
+                    return;
+                }
+
+                Accessor.INSTANCE.process(jf, wc, new JavaFix.UpgradeUICallback() {
+                    public boolean shouldUpgrade(String comment) {
+                        NotifyDescriptor nd = new NotifyDescriptor.Confirmation(comment, "Update spec version.", NotifyDescriptor.YES_NO_OPTION);
+
+                        return DialogDisplayer.getDefault().notify(nd) == NotifyDescriptor.YES_OPTION;
+                    }
+                });
+            }
+        }).commit();
+
+        return null;
+    }
+
+    public static abstract class Accessor {
+
+        public static Accessor INSTANCE;
+
+        public abstract String getText(JavaFix jf);
+        public abstract ChangeInfo process(JavaFix jf, WorkingCopy wc, UpgradeUICallback callback) throws Exception;
+        public abstract FileObject getFile(JavaFix jf);
+        
+    }
+}

api/src/org/netbeans/modules/jackpot30/impl/batch/OptionProcessorImpl.java

+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2008-2009 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 2008-2009 Sun Microsystems, Inc.
+ */
+
+package org.netbeans.modules.jackpot30.impl.batch;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import org.netbeans.api.java.classpath.ClassPath;
+import org.netbeans.api.java.classpath.GlobalPathRegistry;
+import org.netbeans.api.java.source.SourceUtils;
+import org.netbeans.api.project.Project;
+import org.netbeans.api.project.ProjectManager;
+import org.netbeans.api.project.ProjectUtils;
+import org.netbeans.api.project.SourceGroup;
+import org.netbeans.api.project.ui.OpenProjects;
+import org.netbeans.api.sendopts.CommandException;
+import org.netbeans.spi.java.classpath.support.ClassPathSupport;
+import org.netbeans.spi.sendopts.Env;
+import org.netbeans.spi.sendopts.Option;
+import org.netbeans.spi.sendopts.OptionProcessor;
+import org.openide.filesystems.FileObject;
+import org.openide.filesystems.FileUtil;
+import org.openide.util.Exceptions;
+import org.openide.util.Lookup;
+import org.openide.util.lookup.Lookups;
+import org.openide.util.lookup.ServiceProvider;
+
+/**
+ *
+ * @author Jan Lahoda
+ */
+@ServiceProvider(service=OptionProcessor.class)
+public class OptionProcessorImpl extends OptionProcessor {
+
+    private static final Logger LOG = Logger.getLogger(OptionProcessorImpl.class.getName());
+    
+    private static final String APPLY_TRANSFORMATIONS_PROJECT_OPTION = "apply-transformations-project";
+
+//    private static final Option LIST = Option.withoutArgument(Option.NO_SHORT_NAME, "list-hints-transformation");
+    private static final Option APPLY_TRANSFORMATIONS = Option.shortDescription(
+                                                            Option.requiredArgument(Option.NO_SHORT_NAME, "apply-transformations"),
+                                                            "org.netbeans.modules.jackpot30.hints.batch.Bundle",
+                                                            "SD_ApplyTransformations");
+    private static final Option APPLY_TRANSFORMATIONS_PROJECT = Option.shortDescription(
+                                                            Option.additionalArguments(Option.NO_SHORT_NAME, APPLY_TRANSFORMATIONS_PROJECT_OPTION),
+                                                            "org.netbeans.modules.jackpot30.hints.batch.Bundle",
+                                                            "SD_ApplyTransformationsProject");
+
+    private static final Set<Option> OPTIONS = new HashSet<Option>(Arrays.asList(/*LIST, */APPLY_TRANSFORMATIONS, APPLY_TRANSFORMATIONS_PROJECT));
+    
+    @Override
+    protected Set<Option> getOptions() {
+        return OPTIONS;
+    }
+
+    @Override
+    protected void process(Env env, Map<Option, String[]> optionValues) throws CommandException {
+//        if (optionValues.containsKey(LIST)) {
+//            env.getOutputStream().println("Supported Hints:");
+//            for (TreeRule r : BatchApply.listHints()) {
+//                env.getOutputStream().println(r.getDisplayName() + " - " + r.getId());
+//            }
+//        }
+
+        List<Project> projects = new LinkedList<Project>();
+        Map<String, List<ClassPath>> classPaths = new HashMap<String, List<ClassPath>>();
+        
+        if (optionValues.containsKey(APPLY_TRANSFORMATIONS_PROJECT)) {
+            String[] projectNames = optionValues.get(APPLY_TRANSFORMATIONS_PROJECT);
+
+            if (projectNames.length == 0) {
+                env.getErrorStream().println("At least one parameter needed for " + APPLY_TRANSFORMATIONS_PROJECT_OPTION);
+                throw new CommandException(1);
+            }