This Bugzilla instance is a read-only archive of historic NetBeans bug reports. To report a bug in NetBeans please follow the project's instructions for reporting issues.

View | Details | Raw Unified | Return to bug 246587
Collapse All | Expand All

(-)a/java.hints/src/org/netbeans/modules/java/hints/threading/Tiny.java (-2 / +2 lines)
Lines 243-253 Link Here
243
    }
243
    }
244
244
245
    @Hint(displayName = "#DN_org.netbeans.modules.java.hints.threading.Tiny.synchronizedOnLock", description = "#DESC_org.netbeans.modules.java.hints.threading.Tiny.synchronizedOnLock", category="thread", suppressWarnings="SynchroniziationOnLockObject")
245
    @Hint(displayName = "#DN_org.netbeans.modules.java.hints.threading.Tiny.synchronizedOnLock", description = "#DESC_org.netbeans.modules.java.hints.threading.Tiny.synchronizedOnLock", category="thread", suppressWarnings="SynchroniziationOnLockObject")
246
    @TriggerPattern(value="synchronized ($lock) {$statements$;}",
246
    @TriggerPattern(value="synchronized ($lock) #{s}#{$statements$;}",
247
                    constraints=@ConstraintVariableType(variable="$lock", type="java.util.concurrent.locks.Lock"))
247
                    constraints=@ConstraintVariableType(variable="$lock", type="java.util.concurrent.locks.Lock"))
248
    public static ErrorDescription synchronizedOnLock(HintContext ctx) {
248
    public static ErrorDescription synchronizedOnLock(HintContext ctx) {
249
        String fixDisplayName = NbBundle.getMessage(Tiny.class, "FIX_SynchronizedOnLock");
249
        String fixDisplayName = NbBundle.getMessage(Tiny.class, "FIX_SynchronizedOnLock");
250
        Fix f = JavaFixUtilities.rewriteFix(ctx, fixDisplayName, ctx.getPath(), "$lock.lock(); try {$statements$;} finally {$lock.unlock();}");
250
        Fix f = JavaFixUtilities.rewriteFix(ctx, fixDisplayName, ctx.getPath(), "$lock.lock(); try #{s}#{$statements$;} finally {$lock.unlock();}", ctx.getAnchors());
251
        String displayName = NbBundle.getMessage(Tiny.class, "ERR_SynchronizedOnLock");
251
        String displayName = NbBundle.getMessage(Tiny.class, "ERR_SynchronizedOnLock");
252
252
253
        return ErrorDescriptionFactory.forName(ctx, ctx.getPath(), displayName, f);
253
        return ErrorDescriptionFactory.forName(ctx, ctx.getPath(), displayName, f);
(-)a/java.source/apichanges.xml (+12 lines)
Lines 108-113 Link Here
108
    <!-- ACTUAL CHANGES BEGIN HERE: -->
108
    <!-- ACTUAL CHANGES BEGIN HERE: -->
109
109
110
    <changes>
110
    <changes>
111
        <change id="PatternAnchors">
112
            <api name="general"/>
113
            <summary>Places in search pattern can be named for further navigation in code transformation process</summary>
114
            <version major="0" minor="141"/>
115
            <date day="29" month="8" year="2014"/>
116
            <author login="sdedic"/>
117
            <compatibility addition="yes" binary="compatible" source="compatible"/>
118
            <description>
119
                The Pattern is able to accept and record places (TreePaths) that match named Trees
120
                in the passed tree pattern. The recorded matches are available from the {@code Occurrence} result class.
121
            </description>
122
        </change>
111
        <change id="RemoteJavadocPolicy">
123
        <change id="RemoteJavadocPolicy">
112
            <api name="general"/>
124
            <api name="general"/>
113
            <summary>Added a possibility to specify remote javadoc handling policy</summary>
125
            <summary>Added a possibility to specify remote javadoc handling policy</summary>
(-)a/java.source/nbproject/project.properties (-1 / +1 lines)
Lines 46-52 Link Here
46
javadoc.title=Java Source
46
javadoc.title=Java Source
47
javadoc.arch=${basedir}/arch.xml
47
javadoc.arch=${basedir}/arch.xml
48
javadoc.apichanges=${basedir}/apichanges.xml
48
javadoc.apichanges=${basedir}/apichanges.xml
49
spec.version.base=0.140.0
49
spec.version.base=0.141.0
50
test.qa-functional.cp.extra=${refactoring.java.dir}/modules/ext/nb-javac-api.jar
50
test.qa-functional.cp.extra=${refactoring.java.dir}/modules/ext/nb-javac-api.jar
51
test.unit.run.cp.extra=${o.n.core.dir}/core/core.jar:\
51
test.unit.run.cp.extra=${o.n.core.dir}/core/core.jar:\
52
    ${o.n.core.dir}/lib/boot.jar:\
52
    ${o.n.core.dir}/lib/boot.jar:\
(-)a/java.source/src/org/netbeans/api/java/source/matching/Matcher.java (-3 / +6 lines)
Lines 42-52 Link Here
42
package org.netbeans.api.java.source.matching;
42
package org.netbeans.api.java.source.matching;
43
43
44
import com.sun.source.tree.CompilationUnitTree;
44
import com.sun.source.tree.CompilationUnitTree;
45
import com.sun.source.tree.Tree;
45
import com.sun.source.util.TreePath;
46
import com.sun.source.util.TreePath;
46
import java.util.ArrayList;
47
import java.util.ArrayList;
47
import java.util.Collection;
48
import java.util.Collection;
48
import java.util.Collections;
49
import java.util.Collections;
49
import java.util.EnumSet;
50
import java.util.EnumSet;
51
import java.util.HashMap;
50
import java.util.List;
52
import java.util.List;
51
import java.util.Map;
53
import java.util.Map;
52
import java.util.Map.Entry;
54
import java.util.Map.Entry;
Lines 175-181 Link Here
175
        State preinitializeState;
177
        State preinitializeState;
176
178
177
        if (variables != null) {
179
        if (variables != null) {
178
            preinitializeState = State.from(variables, multiVariables, variables2Names);
180
            preinitializeState = State.from(variables, multiVariables, variables2Names, new HashMap<String, TreePath>());
179
        } else {
181
        } else {
180
            preinitializeState = null;
182
            preinitializeState = null;
181
        }
183
        }
Lines 186-193 Link Here
186
            }
188
            }
187
        };
189
        };
188
190
189
        for (Entry<TreePath, VariableAssignments> e : CopyFinder.internalComputeDuplicates(info, pattern.pattern, root, preinitializeState, pattern.remappable, cancel, pattern.variable2Type, opts.toArray(new Options[opts.size()])).entrySet()) {
191
        for (Entry<TreePath, VariableAssignments> e : CopyFinder.internalComputeDuplicates(info, pattern.pattern, root, preinitializeState, pattern.remappable, cancel, pattern.variable2Type, pattern.anchorNames, opts.toArray(new Options[opts.size()])).entrySet()) {
190
            result.add(new Occurrence(e.getKey(), e.getValue().variables, e.getValue().multiVariables, e.getValue().variables2Names, e.getValue().variablesRemapToElement, e.getValue().variablesRemapToTrees));
192
            result.add(new Occurrence(e.getKey(), e.getValue().variables, e.getValue().multiVariables, e.getValue().variables2Names, e.getValue().variablesRemapToElement, e.getValue().variablesRemapToTrees,
193
            e.getValue().anchors));
191
        }
194
        }
192
195
193
        return Collections.unmodifiableCollection(result);
196
        return Collections.unmodifiableCollection(result);
(-)a/java.source/src/org/netbeans/api/java/source/matching/Occurrence.java (-2 / +14 lines)
Lines 57-70 Link Here
57
    private final Map<String, String> variables2Names;
57
    private final Map<String, String> variables2Names;
58
    private final Map<Element, Element> variablesRemapToElement;
58
    private final Map<Element, Element> variablesRemapToElement;
59
    private final Map<Element, TreePath> variablesRemapToTrees;
59
    private final Map<Element, TreePath> variablesRemapToTrees;
60
60
    private final Map<String, TreePath> anchors;
61
    Occurrence(TreePath occurrenceRoot, Map<String, TreePath> variables, Map<String, Collection<? extends TreePath>> multiVariables, Map<String, String> variables2Names, Map<Element, Element> variablesRemapToElement, Map<Element, TreePath> variablesRemapToTrees) {
61
    
62
    Occurrence(TreePath occurrenceRoot, Map<String, TreePath> variables, Map<String, Collection<? extends TreePath>> multiVariables, Map<String, String> variables2Names, Map<Element, Element> variablesRemapToElement, 
63
            Map<Element, TreePath> variablesRemapToTrees, Map<String, TreePath> anchors) {
62
        this.occurrenceRoot = occurrenceRoot;
64
        this.occurrenceRoot = occurrenceRoot;
63
        this.variables = variables;
65
        this.variables = variables;
64
        this.multiVariables = multiVariables;
66
        this.multiVariables = multiVariables;
65
        this.variables2Names = variables2Names;
67
        this.variables2Names = variables2Names;
66
        this.variablesRemapToElement = variablesRemapToElement;
68
        this.variablesRemapToElement = variablesRemapToElement;
67
        this.variablesRemapToTrees = variablesRemapToTrees;
69
        this.variablesRemapToTrees = variablesRemapToTrees;
70
        this.anchors = anchors;
68
    }
71
    }
