Commits

Jan Lahoda committed ff3a52e

Adding hackish support for transformations.

  • Participants
  • Parent commits 3c7b893

Comments (0)

Files changed (18)

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

 
 package org.netbeans.modules.jackpot30.impl;
 
+import com.sun.source.tree.Tree;
 import com.sun.source.tree.Tree.Kind;
+import com.sun.source.util.TreePath;
+import java.util.Collection;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicBoolean;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.PackageElement;
+import org.netbeans.api.java.source.CompilationInfo;
+import org.netbeans.api.java.source.support.CancellableTreePathScanner;
+import org.netbeans.modules.jackpot30.spi.ElementBasedHintProvider;
 import org.netbeans.modules.jackpot30.spi.HintDescription;
 import org.netbeans.modules.jackpot30.spi.HintDescription.PatternDescription;
 import org.netbeans.modules.jackpot30.spi.HintProvider;
 
     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);
-                }
-            }
+            sortOut(p.computeHints(), kind2Hints, pattern2Hint);
         }
     }
 
         return pattern2Hint;
     }
 
+    public static void computeElementBasedHintsXXX(final CompilationInfo info, AtomicBoolean cancel, final Map<Kind, List<HintDescription>> kind2Hints, final Map<PatternDescription, List<HintDescription>> pattern2Hint) {
+        computeElementBasedHintsXXX(info, cancel, Lookup.getDefault().lookupAll(ElementBasedHintProvider.class), kind2Hints, pattern2Hint);
+    }
+
+    public static void computeElementBasedHintsXXX(final CompilationInfo info, AtomicBoolean cancel, final Collection<? extends ElementBasedHintProvider> providers, final Map<Kind, List<HintDescription>> kind2Hints, final Map<PatternDescription, List<HintDescription>> pattern2Hint) {
+        new CancellableTreePathScanner(cancel) {
+            private Set<Element> handledElements = new HashSet<Element>();
+            private void handleElementImpl(Element el) {
+                if (!handledElements.add(el)) return ;
+                
+                for (ElementBasedHintProvider provider : providers) {
+                    sortOut(provider.computeHints(info, el), kind2Hints, pattern2Hint);
+                }
+            }
+            
+            private void handleElement(Element el) {
+                PackageElement p = info.getElements().getPackageOf(el);
+
+                while (p != el) {
+                    handleElementImpl(el);
+                    el = el.getEnclosingElement();
+                }
+                
+                handleElementImpl(p);
+            }
+
+            @Override
+            public Object scan(Tree tree, Object p) {
+                if (tree == null) return null;
+
+                TreePath tp = new TreePath(getCurrentPath(), tree);
+                Element el = info.getTrees().getElement(tp);
+
+                if (el != null) {
+                    handleElement(el);
+                }
+                
+                return super.scan(tree, p);
+            }
+        }.scan(info.getCompilationUnit(), null);
+    }
+
+    private static void sortOut(Collection<? extends HintDescription> hints, Map<Kind, List<HintDescription>> kind2Hints, Map<PatternDescription, List<HintDescription>> pattern2Hint) {
+        for (HintDescription d : hints) {
+            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);
+            }
+        }
+    }
+
 }

File api/src/org/netbeans/modules/jackpot30/impl/hints/HintsInvoker.java

     }
 
     private List<ErrorDescription> computeHints(CompilationInfo info, TreePath startAt) {
-        Map<Kind, List<HintDescription>> kindHints = RulesManager.getInstance().getKindBasedHints();
-        Map<PatternDescription, List<HintDescription>> patternHints = RulesManager.getInstance().getPatternBasedHints();
+        Map<Kind, List<HintDescription>> kindHints = new HashMap<Kind, List<HintDescription>>(RulesManager.getInstance().getKindBasedHints());
+        Map<PatternDescription, List<HintDescription>> patternHints = new HashMap<PatternDescription, List<HintDescription>>(RulesManager.getInstance().getPatternBasedHints());
+
+        RulesManager.computeElementBasedHintsXXX(info, cancel, kindHints, patternHints);
+        
         return computeHints(info, startAt, kindHints, patternHints);
     }
 