69
72
70
    /**The tree node that represents the occurrence. For multi-part patterns {@link Pattern#createSimplePattern(java.lang.Iterable) },
73
    /**The tree node that represents the occurrence. For multi-part patterns {@link Pattern#createSimplePattern(java.lang.Iterable) },
Lines 125-128 Link Here
125
        return variablesRemapToTrees;
128
        return variablesRemapToTrees;
126
    }
129
    }
127
130
131
    /**
132
     * Retrieves trees matched to the named anchors in the pattern.
133
     * For each matched anchor provides a TreePath to the corresponding Tree.
134
     * @return matched named trees.
135
     * @since 0.141
136
     */
137
    public Map<String, TreePath> getAnchors() {
138
        return anchors;
139
    }
128
}
140
}
(-)a/java.source/src/org/netbeans/api/java/source/matching/Pattern.java (+16 lines)
Lines 42-47 Link Here
42
package org.netbeans.api.java.source.matching;
42
package org.netbeans.api.java.source.matching;
43
43
44
import com.sun.source.tree.IdentifierTree;
44
import com.sun.source.tree.IdentifierTree;
45
import com.sun.source.tree.Tree;
45
import com.sun.source.util.TreePath;
46
import com.sun.source.util.TreePath;
46
import java.util.ArrayList;
47
import java.util.ArrayList;
47
import java.util.Arrays;
48
import java.util.Arrays;
Lines 64-69 Link Here
64
    final Map<String, TypeMirror> variable2Type;
65
    final Map<String, TypeMirror> variable2Type;
65
    final Collection<? extends VariableElement> remappable;
66
    final Collection<? extends VariableElement> remappable;
66
    final boolean allowRemapToTrees;
67
    final boolean allowRemapToTrees;
68
    Map<Tree, String> anchorNames = Collections.emptyMap();
67
69
68
    /**Creates a simple pattern. Tree nodes that are semantically equivalent
70
    /**Creates a simple pattern. Tree nodes that are semantically equivalent
69
     * to the given pattern will match it.
71
     * to the given pattern will match it.
Lines 154-157 Link Here
154
        this.allowRemapToTrees = allowRemapToTrees;
156
        this.allowRemapToTrees = allowRemapToTrees;
155
    }
157
    }
156
158
159
    /**
160
     * Provides naming for certain trees in the pattern. The names are then assigned by matched real trees, so the caller may
161
     * inspect details of the matched code, without the need to repeat some pattern-matching fuzzy logic to dive into the 
162
     * matched subtree.
163
     * 
164
     * @param anchorNames Names for individual Trees. Trees that do not exist somewhere in the pattern will be ignored
165
     * and no match will be assigned to them
166
     * @return this instance
167
     * @since 0.141
168
     */
169
    public final Pattern setAnchors(Map<Tree, String> anchorNames) {
170
        this.anchorNames = anchorNames == null ? Collections.<Tree, String>emptyMap() : anchorNames;
171
        return this;
172
    }
157
}
173
}
(-)a/java.source/src/org/netbeans/modules/java/source/matching/CopyFinder.java (-21 / +50 lines)
Lines 131-138 Link Here
131
    private final Cancel cancel;
131
    private final Cancel cancel;
132
    private static final String CLASS = "class"; //NOI18N
132
    private static final String CLASS = "class"; //NOI18N
133
    private final Set<Options> options;
133
    private final Set<Options> options;
134
134
    private Map<Tree, String> anchorNames;
135
    private Map<String, TypeMirror> designedTypeHack;
135
    private Map<String, TypeMirror> designedTypeHack;
136
    private Map<String, TreePath> anchors = new HashMap<>();
136
137
137
    private static Set<Options> options(Options... options) {
138
    private static Set<Options> options(Options... options) {
138
        Set<Options> result = EnumSet.noneOf(Options.class);
139
        Set<Options> result = EnumSet.noneOf(Options.class);
Lines 152-159 Link Here
152
        this.cancel = cancel;
153
        this.cancel = cancel;
153
        this.options = options;
154
        this.options = options;
154
    }
155
    }
156
    
157
    public static Map<TreePath, VariableAssignments> internalComputeDuplicates(CompilationInfo info, Collection<? extends TreePath> searchingFor, TreePath scope, State preinitializedState, Collection<? extends VariableElement> variablesWithAllowedRemap, Cancel cancel, Map<String, TypeMirror> designedTypeHack, Options... options) {
158
        return internalComputeDuplicates(info, searchingFor, scope, preinitializedState, variablesWithAllowedRemap, cancel, designedTypeHack, 
159
                new HashMap<Tree, String>(), options);
160
    }
155
161
156
    public static Map<TreePath, VariableAssignments> internalComputeDuplicates(CompilationInfo info, Collection<? extends TreePath> searchingFor, TreePath scope, State preinitializedState, Collection<? extends VariableElement> variablesWithAllowedRemap, Cancel cancel, Map<String, TypeMirror> designedTypeHack, Options... options) {
162
    public static Map<TreePath, VariableAssignments> internalComputeDuplicates(CompilationInfo info, Collection<? extends TreePath> searchingFor, TreePath scope, State preinitializedState, Collection<? extends VariableElement> variablesWithAllowedRemap, Cancel cancel, Map<String, TypeMirror> designedTypeHack, 
163
            Map<Tree, String> anchorNames, Options... options) {
157
        TreePath first = searchingFor.iterator().next();
164
        TreePath first = searchingFor.iterator().next();
158
        Set<Options> optionsSet = EnumSet.noneOf(Options.class);
165
        Set<Options> optionsSet = EnumSet.noneOf(Options.class);
159
166
Lines 170-176 Link Here
170
        f.variablesWithAllowedRemap = variablesWithAllowedRemap != null ? new HashSet<VariableElement>(variablesWithAllowedRemap) : Collections.<VariableElement>emptySet();
177
        f.variablesWithAllowedRemap = variablesWithAllowedRemap != null ? new HashSet<VariableElement>(variablesWithAllowedRemap) : Collections.<VariableElement>emptySet();
171
        f.allowVariablesRemap = variablesWithAllowedRemap != null;
178
        f.allowVariablesRemap = variablesWithAllowedRemap != null;
172
        f.nocheckOnAllowVariablesRemap = variablesWithAllowedRemap != null;
179
        f.nocheckOnAllowVariablesRemap = variablesWithAllowedRemap != null;
173
180
        f.anchorNames = anchorNames;
174
        if (preinitializedState != null) {
181
        if (preinitializedState != null) {
175
            f.bindState = State.copyOf(f.preinitializeState = preinitializedState);
182
            f.bindState = State.copyOf(f.preinitializeState = preinitializedState);
176
        }
183
        }
Lines 215-221 Link Here
215
            Map<String, String> variables2Names = new HashMap<String, String>(e.getValue().variables2Names);
222
            Map<String, String> variables2Names = new HashMap<String, String>(e.getValue().variables2Names);
216
            Map<Element, Element> remapElements = new HashMap<Element, Element>(e.getValue().variablesRemapToElement);
223
            Map<Element, Element> remapElements = new HashMap<Element, Element>(e.getValue().variablesRemapToElement);
217
            Map<Element, TreePath> remapTrees = new HashMap<Element, TreePath>(e.getValue().variablesRemapToTrees);
224
            Map<Element, TreePath> remapTrees = new HashMap<Element, TreePath>(e.getValue().variablesRemapToTrees);
218
225
            Map<String, TreePath> anchors = new HashMap<>(e.getValue().anchors);
219
            toProcess.next();
226
            toProcess.next();
220
227
221
            while (toProcess.hasNext()) {
228
            while (toProcess.hasNext()) {
Lines 229-235 Link Here
229
                ver.variablesWithAllowedRemap = variablesWithAllowedRemap != null ? new HashSet<VariableElement>(variablesWithAllowedRemap) : Collections.<VariableElement>emptySet();
236
                ver.variablesWithAllowedRemap = variablesWithAllowedRemap != null ? new HashSet<VariableElement>(variablesWithAllowedRemap) : Collections.<VariableElement>emptySet();
230
                ver.allowVariablesRemap = variablesWithAllowedRemap != null;
237
                ver.allowVariablesRemap = variablesWithAllowedRemap != null;
231
                ver.nocheckOnAllowVariablesRemap = variablesWithAllowedRemap != null;
238
                ver.nocheckOnAllowVariablesRemap = variablesWithAllowedRemap != null;
232
                ver.bindState = State.from(variables, multiVariables, variables2Names);
239
                ver.bindState = State.from(variables, multiVariables, variables2Names, anchors);
233
240
234
                if (ver.allowVariablesRemap) {
241
                if (ver.allowVariablesRemap) {
235
                    ver.bindState = State.from(ver.bindState, remapElements, remapTrees);
242
                    ver.bindState = State.from(ver.bindState, remapElements, remapTrees);
Lines 244-252 Link Here
244
                variables2Names = ver.bindState.variables2Names;
251
                variables2Names = ver.bindState.variables2Names;
245
                remapElements = ver.bindState.variablesRemapToElement;
252
                remapElements = ver.bindState.variablesRemapToElement;
246
                remapTrees = ver.bindState.variablesRemapToTrees;
253
                remapTrees = ver.bindState.variablesRemapToTrees;
254
                anchors = ver.bindState.anchors;
247
            }
255
            }
248
256
249
            result.put(e.getKey(), new VariableAssignments(variables, multiVariables, variables2Names, remapElements, remapTrees));
257
            result.put(e.getKey(), new VariableAssignments(variables, multiVariables, variables2Names, remapElements, remapTrees, anchors));
250
        }
258
        }
251
259
252
        return result;
260
        return result;
Lines 323-328 Link Here
323
331
324
    @Override
332
    @Override
325
    public Boolean scan(Tree node, TreePath p) {
333
    public Boolean scan(Tree node, TreePath p) {
334
        Boolean r = scan0(node, p);
335
        if (r != null && r && anchorNames != null) {
336
            String tn = anchorNames.get(p.getLeaf());
337
            if (tn != null) {
338
                bindState.anchors.put(tn, getCurrentPath());
339
            }
340
        }
341
        return r;
342
    }
343
    
344
    Boolean scan0(Tree node, TreePath p) {
326
        if (cancel.isCancelled()) {
345
        if (cancel.isCancelled()) {
327
            return false;
346
            return false;
328
        }
347
        }
Lines 1788-1808 Link Here
1788
        public final Map<String, String> variables2Names;
1807
        public final Map<String, String> variables2Names;
1789
        public final Map<Element, Element> variablesRemapToElement;
1808
        public final Map<Element, Element> variablesRemapToElement;
1790
        public final Map<Element, TreePath> variablesRemapToTrees;
1809
        public final Map<Element, TreePath> variablesRemapToTrees;
1810
        public final Map<String, TreePath> anchors;
1791
1811
1792
        public VariableAssignments(Map<String, TreePath> variables, Map<String, Collection<? extends TreePath>> multiVariables, Map<String, String> variables2Names) {
1812
//        public VariableAssignments(Map<String, TreePath> variables, Map<String, Collection<? extends TreePath>> multiVariables, Map<String, String> variables2Names) {
1793
            this.variables = variables;
1813
//            this.variables = variables;
1794
            this.multiVariables = multiVariables;
1814
//            this.multiVariables = multiVariables;
1795
            this.variables2Names = variables2Names;
1815
//            this.variables2Names = variables2Names;
1796
            this.variablesRemapToElement = null;
1816
//            this.variablesRemapToElement = null;
1797
            this.variablesRemapToTrees = null;
1817
//            this.variablesRemapToTrees = null;
1798
        }
1818
//        }
1799
1819
//
1800
        public VariableAssignments(Map<String, TreePath> variables, Map<String, Collection<? extends TreePath>> multiVariables, Map<String, String> variables2Names, Map<Element, Element> variablesRemapToElement, Map<Element, TreePath> variablesRemapToTrees) {
1820
        public VariableAssignments(Map<String, TreePath> variables, Map<String, Collection<? extends TreePath>> multiVariables, Map<String, String> variables2Names, Map<Element, Element> variablesRemapToElement, Map<Element, TreePath> variablesRemapToTrees, Map<String, TreePath> anchors) {
1801
            this.variables = variables;
1821
            this.variables = variables;
1802
            this.multiVariables = multiVariables;
1822
            this.multiVariables = multiVariables;
1803
            this.variables2Names = variables2Names;
1823
            this.variables2Names = variables2Names;
1804
            this.variablesRemapToElement = variablesRemapToElement;
1824
            this.variablesRemapToElement = variablesRemapToElement;
1805
            this.variablesRemapToTrees = variablesRemapToTrees;
1825
            this.variablesRemapToTrees = variablesRemapToTrees;
1826
            this.anchors = anchors;
1806
        }
1827
        }
1807
1828
1808
        VariableAssignments(State state) {
1829
        VariableAssignments(State state) {
Lines 1811-1816 Link Here
1811
            this.variables2Names = state.variables2Names;
1832
            this.variables2Names = state.variables2Names;
1812
            this.variablesRemapToElement = state.variablesRemapToElement;
1833
            this.variablesRemapToElement = state.variablesRemapToElement;
1813
            this.variablesRemapToTrees = state.variablesRemapToTrees;
1834
            this.variablesRemapToTrees = state.variablesRemapToTrees;
1835
            this.anchors = state.anchors;
1814
        }
1836
        }
1815
    }
1837
    }
1816
1838
Lines 1835-1862 Link Here
1835
        final Map<String, String> variables2Names;
1857
        final Map<String, String> variables2Names;
1836
        final Map<Element, Element> variablesRemapToElement;
1858
        final Map<Element, Element> variablesRemapToElement;
1837
        final Map<Element, TreePath> variablesRemapToTrees;
1859
        final Map<Element, TreePath> variablesRemapToTrees;
1860
        Map<String, TreePath> anchors;
1838
1861
1839
        private State(Map<String, TreePath> variables, Map<String, Collection<? extends TreePath>> multiVariables, Map<String, String> variables2Names, Map<Element, Element> variablesRemapToElement, Map<Element, TreePath> variablesRemapToTrees) {
1862
        private State(Map<String, TreePath> variables, Map<String, Collection<? extends TreePath>> multiVariables, Map<String, String> variables2Names, Map<Element, Element> variablesRemapToElement, 
1863
                Map<Element, TreePath> variablesRemapToTrees, Map<String, TreePath> anchors) {
1840
            this.variables = variables;
1864
            this.variables = variables;
1841
            this.multiVariables = multiVariables;
1865
            this.multiVariables = multiVariables;
1842
            this.variables2Names = variables2Names;
1866
            this.variables2Names = variables2Names;
1843
            this.variablesRemapToElement = variablesRemapToElement;
1867
            this.variablesRemapToElement = variablesRemapToElement;
1844
            this.variablesRemapToTrees = variablesRemapToTrees;
1868
            this.variablesRemapToTrees = variablesRemapToTrees;
1869
            this.anchors = anchors;
1845
        }
1870
        }
1846
        public static State empty() {
1871
        public static State empty() {
1847
            return new State(new HashMap<String, TreePath>(), new HashMap<String, Collection<? extends TreePath>>(), new HashMap<String, String>(), new HashMap<Element, Element>(), new HashMap<Element, TreePath>());
1872
            return new State(new HashMap<String, TreePath>(), new HashMap<String, Collection<? extends TreePath>>(), new HashMap<String, String>(), new HashMap<Element, Element>(), new HashMap<Element, TreePath>(), 
1873
                    new HashMap<String, TreePath>());
1848
        }
1874
        }
1849
1875
1850
        public static State copyOf(State original) {
1876
        public static State copyOf(State original) {
1851
            return new State(new HashMap<String, TreePath>(original.variables), new HashMap<String, Collection<? extends TreePath>>(original.multiVariables), new HashMap<String, String>(original.variables2Names), new HashMap<Element, Element>(original.variablesRemapToElement), new HashMap<Element, TreePath>(original.variablesRemapToTrees));
1877
            return new State(new HashMap<String, TreePath>(original.variables), new HashMap<String, Collection<? extends TreePath>>(original.multiVariables), new HashMap<String, String>(original.variables2Names), new HashMap<Element, Element>(original.variablesRemapToElement), 
1878
                    new HashMap<Element, TreePath>(original.variablesRemapToTrees), new HashMap<>(original.anchors));
1852
        }
1879
        }
1853
1880
1854
        public static State from(State original, Map<Element, Element> variablesRemapToElement, Map<Element, TreePath> variablesRemapToTrees) {
1881
        public static State from(State original, Map<Element, Element> variablesRemapToElement, Map<Element, TreePath> variablesRemapToTrees) {
1855
            return new State(new HashMap<String, TreePath>(original.variables), new HashMap<String, Collection<? extends TreePath>>(original.multiVariables), new HashMap<String, String>(original.variables2Names), variablesRemapToElement, variablesRemapToTrees);
1882
            return new State(new HashMap<String, TreePath>(original.variables), new HashMap<String, Collection<? extends TreePath>>(original.multiVariables), new HashMap<String, String>(original.variables2Names), variablesRemapToElement, variablesRemapToTrees,
1883
            new HashMap<String, TreePath>(original.anchors));
1856
        }
1884
        }
1857
1885
1858
        public static State from(Map<String, TreePath> variables, Map<String, Collection<? extends TreePath>> multiVariables, Map<String, String> variables2Names) {
1886
        public static State from(Map<String, TreePath> variables, Map<String, Collection<? extends TreePath>> multiVariables, Map<String, String> variables2Names, 
1859
            return new State(variables, multiVariables, variables2Names, new HashMap<Element, Element>(), new HashMap<Element, TreePath>());
1887
                Map<String, TreePath> anchors) {
1888
            return new State(variables, multiVariables, variables2Names, new HashMap<Element, Element>(), new HashMap<Element, TreePath>(), anchors);
1860
        }
1889
        }
1861
    }
1890
    }