+    //XXX: used by tests (in other modules too):
+    public List<ErrorDescription> computeHints(CompilationInfo info,
+                                        Map<Kind, List<HintDescription>> hints,
+                                        Map<PatternDescription, List<HintDescription>> patternHints) {
+        return computeHints(info, new TreePath(info.getCompilationUnit()), hints, patternHints);
+    }
+
     List<ErrorDescription> computeHints(CompilationInfo info,
                                         TreePath startAt,
                                         Map<Kind, List<HintDescription>> hints,

File api/src/org/netbeans/modules/jackpot30/spi/ElementBasedHintProvider.java

+package org.netbeans.modules.jackpot30.spi;
+
+import java.util.Collection;
+import javax.lang.model.element.Element;
+import org.netbeans.api.java.source.CompilationInfo;
+
+/**
+ * XXX: this is an ugly hack!
+ * @author lahvac
+ */
+public interface ElementBasedHintProvider {
+
+    public Collection<? extends HintDescription> computeHints(CompilationInfo info, Element el);
+
+}

File nbproject/project.properties

     ${project.org.netbeans.modules.jackpot30.hintsimpl}:\
     ${project.org.netbeans.modules.jackpot30.api}:\
     ${project.org.netbeans.modules.jackpot30.code}:\
-    ${project.org.netbeans.modules.jackpot30.file}
+    ${project.org.netbeans.modules.jackpot30.file}:\
+    ${project.org.netbeans.modules.jackpot30.transformers}
 project.org.netbeans.modules.jackpot30.api=api
 project.org.netbeans.modules.jackpot30.code=code
 project.org.netbeans.modules.jackpot30.file=file
 project.org.netbeans.modules.jackpot30.test.borrowed=borrowedtests
 
 project.license=cddl-netbeans-sun
+project.org.netbeans.modules.jackpot30.transformers=transformer

File transformer/build.xml

+<?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.transformers" default="netbeans" basedir=".">
+    <description>Builds, tests, and runs the project org.netbeans.modules.jackpot30.transformers.</description>
+    <import file="nbproject/build-impl.xml"/>
+    
+    <target name="-download">
+        <mkdir dir="release/libs" />
+        <get src="https://lang.dev.java.net/files/documents/9560/133823/annotations.jar" dest="release/libs/annotations.jar" usetimestamp="true"/>
+    </target>
+    
+    <target name="init" depends="-download,harness.init" />
+</project>

File transformer/manifest.mf

+Manifest-Version: 1.0
+OpenIDE-Module: org.netbeans.modules.jackpot30.transformers
+OpenIDE-Module-Layer: org/netbeans/modules/jackpot30/transformers/resources/layer.xml
+OpenIDE-Module-Localizing-Bundle: org/netbeans/modules/jackpot30/transformers/resources/Bundle.properties
+OpenIDE-Module-Specification-Version: 1.0
+

File transformer/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.transformers-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>

File transformer/nbproject/genfiles.properties

+build.xml.data.CRC32=ec1a83f8
+build.xml.script.CRC32=14b2fe09
+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=ec1a83f8
+nbproject/build-impl.xml.script.CRC32=07a96417
+nbproject/build-impl.xml.stylesheet.CRC32=261c4bef@1.28.0.7

File transformer/nbproject/project.properties

+javac.source=1.5
+javac.compilerargs=-Xlint -Xlint:-serial

File transformer/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.transformers</code-name-base>
+            <suite-component/>
+            <module-dependencies>
+                <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.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.modules.jackpot30.api</code-name-base>
+                    <build-prerequisite/>
+                    <compile-dependency/>
+                    <run-dependency>
+                        <specification-version>1.0</specification-version>
+                    </run-dependency>
+                </dependency>
+                <dependency>
+                    <code-name-base>org.netbeans.modules.java.source</code-name-base>
+                    <build-prerequisite/>
+                    <compile-dependency/>
+                    <run-dependency>
+                        <specification-version>0.43.0.7.7.1</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.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.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.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/>
+        </data>
+    </configuration>
+</project>

File transformer/nbproject/suite.properties

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

File transformer/release/libs/annotations.jar

Binary file added.

File transformer/src/org/netbeans/modules/jackpot30/transformers/Annotations.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.transformers;
+
+/**
+ *
+ * @author Jan Lahoda
+ */
+public enum Annotations {
+
+    TRANSFORMATION_SET("TransformationSet"),
+    TRANSFORMATION,
+    FIX,
+    PATTERN,
+    CONSTRAINT;
+
+    private static final String PACKAGE_NAME = "net.java.lang.annotations.";
+    
+    private final String specialName;
+
+    private Annotations() {
+        this(null);
+    }
+
+    private Annotations(String specialName) {
+        this.specialName = specialName;
+    }
+    
+    public String toFQN() {
+        return PACKAGE_NAME + (specialName != null ? specialName : name().charAt(0) + name().substring(1).toLowerCase());
+    }
+
+}

File transformer/src/org/netbeans/modules/jackpot30/transformers/TransformationHintProviderImpl.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 bysanno
+ * 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.transformers;
+
+import com.sun.source.tree.Tree;
+import com.sun.source.util.SourcePositions;
+import com.sun.source.util.TreePath;
+import java.io.File;
+import java.io.IOException;
+import java.net.URL;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.EnumSet;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.regex.Matcher;
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.AnnotationValue;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ElementKind;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.Modifier;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.type.DeclaredType;
+import javax.lang.model.type.TypeMirror;
+import javax.lang.model.util.ElementFilter;
+import org.netbeans.api.java.classpath.ClassPath;
+import org.netbeans.api.java.source.ClasspathInfo;
+import org.netbeans.api.java.source.ClasspathInfo.PathKind;
+import org.netbeans.api.java.source.CompilationController;
+import org.netbeans.api.java.source.CompilationInfo;
+import org.netbeans.api.java.source.ElementHandle;
+import org.netbeans.api.java.source.JavaSource;
+import org.netbeans.api.java.source.Task;
+import org.netbeans.api.java.source.TypeMirrorHandle;
+import org.netbeans.modules.jackpot30.spi.ElementBasedHintProvider;
+import org.netbeans.modules.jackpot30.spi.HintContext;
+import org.netbeans.modules.jackpot30.spi.HintDescription;
+import org.netbeans.modules.jackpot30.spi.HintDescription.Worker;
+import org.netbeans.modules.jackpot30.spi.JavaFix;
+import org.netbeans.modules.jackpot30.spi.support.ErrorDescriptionFactory;
+import org.netbeans.spi.editor.hints.ErrorDescription;
+import org.netbeans.spi.editor.hints.Fix;
+import org.netbeans.spi.java.classpath.support.ClassPathSupport;
+import org.openide.filesystems.FileUtil;
+import org.openide.modules.InstalledFileLocator;
+import org.openide.util.Exceptions;
+
+import org.openide.util.lookup.ServiceProvider;
+import static org.netbeans.modules.jackpot30.transformers.Annotations.*;
+
+/**
+ *
+ * @author Jan Lahoda
+ */
+@ServiceProvider(service=ElementBasedHintProvider.class)
+public class TransformationHintProviderImpl implements ElementBasedHintProvider {
+
+    public Collection<? extends HintDescription> computeHints(final CompilationInfo info, final Element el) {
+        final List<HintDescription> hints = new LinkedList<HintDescription>();
+
+        ClasspathInfo currentCP = info.getClasspathInfo();
+        ClassPath overlayCompileCP = prepareOverlayCompileCP();
+        ClassPath extendedCompileCP = ClassPathSupport.createProxyClassPath(overlayCompileCP, currentCP.getClassPath(PathKind.COMPILE));
+        ClassPath overlayBootCP = prepareOverlayBootCP();
+        ClassPath extendedBootCP = ClassPathSupport.createProxyClassPath(overlayBootCP, currentCP.getClassPath(PathKind.BOOT));
+        ClasspathInfo extendedCPInfo = ClasspathInfo.create(extendedBootCP, extendedCompileCP, currentCP.getClassPath(PathKind.SOURCE));
+
+        try {
+        JavaSource.create(extendedCPInfo).runUserActionTask(new Task<CompilationController>() {
+            public void run(final CompilationController overlayInfo) throws Exception {
+                List<HintDescription> w = doComputeHints(info, overlayInfo, el);
+
+                if (w != null) {
+                    hints.addAll(w);
+                }
+            }
+        }, true);
+        } catch (IOException ex) {
+            Exceptions.printStackTrace(ex);
+        }
+
+        return hints;
+    }
+
+    private static final Set<ElementKind> KINDS_FOR_ELEMENT_HANDLE = EnumSet.of(
+            ElementKind.PACKAGE, ElementKind.CLASS, ElementKind.INTERFACE, ElementKind.ENUM,
+            ElementKind.ANNOTATION_TYPE, ElementKind.METHOD, ElementKind.CONSTRUCTOR,
+            ElementKind.INSTANCE_INIT, ElementKind.STATIC_INIT, ElementKind.FIELD,
+            ElementKind.ENUM_CONSTANT);
+    
+    private  List<HintDescription> doComputeHints(CompilationInfo info,
+                                                  CompilationInfo overlayInfo,
+                                                  Element el) {
+        List<HintDescription> patterns = processAnnotations(el, el.getEnclosingElement(), info, info);
+
+        if (patterns.isEmpty() && KINDS_FOR_ELEMENT_HANDLE.contains(el.getKind())) {
+            Element overlay = ElementHandle.create(el).resolve(overlayInfo);
+
+            if (overlay != null) {
+                patterns = processAnnotations(overlay, overlay.getEnclosingElement(), info, overlayInfo);
+            }
+        }
+
+        return patterns;
+    }
+
+    private static final class WorkerImpl implements Worker {
+        
+        private final String displayName;
+        private final List<FixDescription> fixDescriptions;
+
+        public WorkerImpl(String displayName, List<FixDescription> fixDescriptions) {
+            this.displayName = displayName;
+            this.fixDescriptions = fixDescriptions;
+        }
+
+        public Collection<? extends ErrorDescription> createErrors(HintContext ctx) {
+            //XXX: antipatterns not supported by the current infrastructure
+//            TreePath tp = treePath;
+//
+//            while (tp != null) {
+//                if (!pd.getPattern().checkAntipatterns(tp)) {
+//                    return null;
+//                }
+//
+//                tp = tp.getParentPath();
+//            }
+
+            List<Fix> fixes = new LinkedList<Fix>();
+
+            for (FixDescription d : fixDescriptions) {
+                String dn = d.getDisplayName();
+
+                if (dn.length() == 0) {
+                    dn = defaultFixDisplayName(ctx.getInfo(), ctx.getVariables(), d);
+                }
+
+                fixes.add(JavaFix.rewriteFix(ctx.getInfo(), dn, ctx.getPath(), d.getPattern(), ctx.getVariables(), Collections.<String, TypeMirror>emptyMap()/*XXX: pd.pattern.getConstraints()*/));
+            }
+
+            return Collections.singletonList(ErrorDescriptionFactory.forName(ctx, ctx.getPath(), displayName, fixes.toArray(new Fix[0])));
+        }
+    }
+
+    private static <T> T findValue(AnnotationMirror m, String name, Class<T> clazz) {
+        for (Entry<? extends ExecutableElement, ? extends AnnotationValue> e : m.getElementValues().entrySet()) {
+            if (name.equals(e.getKey().getSimpleName().toString())) {
+                return clazz.cast(e.getValue().getValue());
+            }
+        }
+
+        TypeElement te = (TypeElement) m.getAnnotationType().asElement();
+
+        for (ExecutableElement ee : ElementFilter.methodsIn(te.getEnclosedElements())) {
+            if (name.equals(ee.getSimpleName().toString())) {
+                if (ee.getDefaultValue() == null) {
+                    return null;
+                }
+                return clazz.cast(ee.getDefaultValue().getValue());
+            }
+        }
+
+        return null;
+    }
+
+    private static HintDescription create(CompilationInfo info, Element el, AnnotationMirror transformation, CompilationInfo fake) {
+        Collection rawPatterns = findValue(transformation, "pattern", Collection.class);
+
+        if (rawPatterns == null || rawPatterns.isEmpty()) {
+            return null;
+        }
+
+        Iterable<AnnotationMirror> patterns = Utilities.checkedIterableByFilter(rawPatterns, AnnotationMirror.class, true);
+
+        String patternString = findValue(patterns.iterator().next(), "value", String.class);
+
+        if (patternString == null) {
+            return null;
+        }
+
+        Collection rawConstraints = findValue(transformation, "constraint", Collection.class);
+
+        if (rawConstraints == null) {
+            return null;
+        }
+
+        Iterable<AnnotationMirror> constraints = Utilities.checkedIterableByFilter(rawConstraints, AnnotationMirror.class, true);
+        Map<String, String> variableTypes = new HashMap<String, String>();
+
+        for (AnnotationMirror m : constraints) {
+            String var = findValue(m, "variable", String.class);
+            TypeMirror tm = findValue(m, "type", TypeMirror.class);
+
+            if (var == null || tm == null) {
+                return null;
+            }
+
+            variableTypes.put(var, tm.toString()); //XXX: toString()
+        }
+
+        if (!el.getModifiers().contains(Modifier.STATIC) && !variableTypes.containsKey("$this")) {
+            TypeMirror parent = el.getEnclosingElement().asType();
+
+            variableTypes.put("$this", TypeMirrorHandle.create(fake.getTypes().erasure(parent)).resolve(info).toString()); //XXX: toString()
+        }
+
+        List<String> aps = new LinkedList<String>();
+
+        for (AnnotationMirror ap : Utilities.checkedIterableByFilter(findValue(transformation, "antipattern", Collection.class), AnnotationMirror.class, true)) {
+            aps.add(findValue(ap, "value", String.class));
+        }
+
+        List<FixDescription> fixes = new LinkedList<FixDescription>();
+        Collection rawFixes = findValue(transformation, "fix", Collection.class);
+
+        if (rawFixes == null) {
+            return null;
+        }
+
+        for (AnnotationMirror f : Utilities.checkedIterableByFilter(rawFixes, AnnotationMirror.class, true)) {
+            String dn = findValue(f, "displayName", String.class);
+            String to = findValue(f, "value", String.class);
+
+            if (dn == null || to == null) {
+                return null;
+            }
+
+            fixes.add(new FixDescription(dn, to));
+        }
+
+        String displayName = findValue(transformation, "displayName", String.class);
+
+        if (displayName == null) {
+            return null;//XXX
+        }
+
+        return HintDescription.create(HintDescription.PatternDescription.create(patternString, variableTypes), new WorkerImpl(displayName, fixes));
+    }
+
+
+    private static final class FixDescription {
+        private final String displayName;
+        private final String pattern;
+
+        public FixDescription(String displayName, String pattern) {
+            this.displayName = displayName;
+            this.pattern = pattern;
+        }
+
+        public String getDisplayName() {
+            return displayName;
+        }
+
+        public String getPattern() {
+            return pattern;
+        }
+
+    }
+
+    private ClassPath prepareOverlayCompileCP() throws IllegalArgumentException {
+        File nbOverlay = InstalledFileLocator.getDefault().locate("overlay/org-netbeans-nboverlay.jar", null, false);
+
+        if (nbOverlay ==null) {
+            return ClassPathSupport.createClassPath(new URL[0]);
+        }
+        
+        return ClassPathSupport.createClassPath(FileUtil.urlForArchiveOrDir(nbOverlay));
+    }
+
+    private ClassPath prepareOverlayBootCP() throws IllegalArgumentException {
+        File annotations = InstalledFileLocator.getDefault().locate("libs/annotations.jar", null, false);
+        File jdkOverlay = InstalledFileLocator.getDefault().locate("overlay/jdk.jar", null, false);
+
+        if (annotations == null) {
+            return ClassPathSupport.createClassPath(new URL[0]);
+        }
+
+        if (jdkOverlay == null) {
+            return ClassPathSupport.createClassPath(FileUtil.urlForArchiveOrDir(annotations));
+        }
+
+        return ClassPathSupport.createClassPath(FileUtil.urlForArchiveOrDir(annotations), FileUtil.urlForArchiveOrDir(jdkOverlay));
+    }
+
+    private List<HintDescription> processAnnotations(Element e, Element enclosing, CompilationInfo info, CompilationInfo fake) {
+        boolean foundTransformations = false;
+        List<HintDescription> result = new LinkedList<HintDescription>();
+        
+        for (AnnotationMirror am : e.getAnnotationMirrors()) {
+            String fqn = ((TypeElement) am.getAnnotationType().asElement()).getQualifiedName().toString();
+            List<AnnotationMirror> transformations = new LinkedList<AnnotationMirror>();
+            if (TRANSFORMATION.toFQN().equals(fqn)) {
+                transformations.add(am);
+                foundTransformations = true;
+            }
+            if (TRANSFORMATION_SET.toFQN().equals(fqn)) {
+                for (AnnotationMirror m : Utilities.checkedIterableByFilter(findValue(am, "value", Collection.class), AnnotationMirror.class, true)) {
+                    transformations.add(m);
+                }
+                foundTransformations = true;
+            }
+            
+            for (AnnotationMirror t : transformations) {
+                HintDescription pd = create(info, e, t, fake);
+
+                if (pd != null) {
+                    result.add(pd);
+                }
+            }
+        }
+
+        if (!foundTransformations && e.getKind() == ElementKind.METHOD) {
+            TypeElement owner = (TypeElement) e.getEnclosingElement();
+
+            for (TypeMirror tm : fake.getTypes().directSupertypes(owner.asType())) {
+                DeclaredType dt     = (DeclaredType) tm;
+                TypeElement  parent = (TypeElement)  dt.asElement();
+
+                for (ExecutableElement ee : ElementFilter.methodsIn(parent.getEnclosedElements())) {
+                    if (!fake.getElements().overrides((ExecutableElement) e, ee, (TypeElement) enclosing)) {
+                        continue;
+                    }
+                    List<HintDescription> r = processAnnotations(ee, enclosing, info, fake);
+
+                    result.addAll(r);
+                }
+
+            }
+        }
+
+        return result;
+    }
+
+    private static String defaultFixDisplayName(CompilationInfo info, Map<String, TreePath> variables, FixDescription d) {
+        Map<String, String> stringsForVariables = new HashMap<String, String>();
+
+        for (Entry<String, TreePath> e : variables.entrySet()) {
+            Tree t = e.getValue().getLeaf();
+            SourcePositions sp = info.getTrees().getSourcePositions();
+            int startPos = (int) sp.getStartPosition(info.getCompilationUnit(), t);
+            int endPos = (int) sp.getEndPosition(info.getCompilationUnit(), t);
+            
+            stringsForVariables.put(e.getKey(), info.getText().substring(startPos, endPos));
+        }
+
+        if (!stringsForVariables.containsKey("$this")) {
+            stringsForVariables.put("$this", "this");
+        }
+
+        String replaceTarget = d.getPattern();
+
+        for (Entry<String, String> e : stringsForVariables.entrySet()) {
+            String quotedVariable = java.util.regex.Pattern.quote(e.getKey());
+            String quotedTarget = Matcher.quoteReplacement(e.getValue());
+            replaceTarget = replaceTarget.replaceAll(quotedVariable, quotedTarget);
+        }
+
+        return "Rewrite to " + replaceTarget;
+    }
+
+}

File transformer/src/org/netbeans/modules/jackpot30/transformers/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.transformers;
+
+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;
+        }
+    }
+
+}

File transformer/src/org/netbeans/modules/jackpot30/transformers/resources/Bundle.properties

+OpenIDE-Module-Name=Jackpot 3.0 Transformations

File transformer/src/org/netbeans/modules/jackpot30/transformers/resources/layer.xml

+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE filesystem PUBLIC "-//NetBeans//DTD Filesystem 1.1//EN" "http://www.netbeans.org/dtds/filesystem-1_1.dtd">
+<filesystem>
+</filesystem>

File transformer/test/unit/src/org/netbeans/modules/jackpot30/transformers/TransformationHintProviderImplTest.java

+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2008 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 Sun Microsystems, Inc.
+ */
+
+package org.netbeans.modules.jackpot30.transformers;
+
+import com.sun.source.tree.Tree.Kind;
+import java.io.File;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicBoolean;
+import javax.swing.text.Document;
+import org.netbeans.api.java.lexer.JavaTokenId;
+import org.netbeans.api.java.source.CompilationInfo;
+import org.netbeans.api.java.source.JavaSource;
+import org.netbeans.api.java.source.JavaSource.Phase;
+import org.netbeans.api.java.source.SourceUtilsTestUtil;
+import org.netbeans.api.java.source.TestUtilities;
+import org.netbeans.api.lexer.Language;
+import org.netbeans.junit.NbTestCase;
+import org.netbeans.modules.jackpot30.impl.RulesManager;
+import org.netbeans.modules.jackpot30.impl.hints.HintsInvoker;
+import org.netbeans.modules.jackpot30.spi.HintDescription;
+import org.netbeans.modules.jackpot30.spi.HintDescription.PatternDescription;
+import org.netbeans.modules.java.source.TreeLoader;
+import org.netbeans.spi.editor.hints.ErrorDescription;
+import org.netbeans.spi.editor.hints.Fix;
+import org.openide.LifecycleManager;
+import org.openide.cookies.EditorCookie;
+import org.openide.filesystems.FileObject;
+import org.openide.filesystems.FileUtil;
+import org.openide.loaders.DataObject;
+import org.openide.modules.InstalledFileLocator;
+
+/**
+ *
+ * @author Jan Lahoda
+ */
+public class TransformationHintProviderImplTest extends NbTestCase {
+
+    public TransformationHintProviderImplTest(String name) {
+        super(name);
+    }
+
+    public void testSimpleAnalysis() throws Exception {
+        performAnalysisTest("package test;\n" +
+                            "import net.java.lang.annotations.Constraint;\n" +
+                            "import net.java.lang.annotations.Fix;\n" +
+                            "import net.java.lang.annotations.Pattern;\n" +
+                            "import net.java.lang.annotations.Transformation;\n" +
+                            "public class Test {\n" +
+                            "    @Transformation(\n" +
+                            "        displayName=\"O\",\n" +
+                            "        pattern=@Pattern(\"$0.getCookie($1)\"),\n" +
+                            "        constraint={\n" +
+                            "            @Constraint(variable=\"$0\", type=Test.class),\n" +
+                            "            @Constraint(variable=\"$1\", type=Class.class)\n" +
+                            "        },\n" +
+                            "        fix=@Fix(\"$0.lookup($1)\")\n" +
+                            "    )\n" +
+                            "    public void getCookie(Class c) {}\n" +
+                            "    private void test() {\n" +
+                            "         getCookie(null);\n" +
+                            "    }\n" +
+                            "}\n",
+                            "17:9-17:18:verifier:O");
+    }
+
+    public void testCanGoUp() throws Exception {
+        performAnalysisTest("package test;\n" +
+                            "import net.java.lang.annotations.Constraint;\n" +
+                            "import net.java.lang.annotations.Fix;\n" +
+                            "import net.java.lang.annotations.Pattern;\n" +
+                            "import net.java.lang.annotations.Transformation;\n" +
+                            "public abstract class Test {\n" +
+                            "    @Transformation(\n" +
+                            "        displayName=\"O\",\n" +
+                            "        pattern=@Pattern(\"$0.getCookie().toString()\"),\n" +
+                            "        constraint={\n" +
+                            "            @Constraint(variable=\"$0\", type=Test.class)\n" +
+                            "        },\n" +
+                            "        fix=@Fix(\"$0.lookup($1)\")\n" +
+                            "    )\n" +
+                            "    public abstract String getCookie();\n" +
+                            "    private void test() {\n" +
+                            "         getCookie().toString();\n" +
+                            "    }\n" +
+                            "}\n",
+                            "16:21-16:29:verifier:O");
+    }
+
+    public void testForConstructor() throws Exception {
+        performAnalysisTest("package test;\n" +
+                            "import net.java.lang.annotations.Constraint;\n" +
+                            "import net.java.lang.annotations.Fix;\n" +
+                            "import net.java.lang.annotations.Pattern;\n" +
+                            "import net.java.lang.annotations.Transformation;\n" +
+                            "public class Test {\n" +
+                            "    @Transformation(\n" +
+                            "        displayName=\"O\",\n" +
+                            "        pattern=@Pattern(\"new test.Test()\"),\n" +
+                            "        fix={}\n" +
+                            "    )\n" +
+                            "    public Test() {}\n" +
+                            "    private void test() {\n" +
+                            "         new Test();\n" +
+                            "    }\n" +
+                            "}\n",
+                            "13:9-13:19:verifier:O");
+    }
+
+    public void testMoreWarnings() throws Exception {
+        performAnalysisTest("package test;\n" +
+                            "import net.java.lang.annotations.Constraint;\n" +
+                            "import net.java.lang.annotations.Fix;\n" +
+                            "import net.java.lang.annotations.Pattern;\n" +
+                            "import net.java.lang.annotations.Transformation;\n" +
+                            "public class Test {\n" +
+                            "    @Transformation(\n" +
+                            "        displayName=\"O\",\n" +
+                            "        pattern=@Pattern(\"new test.Test()\"),\n" +
+                            "        fix={}\n" +
+                            "    )\n" +
+                            "    public Test() {}\n" +
+                            "    private void test() {\n" +
+                            "         new Test();\n" +
+                            "         new Test();\n" +
+                            "    }\n" +
+                            "}\n",
+                            "13:9-13:19:verifier:O",
+                            "14:9-14:19:verifier:O");
+    }
+
+    public void testCorrectImports() throws Exception {
+        performFixTest("test/Test.java",
+                            "package test;\n" +
+                            "import net.java.lang.annotations.Constraint;\n" +
+                            "import net.java.lang.annotations.Fix;\n" +
+                            "import net.java.lang.annotations.Pattern;\n" +
+                            "import net.java.lang.annotations.Transformation;\n" +
+                            "public abstract class Test {\n" +
+                            "    @Transformation(\n" +
+                            "        displayName=\"O\",\n" +
+                            "        pattern=@Pattern(\"$this.test()\"),\n" +
+                            "        fix=@Fix(\"test.Test.test1().toString()\")\n" +
+                            "    )\n" +
+                            "    private void test() {\n" +
+                            "         test();\n" +
+                            "    }" +
+                            "    static String test1() {return null;}\n" +
+                            "}\n",
+                            "12:9-12:13:verifier:O",
+                            "FixImpl",
+                            "package test;\n" +
+                            "import net.java.lang.annotations.Constraint;\n" +
+                            "import net.java.lang.annotations.Fix;\n" +
+                            "import net.java.lang.annotations.Pattern;\n" +
+                            "import net.java.lang.annotations.Transformation;\n" +
+                            "public abstract class Test {\n" +
+                            "    @Transformation(\n" +
+                            "        displayName=\"O\",\n" +
+                            "        pattern=@Pattern(\"$this.test()\"),\n" +
+                            "        fix=@Fix(\"test.Test.test1().toString()\")\n" +
+                            "    )\n" +
+                            "    private void test() {\n" +
+                            "         Test.test1().toString();\n" +
+                            "    }" +
+                            "    static String test1() {return null;}\n" +
+                            "}\n");
+    }
+
+    public void testNoExceptions1() throws Exception {
+        performNoExceptionsTest("package test;\n" +
+                                "import net.java.lang.annotations.Constraint;\n" +
+                                "import net.java.lang.annotations.Fix;\n" +
+                                "import net.java.lang.annotations.Pattern;\n" +
+                                "import net.java.lang.annotations.Transformation;\n" +
+                                "public abstract class Test {\n" +
+                                "    |public abstract String getCookie();\n" +
+                                "    private void test() {\n" +
+                                "         getCookie().toString();\n" +
+                                "    }\n" +
+                                "}\n",
+                                "    @Transformation(\n" +
+                                "        displayName=\"O\",\n" +
+                                "        pattern=@Pattern(\"$0.getCookie().toString()\"),\n" +
+                                "        constraint={\n" +
+                                "            @Constraint(variable=\"$0\", type=Test.class)\n" +
+                                "        },\n" +
+                                "        fix={@Fix(\"$0.lookup($1)\")}\n" +
+                                "    )\n".replaceAll("[ \n]+", " "));
+    }
+
+    public void testNoExceptions2() throws Exception {
+        performNoExceptionsTest("package test;\n" +
+                                "import net.java.lang.annotations.Constraint;\n" +
+                                "import net.java.lang.annotations.Fix;\n" +
+                                "import net.java.lang.annotations.Pattern;\n" +
+                                "import net.java.lang.annotations.Transformation;\n" +
+                                "public abstract class Test {\n" +
+                                "    |public abstract String getCookie();\n" +
+                                "    private void test() {\n" +
+                                "         getCookie().toString();\n" +
+                                "    }\n" +
+                                "}\n",
+                                "    @Transformation(\n" +
+                                "        displayName=\"O\",\n" +
+                                "        pattern=@Pattern(\"$0.getCookie().toString()\"),\n" +
+                                "        constraint=\n" +
+                                "            @Constraint(variable=\"$0\", type=Test.class)\n" +
+                                "        ,\n" +
+                                "        fix=@Fix(\"$0.lookup($1)\")\n" +
+                                "    )\n".replaceAll("[ \n]+", " "));
+    }
+
+    public void testType1() throws Exception {
+        performFixTest("test/Test.java",
+                            "package test;\n" +
+                            "import net.java.lang.annotations.Constraint;\n" +
+                            "import net.java.lang.annotations.Fix;\n" +
+                            "import net.java.lang.annotations.Pattern;\n" +
+                            "import net.java.lang.annotations.Transformation;\n" +
+                            "public abstract class Test {\n" +
+                            "    @Transformation(\n" +
+                            "        displayName=\"O\",\n" +
+                            "        pattern=@Pattern(\"test.Test.Bad\"),\n" +
+                            "        fix=@Fix(\"test.Test.Good\")\n" +
+                            "    )\n" +
+                            "    public static class Bad {}\n" +
+                            "    public static class Good {}\n" +
+                            "    static void test1() { Bad b; }\n" +
+                            "}\n",
+                            "13:26-13:29:verifier:O",
+                            "FixImpl",
+                            "package test;\n" +
+                            "import net.java.lang.annotations.Constraint;\n" +
+                            "import net.java.lang.annotations.Fix;\n" +
+                            "import net.java.lang.annotations.Pattern;\n" +
+                            "import net.java.lang.annotations.Transformation;\n" +
+                            "public abstract class Test {\n" +
+                            "    @Transformation(\n" +
+                            "        displayName=\"O\",\n" +
+                            "        pattern=@Pattern(\"test.Test.Bad\"),\n" +
+                            "        fix=@Fix(\"test.Test.Good\")\n" +
+                            "    )\n" +
+                            "    public static class Bad {}\n" +
+                            "    public static class Good {}\n" +
+                            "    static void test1() { Good b; }\n" +
+                            "}\n".replaceAll("[ \n]+", " "));
+    }
+
+    public void testCorrectReplace() throws Exception {
+        performFixTest("test/Test.java",
+                            "package test;\n" +
+                            "import net.java.lang.annotations.Constraint;\n" +
+                            "import net.java.lang.annotations.Fix;\n" +
+                            "import net.java.lang.annotations.Pattern;\n" +
+                            "import net.java.lang.annotations.Transformation;\n" +
+                            "public class Test {\n" +
+                            "    @Transformation(\n" +
+                            "        displayName=\"O\",\n" +
+                            "        pattern=@Pattern(\"$this.test()\"),\n" +
+                            "        fix=@Fix(\"$this.test()\")\n" +
+                            "    )\n" +
+                            "    private void test() {\n" +
+                            "         new Test().test();\n" +
+                            "    }\n" +
+                            "}\n",
+                            "12:20-12:24:verifier:O",
+                            "FixImpl",
+                            "package test;\n" +
+                            "import net.java.lang.annotations.Constraint;\n" +
+                            "import net.java.lang.annotations.Fix;\n" +
+                            "import net.java.lang.annotations.Pattern;\n" +
+                            "import net.java.lang.annotations.Transformation;\n" +
+                            "public class Test {\n" +
+                            "    @Transformation(\n" +
+                            "        displayName=\"O\",\n" +
+                            "        pattern=@Pattern(\"$this.test()\"),\n" +
+                            "        fix=@Fix(\"$this.test()\")\n" +
+                            "    )\n" +
+                            "    private void test() {\n" +
+                            "         new Test().test();\n" +
+                            "    }\n" +
+                            "}\n");
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        
+        File jar = FileUtil.archiveOrDirForURL(TransformationHintProviderImpl.class.getProtectionDomain().getCodeSource().getLocation());
+
+        assertNotNull(jar);
+
+        String nbHome = jar.getParentFile().getParentFile().getAbsolutePath();
+
+        System.setProperty("netbeans.home",nbHome);
+
+        SourceUtilsTestUtil.prepareTest(new String[] {"org/netbeans/modules/java/editor/resources/layer.xml"}, new Object[0]);
+        TreeLoader.DISABLE_CONFINEMENT_TEST = true;
+    }
+
+    private List<ErrorDescription> computeWarnings() {
+        Map<Kind, List<HintDescription>> hints = new HashMap<Kind, List<HintDescription>>();
+        Map<PatternDescription, List<HintDescription>> patternHints = new HashMap<PatternDescription, List<HintDescription>>();
+
+        RulesManager.computeElementBasedHintsXXX(info, new AtomicBoolean(), Collections.singletonList(new TransformationHintProviderImpl()), hints, patternHints);
+
+        List<ErrorDescription> warnings = new HintsInvoker().computeHints(info, hints, patternHints);
+
+        return warnings;
+    }
+
+    private void prepareTest(String fileName, String code) throws Exception {
+        prepareTest(fileName, code, false);
+    }
+
+    private void prepareTest(String fileName, String code, boolean ignoreErrors) throws Exception {
+        clearWorkDir();
+
+        FileUtil.refreshFor(File.listRoots());
+
+        FileObject workFO = FileUtil.toFileObject(getWorkDir());
+
+        assertNotNull(workFO);
+
+        workFO.refresh();
+
+        sourceRoot = workFO.createFolder("src");
+        FileObject buildRoot  = workFO.createFolder("build");
+        FileObject cache = workFO.createFolder("cache");
+
+        FileObject data = FileUtil.createData(sourceRoot, fileName);
+        File dataFile = FileUtil.toFile(data);
+
+        assertNotNull(dataFile);
+
+        TestUtilities.copyStringToFile(dataFile, code);
+
+        File ann = InstalledFileLocator.getDefault().locate("libs/annotations.jar", null, false);
+
+        assertNotNull(ann);
+
+        FileObject annFO = FileUtil.toFileObject(ann);
+
+        assertNotNull(annFO);
+
+        SourceUtilsTestUtil.prepareTest(sourceRoot, buildRoot, cache, new FileObject[] {FileUtil.getArchiveRoot(annFO)});
+
+        DataObject od = DataObject.find(data);
+        EditorCookie ec = od.getLookup().lookup(EditorCookie.class);
+
+        assertNotNull(ec);
+
+        doc = ec.openDocument();
+        doc.putProperty(Language.class, JavaTokenId.language());
+
+        JavaSource js = JavaSource.forFileObject(data);
+
+        assertNotNull(js);
+
+        info = SourceUtilsTestUtil.getCompilationInfo(js, Phase.RESOLVED);
+
+        assertNotNull(info);
+
+        if (!ignoreErrors) {
+            assertEquals(info.getDiagnostics().toString(), 0, info.getDiagnostics().size());
+        }
+    }
+
+    private FileObject sourceRoot;
+    private CompilationInfo info;
+    private Document doc;
+    private String wordDirPath;
+    
+    protected void performNoExceptionsTest(String code, String type) throws Exception {
+        String wDP = getWorkDirPath();
+        
+        int offset = code.indexOf('|');
+
+        code = code.replace("|", "");
+
+        File prev = null;
+
+        for (int i = 0; i < type.length(); i++) {
+            StringBuilder nueCode = new StringBuilder();
+
+            nueCode.append(code);
+            nueCode.insert(offset, type.substring(0, i + 1));
+
+            this.wordDirPath = wDP + File.separatorChar + i;
+            
+            performAnalysisTest(nueCode.toString(), null, true);
+
+            if (prev != null) {
+                deleteFile(prev);
+                FileUtil.refreshFor(File.listRoots());
+            }
+
+            prev = getWorkDir();
+        }
+    }
+
+    protected void performAnalysisTest(String code, String... golden) throws Exception {
+        performAnalysisTest(code, golden, false);
+    }
+
+    protected void performAnalysisTest(String code, String[] golden, boolean ignoreErrors) throws Exception {
+        prepareTest("test/Test.java", code, ignoreErrors);
+
+        List<ErrorDescription> warnings = computeWarnings();
+        List<String> dns = new LinkedList<String>();
+
+        for (ErrorDescription ed : warnings) {
+            dns.add(ed.toString());
+        }
+
+        if (golden != null) {
+            assertEquals(Arrays.asList(golden), dns);
+        }
+    }
+
+    protected String performFixTest(String fileName, String code, String errorDescriptionToString, String fixDebugString, String golden) throws Exception {
+        prepareTest(fileName, code);
+
+        List<ErrorDescription> errors = computeWarnings();
+
+        ErrorDescription toFix = null;
+
+        for (ErrorDescription d : errors) {
+            if (errorDescriptionToString.equals(d.toString())) {
+                toFix = d;
+                break;
+            }
+        }
+
+        assertNotNull("Error: \"" + errorDescriptionToString + "\" not found. All ErrorDescriptions: " + errors.toString(), toFix);
+
+        assertTrue("Must be computed", toFix.getFixes().isComputed());
+
+        List<Fix> fixes = toFix.getFixes().getFixes();
+        List<String> fixNames = new LinkedList<String>();
+        Fix toApply = null;
+
+        for (Fix f : fixes) {
+            if (fixDebugString.equals(toDebugString(info, f))) {
+                toApply = f;
+            }
+
+            fixNames.add(toDebugString(info, f));
+        }
+
+        assertNotNull("Cannot find fix to invoke: " + fixNames.toString(), toApply);
+
+        toApply.implement();
+
+        FileObject toCheck = sourceRoot.getFileObject(fileName);
+
+        assertNotNull(toCheck);
+
+        DataObject toCheckDO = DataObject.find(toCheck);
+        EditorCookie ec = toCheckDO.getCookie(EditorCookie.class);
+        Document toCheckDocument = ec.openDocument();
+
+        String realCode = toCheckDocument.getText(0, toCheckDocument.getLength());
+
+        //ignore whitespaces:
+        realCode = realCode.replaceAll("[ \t\n]+", " ");
+
+        if (golden != null) {
+            golden = golden.replaceAll("[ \t\n]+", " ");
+            assertEquals("The output code does not match the expected code.", golden, realCode);
+        }
+
+        LifecycleManager.getDefault().saveAll();
+
+        return realCode;
+    }
+
+    private String toDebugString(CompilationInfo info, Fix f) {
+        return "FixImpl";
+    }
+
+    @Override
+    public String getWorkDirPath() {
+        if (this.wordDirPath != null) {
+            return this.wordDirPath;
+        }
+        
+        return super.getWorkDirPath();
+    }
+
+    private static void deleteFile(File file) throws IOException {
+        if (file.isDirectory()) {
+            // file is a directory - delete sub files first
+            File files[] = file.listFiles();
+            for (int i = 0; i < files.length; i++) {
+                deleteFile(files[i]);
+            }
+
+        }
+        // file is a File :-)
+        boolean result = file.delete();
+        if (result == false ) {
+            // a problem has appeared
+            throw new IOException("Cannot delete file, file = "+file.getPath());
+        }
+    }
+}