1862
1891
(-)a/spi.java.hints/apichanges.xml (+16 lines)
Lines 46-51 Link Here
46
        <apidef name="JavaHintsSPI">Java Hints SPI</apidef>
46
        <apidef name="JavaHintsSPI">Java Hints SPI</apidef>
47
    </apidefs>
47
    </apidefs>
48
    <changes>
48
    <changes>
49
        <change id="PatternAnchors">
50
            <api name="JavaHintsSPI">
51
                <summary>Added ability to associate parts of template-based fix with pieces of original source</summary>
52
                <version major="1" minor="23"/>
53
                <author login="sdedic"/>
54
                <date day="29" month="8" year="2014"/>
55
                <compatibility addition="yes"/>
56
                <description>
57
                    <p>
58
                        HintContext now provides named places specified in the trigger pattern and the respective matched
59
                        TreePaths in the original text. JavaFix is able to use that information to associate same-named places
60
                        in the fix template to the original code pieces to preserve comments/formatting.
61
                    </p>
62
                </description>
63
            </api>
64
        </change>
49
        <change id="EnhancedJavaFix">
65
        <change id="EnhancedJavaFix">
50
            <api name="JavaHintsSPI"/>
66
            <api name="JavaHintsSPI"/>
51
            <summary>Added ability to specify sort text for JavaFix</summary>
67
            <summary>Added ability to specify sort text for JavaFix</summary>
(-)a/spi.java.hints/nbproject/project.properties (-1 / +1 lines)
Lines 1-7 Link Here
1
is.autoload=true
1
is.autoload=true
2
javac.source=1.7
2
javac.source=1.7
3
javac.compilerargs=-Xlint -Xlint:-serial
3
javac.compilerargs=-Xlint -Xlint:-serial
4
spec.version.base=1.22.0
4
spec.version.base=1.23.0
5
requires.nb.javac=true
5
requires.nb.javac=true
6
javadoc.arch=${basedir}/arch.xml
6
javadoc.arch=${basedir}/arch.xml
7
javadoc.apichanges=${basedir}/apichanges.xml
7
javadoc.apichanges=${basedir}/apichanges.xml
(-)a/spi.java.hints/src/org/netbeans/modules/java/hints/spiimpl/JavaFixImpl.java (-1 / +4 lines)
Lines 152-158 Link Here
152
        public abstract ChangeInfo process(JavaFix jf, WorkingCopy wc, boolean canShowUI, Map<FileObject, byte[]> resourceContent, Collection<? super RefactoringElementImplementation> fileChanges) throws Exception;
152
        public abstract ChangeInfo process(JavaFix jf, WorkingCopy wc, boolean canShowUI, Map<FileObject, byte[]> resourceContent, Collection<? super RefactoringElementImplementation> fileChanges) throws Exception;
153
        public abstract FileObject getFile(JavaFix jf);
153
        public abstract FileObject getFile(JavaFix jf);
154
        public abstract Map<String, String> getOptions(JavaFix jf);
154
        public abstract Map<String, String> getOptions(JavaFix jf);
155
        public abstract Fix rewriteFix(CompilationInfo info, String displayName, TreePath what, final String to, Map<String, TreePath> parameters, Map<String, Collection<? extends TreePath>> parametersMulti, final Map<String, String> parameterNames, Map<String, TypeMirror> constraints, Map<String, String> options, String... imports);
155
        public abstract Fix rewriteFix(CompilationInfo info, String displayName, TreePath what, final String to, Map<String, TreePath> parameters, Map<String, Collection<? extends TreePath>> parametersMulti, final Map<String, String> parameterNames, Map<String, TypeMirror> constraints, Map<String, String> options, Map<String, TreePath> anchors, String... imports);
156
        public final Fix rewriteFix(CompilationInfo info, String displayName, TreePath what, final String to, Map<String, TreePath> parameters, Map<String, Collection<? extends TreePath>> parametersMulti, final Map<String, String> parameterNames, Map<String, TypeMirror> constraints, Map<String, String> options, String... imports) {
157
            return rewriteFix(info, displayName, what, to, parameters, parametersMulti, parameterNames, constraints, options, new HashMap<String, TreePath>(), imports);
158
        }
156
        public abstract Fix createSuppressWarningsFix(CompilationInfo compilationInfo, TreePath treePath, String... keys);
159
        public abstract Fix createSuppressWarningsFix(CompilationInfo compilationInfo, TreePath treePath, String... keys);
157
        public abstract List<Fix> createSuppressWarnings(CompilationInfo compilationInfo, TreePath treePath, String... keys);
160
        public abstract List<Fix> createSuppressWarnings(CompilationInfo compilationInfo, TreePath treePath, String... keys);
158
        public abstract List<Fix> resolveDefaultFixes(HintContext ctx, Fix... provided);
161
        public abstract List<Fix> resolveDefaultFixes(HintContext ctx, Fix... provided);
(-)a/spi.java.hints/src/org/netbeans/modules/java/hints/spiimpl/SPIAccessor.java (-1 / +1 lines)
Lines 77-83 Link Here
77
        accessor = instance;
77
        accessor = instance;
78
    }
78
    }
79
79
80
    public abstract HintContext createHintContext(CompilationInfo info, HintsSettings settings, HintMetadata metadata, TreePath path, Map<String, TreePath> variables, Map<String, Collection<? extends TreePath>> multiVariables, Map<String, String> variableNames, Map<String, TypeMirror> constraints, Collection<? super MessageImpl> problems, boolean bulkMode, AtomicBoolean cancel, int caret);
80
    public abstract HintContext createHintContext(CompilationInfo info, HintsSettings settings, HintMetadata metadata, TreePath path, Map<String, TreePath> variables, Map<String, Collection<? extends TreePath>> multiVariables, Map<String, String> variableNames, Map<String, TypeMirror> constraints, Collection<? super MessageImpl> problems, boolean bulkMode, AtomicBoolean cancel, int caret, Map<String, TreePath> anchors);
81
    public abstract HintContext createHintContext(CompilationInfo info, HintsSettings settings, HintMetadata metadata, TreePath path, Map<String, TreePath> variables, Map<String, Collection<? extends TreePath>> multiVariables, Map<String, String> variableNames);
81
    public abstract HintContext createHintContext(CompilationInfo info, HintsSettings settings, HintMetadata metadata, TreePath path, Map<String, TreePath> variables, Map<String, Collection<? extends TreePath>> multiVariables, Map<String, String> variableNames);
82
    public abstract HintMetadata getHintMetadata(HintContext ctx);
82
    public abstract HintMetadata getHintMetadata(HintContext ctx);
83
    public abstract HintsSettings getHintSettings(HintContext ctx);
83
    public abstract HintsSettings getHintSettings(HintContext ctx);
(-)a/spi.java.hints/src/org/netbeans/modules/java/hints/spiimpl/Utilities.java (-12 / +70 lines)
Lines 123-128 Link Here
123
import java.util.Map.Entry;
123
import java.util.Map.Entry;
124
import java.util.Set;
124
import java.util.Set;
125
import java.util.concurrent.atomic.AtomicBoolean;
125
import java.util.concurrent.atomic.AtomicBoolean;
126
import java.util.regex.Matcher;
127
import java.util.regex.Pattern;
126
import javax.lang.model.element.AnnotationMirror;
128
import javax.lang.model.element.AnnotationMirror;
127
import javax.lang.model.element.AnnotationValue;
129
import javax.lang.model.element.AnnotationValue;
128
import javax.lang.model.element.AnnotationValueVisitor;
130
import javax.lang.model.element.AnnotationValueVisitor;
Lines 329-340 Link Here
329
        return parseAndAttribute(info, pattern, scope, null);
331
        return parseAndAttribute(info, pattern, scope, null);
330
    }
332
    }
331
333
334
    public static Tree parseAndAttributeNamed(CompilationInfo info, String pattern, Scope scope, Map<Tree, String> namedTrees) {
335
        return parseAndAttribute(info, JavaSourceAccessor.getINSTANCE().getJavacTask(info), pattern, scope, new SourcePositions[1], null, namedTrees);
336
    }
337
332
    public static Tree parseAndAttribute(CompilationInfo info, String pattern, Scope scope, Collection<Diagnostic<? extends JavaFileObject>> errors) {
338
    public static Tree parseAndAttribute(CompilationInfo info, String pattern, Scope scope, Collection<Diagnostic<? extends JavaFileObject>> errors) {
333
        return parseAndAttribute(info, JavaSourceAccessor.getINSTANCE().getJavacTask(info), pattern, scope, errors);
339
        return parseAndAttribute(info, JavaSourceAccessor.getINSTANCE().getJavacTask(info), pattern, scope, errors);
334
    }
340
    }
335
341
342
    public static Tree parseAndAttributeNamed(CompilationInfo info, String pattern, Scope scope, Collection<Diagnostic<? extends JavaFileObject>> errors, Map<Tree, String> namedTrees) {
343
        return parseAndAttribute(info, 
344
                JavaSourceAccessor.getINSTANCE().getJavacTask(info), pattern, 
345
                scope, new SourcePositions[1], errors, namedTrees);
346
    }
347
336
    public static Tree parseAndAttribute(CompilationInfo info, String pattern, Scope scope, SourcePositions[] sourcePositions, Collection<Diagnostic<? extends JavaFileObject>> errors) {
348
    public static Tree parseAndAttribute(CompilationInfo info, String pattern, Scope scope, SourcePositions[] sourcePositions, Collection<Diagnostic<? extends JavaFileObject>> errors) {
337
        return parseAndAttribute(info, JavaSourceAccessor.getINSTANCE().getJavacTask(info), pattern, scope, sourcePositions, errors);
349
        return parseAndAttribute(info, JavaSourceAccessor.getINSTANCE().getJavacTask(info), pattern, scope, sourcePositions, errors,
350
                new HashMap<Tree, String>());
338
    }
351
    }
339
352
340
    public static Tree parseAndAttribute(JavacTaskImpl jti, String pattern) {
353
    public static Tree parseAndAttribute(JavacTaskImpl jti, String pattern) {
Lines 346-371 Link Here
346
    }
359
    }
347
360
348
    public static Tree parseAndAttribute(JavacTaskImpl jti, String pattern, SourcePositions[] sourcePositions, Collection<Diagnostic<? extends JavaFileObject>> errors) {
361
    public static Tree parseAndAttribute(JavacTaskImpl jti, String pattern, SourcePositions[] sourcePositions, Collection<Diagnostic<? extends JavaFileObject>> errors) {
349
        return parseAndAttribute(null, jti, pattern, null, sourcePositions, errors);
362
        return parseAndAttribute(null, jti, pattern, null, sourcePositions, errors, new HashMap<Tree, String>());
350
    }
363
    }
351
364
352
    private static Tree parseAndAttribute(CompilationInfo info, JavacTaskImpl jti, String pattern, Scope scope, Collection<Diagnostic<? extends JavaFileObject>> errors) {
365
    private static Tree parseAndAttribute(CompilationInfo info, JavacTaskImpl jti, String pattern, Scope scope, Collection<Diagnostic<? extends JavaFileObject>> errors) {
353
        return parseAndAttribute(info, jti, pattern, scope, new SourcePositions[1], errors);
366
        return parseAndAttribute(info, jti, pattern, scope, new SourcePositions[1], errors, new HashMap<Tree, String>());
354
    }
367
    }
355
368
356
    private static Tree parseAndAttribute(CompilationInfo info, JavacTaskImpl jti, String pattern, Scope scope, SourcePositions[] sourcePositions, Collection<Diagnostic<? extends JavaFileObject>> errors) {
369
    @SuppressWarnings("StringEquality")
370
    private static Tree parseAndAttribute(CompilationInfo info, JavacTaskImpl jti, String pattern, Scope scope, final SourcePositions[] sourcePositions, Collection<Diagnostic<? extends JavaFileObject>> errors,
371
            final Map<Tree, String> namedTrees) {
357
        Context c = jti.getContext();
372
        Context c = jti.getContext();
358
        TreeFactory make = TreeFactory.instance(c);
373
        TreeFactory make = TreeFactory.instance(c);
359
        List<Diagnostic<? extends JavaFileObject>> patternTreeErrors = new LinkedList<Diagnostic<? extends JavaFileObject>>();
374
        List<Diagnostic<? extends JavaFileObject>> patternTreeErrors = new LinkedList<Diagnostic<? extends JavaFileObject>>();
360
        Tree toAttribute;
375
        Tree toAttribute;
361
        Tree patternTree = toAttribute = !isStatement(pattern) ? parseExpression(c, pattern, true, sourcePositions, patternTreeErrors) : null;
376
        final Map<Integer, String> pos = new HashMap<>();
377
        String pattern2 = removeAnchors(pattern, pos);
378
379
        Tree patternTree = toAttribute = !isStatement(pattern2) ? parseExpression(c, pattern2, true, sourcePositions, patternTreeErrors) : null;
362
        int offset = 0;
380
        int offset = 0;
363
        boolean expression = true;
381
        boolean expression = true;
364
        boolean classMember = false;
382
        boolean classMember = false;
365
383
        if (pattern2.startsWith("case ")) {//XXX: should be a lexer token
366
        if (pattern.startsWith("case ")) {//XXX: should be a lexer token
367
            List<Diagnostic<? extends JavaFileObject>> currentPatternTreeErrors = new LinkedList<Diagnostic<? extends JavaFileObject>>();
384
            List<Diagnostic<? extends JavaFileObject>> currentPatternTreeErrors = new LinkedList<Diagnostic<? extends JavaFileObject>>();
368
            Tree switchTree = parseStatement(c, "switch ($$foo) {" + pattern + "}", sourcePositions, currentPatternTreeErrors);
385
            Tree switchTree = parseStatement(c, "switch ($$foo) {" + pattern2 + "}", sourcePositions, currentPatternTreeErrors);
369
386
370
            offset = "switch ($$foo) {".length();
387
            offset = "switch ($$foo) {".length();
371
            patternTreeErrors = currentPatternTreeErrors;
388
            patternTreeErrors = currentPatternTreeErrors;
Lines 376-382 Link Here
376
        if (patternTree == null || isErrorTree(patternTree)) {
393
        if (patternTree == null || isErrorTree(patternTree)) {
377
            SourcePositions[] currentPatternTreePositions = new SourcePositions[1];
394
            SourcePositions[] currentPatternTreePositions = new SourcePositions[1];
378
            List<Diagnostic<? extends JavaFileObject>> currentPatternTreeErrors = new LinkedList<Diagnostic<? extends JavaFileObject>>();
395
            List<Diagnostic<? extends JavaFileObject>> currentPatternTreeErrors = new LinkedList<Diagnostic<? extends JavaFileObject>>();
379
            Tree currentPatternTree = parseStatement(c, "{" + pattern + "}", currentPatternTreePositions, currentPatternTreeErrors);
396
            Tree currentPatternTree = parseStatement(c, "{" + pattern2 + "}", currentPatternTreePositions, currentPatternTreeErrors);
380
397
381
            assert currentPatternTree.getKind() == Kind.BLOCK : currentPatternTree.getKind();
398
            assert currentPatternTree.getKind() == Kind.BLOCK : currentPatternTree.getKind();
382
399
Lines 402-408 Link Here
402
                //maybe a class member?
419
                //maybe a class member?
403
                SourcePositions[] classPatternTreePositions = new SourcePositions[1];
420
                SourcePositions[] classPatternTreePositions = new SourcePositions[1];
404
                List<Diagnostic<? extends JavaFileObject>> classPatternTreeErrors = new LinkedList<Diagnostic<? extends JavaFileObject>>();
421
                List<Diagnostic<? extends JavaFileObject>> classPatternTreeErrors = new LinkedList<Diagnostic<? extends JavaFileObject>>();
405
                Tree classPatternTree = parseExpression(c, "new Object() {" + pattern + "}", false, classPatternTreePositions, classPatternTreeErrors);
422
                Tree classPatternTree = parseExpression(c, "new Object() {" + pattern2 + "}", false, classPatternTreePositions, classPatternTreeErrors);
406
423
407
                if (!containsError(classPatternTree)) {
424
                if (!containsError(classPatternTree)) {
408
                    sourcePositions[0] = classPatternTreePositions[0];
425
                    sourcePositions[0] = classPatternTreePositions[0];
Lines 442-448 Link Here
442
                if (Utilities.isPureMemberSelect(patternTree, false)) {
459
                if (Utilities.isPureMemberSelect(patternTree, false)) {
443
                    SourcePositions[] varPositions = new SourcePositions[1];
460
                    SourcePositions[] varPositions = new SourcePositions[1];
444
                    List<Diagnostic<? extends JavaFileObject>> varErrors = new LinkedList<Diagnostic<? extends JavaFileObject>>();
461
                    List<Diagnostic<? extends JavaFileObject>> varErrors = new LinkedList<Diagnostic<? extends JavaFileObject>>();
445
                    Tree var = parseExpression(c, pattern + ".Class.class;", false, varPositions, varErrors);
462
                    Tree var = parseExpression(c, pattern2 + ".Class.class;", false, varPositions, varErrors);
446
463
447
                    attributeTree(jti, var, scope, varErrors);
464
                    attributeTree(jti, var, scope, varErrors);
448
465
Lines 537-545 Link Here
537
        }
554
        }
538
555
539
        sourcePositions[0] = new OffsetSourcePositions(sourcePositions[0], -offset);
556
        sourcePositions[0] = new OffsetSourcePositions(sourcePositions[0], -offset);
540
        
557
        // if the caller wants to collect named trees AND there have been some named
558
        // trees, collect their positions.
559
        if (pattern2 != pattern && !pos.isEmpty()) {
560
            TreeScanner scn = new TreeScanner() {
561
562
                @Override
563
                public Object scan(Tree node, Object p) {
564
                    if (node != null) {
565
                        int x = (int)sourcePositions[0].getStartPosition(null, node);
566
                        if (x > 0) {
567
                            String n = pos.get(x);
568
                            if (n != null) {
569
                                namedTrees.put(node, n);
570
                            }
571
                        }
572
                    }
573
                    return super.scan(node, p); //To change body of generated methods, choose Tools | Templates.
574
                }
575
                
576
            };
577
            
578
            scn.scan(patternTree, null);
579
        }
541
        return patternTree;
580
        return patternTree;
542
    }
581
    }
582
    
583
    private static final Pattern REGEXP_ANCHOR = Pattern.compile("#\\{(\\p{Alnum}+)\\}#"); // NOI18N
584
    
585
    private static String removeAnchors(String pattern, Map<Integer, String> positions) {
586
        Matcher m = REGEXP_ANCHOR.matcher(pattern);
587
        StringBuilder sb = null;
588
        int last = 0;
589
        while (m.find()) {
590
            int s = m.start();
591
            int e = m.end();
592
            if (sb == null) {
593
                sb = new StringBuilder(pattern.length());
594
            }
595
            sb.append(pattern.subSequence(last, s));
596
            positions.put(sb.length(), m.group(1));
597
            last = e;
598
        }
599
        return sb == null ? pattern : sb.append(pattern.substring(last)).toString();
600
    }
543
601
544
    static boolean isError(Element el) {
602
    static boolean isError(Element el) {
545
        return (el == null || (el.getKind() == ElementKind.CLASS) && isError(((TypeElement) el).asType()));
603
        return (el == null || (el.getKind() == ElementKind.CLASS) && isError(((TypeElement) el).asType()));
(-)a/spi.java.hints/src/org/netbeans/modules/java/hints/spiimpl/hints/HintsInvoker.java (-3 / +2 lines)
Lines 542-548 Link Here
542
542
543
                    constraints.put(e.getKey(), designedType);
543
                    constraints.put(e.getKey(), designedType);
544
                }
544
                }
545
546
                Pattern pattern = PatternCompiler.compile(info, occ.getKey(), constraints, d.getImports());
545
                Pattern pattern = PatternCompiler.compile(info, occ.getKey(), constraints, d.getImports());
547
546
548
                for (TreePath candidate : occ.getValue()) {
547
                for (TreePath candidate : occ.getValue()) {
Lines 559-565 Link Here
559
558
560
                    for (HintDescription hd : patternHints.get(d)) {
559
                    for (HintDescription hd : patternHints.get(d)) {
561
                        HintMetadata hm = hd.getMetadata();
560
                        HintMetadata hm = hd.getMetadata();
562
                        HintContext c = SPIAccessor.getINSTANCE().createHintContext(info, settings, hm, candidate, verifiedVariables.getVariables(), verifiedVariables.getMultiVariables(), verifiedVariables.getVariables2Names(), constraints, problems, bulkMode, cancel, caret);
561
                        HintContext c = SPIAccessor.getINSTANCE().createHintContext(info, settings, hm, candidate, verifiedVariables.getVariables(), verifiedVariables.getMultiVariables(), verifiedVariables.getVariables2Names(), constraints, problems, bulkMode, cancel, caret, verifiedVariables.getAnchors());
563
562
564
                        if (!Collections.disjoint(suppressedWarnings, hm.suppressWarnings))
563
                        if (!Collections.disjoint(suppressedWarnings, hm.suppressWarnings))
565
                            continue;
564
                            continue;
Lines 653-659 Link Here
653
                        }
652
                        }
654
                    }
653
                    }
655
654
656
                    HintContext c = SPIAccessor.getINSTANCE().createHintContext(info, settings, hm, path, Collections.<String, TreePath>emptyMap(), Collections.<String, Collection<? extends TreePath>>emptyMap(), Collections.<String, String>emptyMap(), Collections.<String, TypeMirror>emptyMap(), new ArrayList<MessageImpl>(), bulkMode, cancel, caret);
655
                    HintContext c = SPIAccessor.getINSTANCE().createHintContext(info, settings, hm, path, Collections.<String, TreePath>emptyMap(), Collections.<String, Collection<? extends TreePath>>emptyMap(), Collections.<String, String>emptyMap(), Collections.<String, TypeMirror>emptyMap(), new ArrayList<MessageImpl>(), bulkMode, cancel, caret, null);
657
                    Collection<? extends ErrorDescription> errors = runHint(hd, c);
656
                    Collection<? extends ErrorDescription> errors = runHint(hd, c);
658
657
659
                    if (errors != null) {
658
                    if (errors != null) {
(-)a/spi.java.hints/src/org/netbeans/modules/java/hints/spiimpl/pm/PatternCompiler.java (-2 / +4 lines)
Lines 45-50 Link Here
45
import com.sun.source.tree.Scope;
45
import com.sun.source.tree.Scope;
46
import com.sun.source.tree.Tree;
46
import com.sun.source.tree.Tree;
47
import com.sun.source.util.TreePath;
47
import com.sun.source.util.TreePath;
48
import java.util.HashMap;
48
import java.util.Map;
49
import java.util.Map;
49
import javax.lang.model.type.TypeMirror;
50
import javax.lang.model.type.TypeMirror;
50
import org.netbeans.api.java.source.CompilationInfo;
51
import org.netbeans.api.java.source.CompilationInfo;
Lines 64-72 Link Here
64
            return null; //TODO: can happen?
65
            return null; //TODO: can happen?
65
        }
66
        }
66
67
67
        Tree patternTree = Utilities.parseAndAttribute(info, pattern, scope);
68
        Map<Tree, String> namedTrees = new HashMap<>();
69
        Tree patternTree = Utilities.parseAndAttributeNamed(info, pattern, scope, namedTrees);
68
70
69
        return Pattern.createPatternWithFreeVariables(new TreePath(new TreePath(info.getCompilationUnit()), patternTree), constraints);
71
        return Pattern.createPatternWithFreeVariables(new TreePath(new TreePath(info.getCompilationUnit()), patternTree), constraints).setAnchors(namedTrees);
70
    }
72
    }
71
73
72
}
74
}
(-)a/spi.java.hints/src/org/netbeans/spi/java/hints/HintContext.java (-2 / +31 lines)
Lines 51-56 Link Here
51
import java.util.concurrent.atomic.AtomicBoolean;
51
import java.util.concurrent.atomic.AtomicBoolean;
52
import java.util.prefs.Preferences;
52
import java.util.prefs.Preferences;
53
import javax.lang.model.type.TypeMirror;
53
import javax.lang.model.type.TypeMirror;
54
import org.netbeans.api.annotations.common.CheckForNull;
55
import org.netbeans.api.annotations.common.NonNull;
54
import org.netbeans.api.java.source.CompilationInfo;
56
import org.netbeans.api.java.source.CompilationInfo;
55
import org.netbeans.modules.java.hints.providers.spi.HintMetadata;
57
import org.netbeans.modules.java.hints.providers.spi.HintMetadata;
56
import org.netbeans.modules.java.hints.spiimpl.MessageImpl;
58
import org.netbeans.modules.java.hints.spiimpl.MessageImpl;
Lines 79-84 Link Here
79
    private final boolean bulkMode;
81
    private final boolean bulkMode;
80
    private final AtomicBoolean cancel;
82
    private final AtomicBoolean cancel;
81
    private final int caret;
83
    private final int caret;
84
    private Map<String, TreePath> anchors;
82
85
83
    private HintContext(CompilationInfo info, HintsSettings settings, HintMetadata metadata, TreePath path, Map<String, TreePath> variables, Map<String, Collection<? extends TreePath>> multiVariables, Map<String, String> variableNames, Map<String, TypeMirror> constraints, Collection<? super MessageImpl> problems, boolean bulkMode, AtomicBoolean cancel, int caret) {
86
    private HintContext(CompilationInfo info, HintsSettings settings, HintMetadata metadata, TreePath path, Map<String, TreePath> variables, Map<String, Collection<? extends TreePath>> multiVariables, Map<String, String> variableNames, Map<String, TypeMirror> constraints, Collection<? super MessageImpl> problems, boolean bulkMode, AtomicBoolean cancel, int caret) {
84
        this.info = info;
87
        this.info = info;
Lines 100-105 Link Here
100
        this.cancel = cancel;
103
        this.cancel = cancel;
101
        this.caret = caret;
104
        this.caret = caret;
102
    }
105
    }
106
    
107
    HintContext setAnchors(Map<String, TreePath> anchors) {
108
        this.anchors = anchors;
109
        return this;
110
    }
103
111
104
    public CompilationInfo getInfo() {
112
    public CompilationInfo getInfo() {
105
        return info;
113
        return info;
Lines 178-187 Link Here
178
        WARNING, ERROR;
186
        WARNING, ERROR;
179
    }
187
    }
180
    
188
    
189
    /**
190
     * Provides a path to tree that matched a named position in the pattern.
191
     * @param an anchor name
192
     * @return TreePath that matched the node at the given position, or {@code null}
193
     * @since 1.23
194
     */
195
    public final @CheckForNull TreePath getAnchoredPath(String an) {
196
        return anchors == null ? null : anchors.get(an);
197
    }
198
    
199
    /**
200
     * Provides all anchors and their matched nodes.
201
     * Only found anchors are present; those not matched have no entry in the returned
202
     * Map
203
     * @return anchor names associated with path to the matched Tree node
204
     * @since 1.23
205
     */
206
    public final @NonNull Map<String, TreePath> getAnchors() {
207
        return anchors == null ? Collections.<String, TreePath>emptyMap() : anchors;
208
    }
209
    
181
    static {
210
    static {
182
        SPIAccessor.setINSTANCE(new SPIAccessor() {
211
        SPIAccessor.setINSTANCE(new SPIAccessor() {
183
            @Override public HintContext createHintContext(CompilationInfo info, HintsSettings settings, HintMetadata metadata, TreePath path, Map<String, TreePath> variables, Map<String, Collection<? extends TreePath>> multiVariables, Map<String, String> variableNames, Map<String, TypeMirror> constraints, Collection<? super MessageImpl> problems, boolean bulkMode, AtomicBoolean cancel, int caret) {
212
            @Override public HintContext createHintContext(CompilationInfo info, HintsSettings settings, HintMetadata metadata, TreePath path, Map<String, TreePath> variables, Map<String, Collection<? extends TreePath>> multiVariables, Map<String, String> variableNames, Map<String, TypeMirror> constraints, Collection<? super MessageImpl> problems, boolean bulkMode, AtomicBoolean cancel, int caret, Map<String, TreePath> anchors) {
184
                return new HintContext(info, settings, metadata, path, variables, multiVariables, variableNames, constraints, problems, bulkMode, cancel, caret);
213
                return new HintContext(info, settings, metadata, path, variables, multiVariables, variableNames, constraints, problems, bulkMode, cancel, caret).setAnchors(anchors);
185
            }
214
            }
186
            @Override public HintContext createHintContext(CompilationInfo info, HintsSettings settings, HintMetadata metadata, TreePath path, Map<String, TreePath> variables, Map<String, Collection<? extends TreePath>> multiVariables, Map<String, String> variableNames) {
215
            @Override public HintContext createHintContext(CompilationInfo info, HintsSettings settings, HintMetadata metadata, TreePath path, Map<String, TreePath> variables, Map<String, Collection<? extends TreePath>> multiVariables, Map<String, String> variableNames) {
187
                return new HintContext(info, settings, metadata, path, variables, multiVariables, variableNames, Collections.<String, TypeMirror>emptyMap(), new LinkedList<MessageImpl>(), false, new AtomicBoolean(), -1);
216
                return new HintContext(info, settings, metadata, path, variables, multiVariables, variableNames, Collections.<String, TypeMirror>emptyMap(), new LinkedList<MessageImpl>(), false, new AtomicBoolean(), -1);
(-)a/spi.java.hints/src/org/netbeans/spi/java/hints/JavaFix.java (-2 / +5 lines)
Lines 42-47 Link Here
42
42
43
package org.netbeans.spi.java.hints;
43
package org.netbeans.spi.java.hints;
44
44
45
import com.sun.source.tree.Tree;
45
import com.sun.source.util.TreePath;
46
import com.sun.source.util.TreePath;
46
import java.io.ByteArrayInputStream;
47
import java.io.ByteArrayInputStream;
47
import java.io.ByteArrayOutputStream;
48
import java.io.ByteArrayOutputStream;
Lines 205-212 Link Here
205
            }
206
            }
206
207
207
            @Override
208
            @Override
208
            public Fix rewriteFix(CompilationInfo info, String displayName, TreePath what, String to, Map<String, TreePath> parameters, Map<String, Collection<? extends TreePath>> parametersMulti, Map<String, String> parameterNames, Map<String, TypeMirror> constraints, Map<String, String> options, String... imports) {
209
            public Fix rewriteFix(CompilationInfo info, String displayName, TreePath what, String to, Map<String, TreePath> parameters, Map<String, Collection<? extends TreePath>> parametersMulti, Map<String, String> parameterNames, Map<String, TypeMirror> constraints, Map<String, String> options, 
209
                return JavaFixUtilities.rewriteFix(info, displayName, what, to, parameters, parametersMulti, parameterNames, constraints, options, imports);
210
                    Map<String, TreePath> anchors, String... imports) {
211
                return JavaFixUtilities.rewriteFix(info, displayName, what, to, parameters, parametersMulti, parameterNames, constraints, options, 
212
                        anchors, imports);
210
            }
213
            }
211
214
212
            @Override
215
            @Override
(-)a/spi.java.hints/src/org/netbeans/spi/java/hints/JavaFixUtilities.java (-6 / +73 lines)
Lines 159-168 Link Here
159
     * @return an editor fix that performs the required transformation
159
     * @return an editor fix that performs the required transformation
160
     */
160
     */
161
    public static Fix rewriteFix(HintContext ctx, String displayName, TreePath what, final String to) {
161
    public static Fix rewriteFix(HintContext ctx, String displayName, TreePath what, final String to) {
162
        return rewriteFix(ctx.getInfo(), displayName, what, to, ctx.getVariables(), ctx.getMultiVariables(), ctx.getVariableNames(), ctx.getConstraints(), Collections.<String, String>emptyMap());
162
        return rewriteFix(
163
                ctx.getInfo(), displayName, what, to, ctx.getVariables(), 
164
                ctx.getMultiVariables(), ctx.getVariableNames(), ctx.getConstraints(), 
165
                Collections.<String, String>emptyMap(), ctx.getAnchors());
166
    }
167
    
168
    /**Prepare a fix that will replace the given tree node ({@code what}) with the
169
     * given code. Any variables in the {@code to} pattern will be replaced with their
170
     * values from {@link HintContext#getVariables() }, {@link HintContext#getMultiVariables() }
171
     * and {@link HintContext#getVariableNames() }.
172
     * <p/>
173
     * Unlike the other method, the {@code to} pattern may contain named tags, which will associate
174
     * former code in the matched original to the code newly generated from the {@code to} pattern.
175
     * Such generated code will transfer comments, javadoc and possibly even formatting from the original
176
     * trees. The syntax for a named tag is {@code #{tagName}#} and will be associated to a Tree which 
177
     * starts exactly on the position immediately following the tag spec (so don't put any whitespace between }# and 
178
     * java code).
179
     * @param ctx basic context for which the fix should be created
180
     * @param displayName the display name of the fix
181
     * @param what the tree node that should be replaced
182
     * @param to the new code that should replaced the {@code what} tree node
183
     * @param namedTrees trees in the original soruce that matched named tags.
184
     * @return an editor fix that performs the required transformation
185
     * @since 1.23
186
     */
187
    public static Fix rewriteFix(HintContext ctx, String displayName, TreePath what, final String to, Map<String,TreePath> namedTrees) {
188
        return rewriteFix(
189
                ctx.getInfo(), displayName, what, to, ctx.getVariables(), 
190
                ctx.getMultiVariables(), ctx.getVariableNames(), ctx.getConstraints(), 
191
                Collections.<String, String>emptyMap(), namedTrees);
163
    }
192
    }
164
193
165
    static Fix rewriteFix(CompilationInfo info, String displayName, TreePath what, final String to, Map<String, TreePath> parameters, Map<String, Collection<? extends TreePath>> parametersMulti, final Map<String, String> parameterNames, Map<String, TypeMirror> constraints, Map<String, String> options, String... imports) {
194
    static Fix rewriteFix(CompilationInfo info, String displayName, TreePath what, final String to, Map<String, TreePath> parameters, Map<String, Collection<? extends TreePath>> parametersMulti, final Map<String, String> parameterNames, Map<String, TypeMirror> constraints, Map<String, String> options, 
195
            Map<String, TreePath> namedTrees, String... imports) {
166
        final Map<String, TreePathHandle> params = new HashMap<String, TreePathHandle>();
196
        final Map<String, TreePathHandle> params = new HashMap<String, TreePathHandle>();
167
        final Map<String, Object> extraParamsData = new HashMap<String, Object>();
197
        final Map<String, Object> extraParamsData = new HashMap<String, Object>();
168
198
Lines 198-205 Link Here
198
        if (displayName == null) {
228
        if (displayName == null) {
199
            displayName = defaultFixDisplayName(info, parameters, to);
229
            displayName = defaultFixDisplayName(info, parameters, to);
200
        }
230
        }
231
        
232
        Map<String, TreePathHandle> namedHandles;
233
        if (namedTrees != null && !namedTrees.isEmpty()) {
234
            namedHandles = new HashMap<>(namedTrees.size());
235
            for (Map.Entry<String, TreePath> e : namedTrees.entrySet()) {
236
                namedHandles.put(e.getKey(), TreePathHandle.create(e.getValue(), info));
237
            }
238
        } else {
239
            namedHandles = null;
240
        }
201
241
202
        return new JavaFixRealImpl(info, what, options, displayName, to, params, extraParamsData, paramsMulti, parameterNames, constraintsHandles, Arrays.asList(imports)).toEditorFix();
242
        return new JavaFixRealImpl(info, what, options, displayName, to, params, extraParamsData, paramsMulti, parameterNames, constraintsHandles, Arrays.asList(imports),
243
        namedHandles).toEditorFix();
203
    }
244
    }
204
245
205
    /**Creates a fix that removes the given code corresponding to the given tree
246
    /**Creates a fix that removes the given code corresponding to the given tree
Lines 378-385 Link Here
378
        private final Map<String, TypeMirrorHandle<?>> constraintsHandles;
419
        private final Map<String, TypeMirrorHandle<?>> constraintsHandles;
379
        private final Iterable<? extends String> imports;
420
        private final Iterable<? extends String> imports;
380
        private final String to;
421
        private final String to;
422
        private final Map<String, TreePathHandle> namedTrees;
381
423
382
        public JavaFixRealImpl(CompilationInfo info, TreePath what, Map<String, String> options, String displayName, String to, Map<String, TreePathHandle> params, Map<String, Object> extraParamsData, Map<String, Collection<TreePathHandle>> paramsMulti, final Map<String, String> parameterNames, Map<String, TypeMirrorHandle<?>> constraintsHandles, Iterable<? extends String> imports) {
424
        public JavaFixRealImpl(CompilationInfo info, TreePath what, Map<String, String> options, String displayName, String to, Map<String, TreePathHandle> params, Map<String, Object> extraParamsData, Map<String, Collection<TreePathHandle>> paramsMulti, final Map<String, String> parameterNames, Map<String, TypeMirrorHandle<?>> constraintsHandles, Iterable<? extends String> imports,
425
                Map<String, TreePathHandle> namedTrees) {
383
            super(info, what, options);
426
            super(info, what, options);
384
427
385
            this.displayName = displayName;
428
            this.displayName = displayName;
Lines 390-395 Link Here
390
            this.parameterNames = parameterNames;
433
            this.parameterNames = parameterNames;
391
            this.constraintsHandles = constraintsHandles;
434
            this.constraintsHandles = constraintsHandles;
392
            this.imports = imports;
435
            this.imports = imports;
436
            this.namedTrees = namedTrees;
393
        }
437
        }
394
438
395
        @Override
439
        @Override
Lines 439-450 Link Here
439
            for (Entry<String, TypeMirrorHandle<?>> c : constraintsHandles.entrySet()) {
483
            for (Entry<String, TypeMirrorHandle<?>> c : constraintsHandles.entrySet()) {
440
                constraints.put(c.getKey(), c.getValue().resolve(wc));
484
                constraints.put(c.getKey(), c.getValue().resolve(wc));
441
            }
485
            }
486
            
487
            Map<String, TreePath> origNamedTrees;
488
            
489
            if (namedTrees != null) {
490
                origNamedTrees = new HashMap<>(namedTrees.size());
491
                for (Map.Entry<String, TreePathHandle> e : namedTrees.entrySet()) {
492
                    TreePath x = e.getValue().resolve(wc);
493
                    if (x == null) {
494
                        // TODO - shouldn't we bail out ? A matched subtree no longer exists
495
                        continue;
496
                    }
497
                    origNamedTrees.put(e.getKey(), x);
498
                }
499
            } else {
500
                origNamedTrees = Collections.emptyMap();
501
            }
442
502
443
            Scope scope = Utilities.constructScope(wc, constraints, imports);
503
            Scope scope = Utilities.constructScope(wc, constraints, imports);
444
504
445
            assert scope != null;
505
            assert scope != null;
446
506
            Map<Tree, String> namedTrees = new HashMap<>();
447
            Tree parsed = Utilities.parseAndAttribute(wc, to, scope);
507
            Tree parsed = Utilities.parseAndAttributeNamed(wc, to, scope, namedTrees);
448
            
508
            
449
            if (parsed.getKind() == Kind.EXPRESSION_STATEMENT && ExpressionTree.class.isAssignableFrom(tp.getLeaf().getKind().asInterface())) {
509
            if (parsed.getKind() == Kind.EXPRESSION_STATEMENT && ExpressionTree.class.isAssignableFrom(tp.getLeaf().getKind().asInterface())) {
450
                parsed = ((ExpressionStatementTree) parsed).getExpression();
510
                parsed = ((ExpressionStatementTree) parsed).getExpression();
Lines 564-569 Link Here
564
//                gen.copyComments(from, to, false);
624
//                gen.copyComments(from, to, false);
565
                wc.rewrite(from, to);
625
                wc.rewrite(from, to);
566
            }
626
            }
627
            for (Map.Entry<Tree, String> ne : namedTrees.entrySet()) {
628
                TreePath origP = origNamedTrees.get(ne.getValue());
629
                if (origP == null) {
630
                    continue;
631
                }
632
                wc.getTreeMaker().asReplacementOf(ne.getKey(), origP.getLeaf(), true);
633
            }
567
        }
634
        }
568
    }
635
    }
569
    
636
    
(-)a/spi.java.hints/test/unit/src/org/netbeans/spi/java/hints/JavaFixUtilitiesTest.java (-1 / +5 lines)
Lines 251-257 Link Here
251
        VariableTree variable = (VariableTree) clazz.getMembers().get(1);
251
        VariableTree variable = (VariableTree) clazz.getMembers().get(1);
252
        ExpressionTree init = variable.getInitializer();
252
        ExpressionTree init = variable.getInitializer();
253
        TreePath tp = new TreePath(new TreePath(new TreePath(new TreePath(info.getCompilationUnit()), clazz), variable), init);
253
        TreePath tp = new TreePath(new TreePath(new TreePath(new TreePath(info.getCompilationUnit()), clazz), variable), init);
254
        Fix fix = JavaFixUtilities.rewriteFix(info, "A", tp, orig, Collections.<String, TreePath>emptyMap(), Collections.<String, Collection<? extends TreePath>>emptyMap(), Collections.<String, String>emptyMap(), Collections.<String, TypeMirror>emptyMap(), Collections.<String, String>emptyMap());
254
        Fix fix = JavaFixUtilities.rewriteFix(info, "A", tp, orig, Collections.<String, TreePath>emptyMap(), 
255
                Collections.<String, Collection<? extends TreePath>>emptyMap(), 
256
                Collections.<String, String>emptyMap(), 
257
                Collections.<String, TypeMirror>emptyMap(), 
258
                Collections.<String, String>emptyMap(), Collections.<String, TreePath>emptyMap());
255
        fix.implement();
259
        fix.implement();
256
260
257
        String golden = replace(nue);
261
        String golden = replace(nue);

Return to bug 246587