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 245348
Collapse All | Expand All

(-)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="nodeReplacemenHints">
112
            <api name="general"/>
113
            <summary>Added API to preserve comments and whitespaces in transformations</summary>
114
            <version major="0" minor="131"/>
115
            <date day="2" month="7" year="2014"/>
116
            <author login="sdedic"/>
117
            <compatibility addition="yes" binary="compatible" deletion="no" deprecation="no" modification="no" semantic="compatible" source="compatible"/>
118
            <description>
119
                If a transformation moves a Tree node elsewhere, or produces a replacement of it, it can now mark the new tree as a 'replacement' for the old one,
120
                which causes whitespaces (most notably comments) to attach to the replacement node.
121
            </description>
122
        </change>
111
        <change id="findLabelSpan">
123
        <change id="findLabelSpan">
112
             <api name="general"/>
124
             <api name="general"/>
113
             <summary>Added utility methods to find span of a Label's name in the source.</summary>
125
             <summary>Added utility methods to find span of a Label's name in the source.</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.136.0
49
spec.version.base=0.137.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/GeneratorUtilities.java (-4 / +4 lines)
Lines 961-971 Link Here
961
        CommentSetImpl t = handler.getComments(target);
961
        CommentSetImpl t = handler.getComments(target);
962
962
963
        if (preceding) {
963
        if (preceding) {
964
            t.addComments(RelativePosition.PRECEDING, s.getComments(RelativePosition.PRECEDING));
964
            t.addComments(RelativePosition.PRECEDING, copy.useComments(s.getComments(RelativePosition.PRECEDING)));
965
            t.addComments(RelativePosition.INNER, s.getComments(RelativePosition.INNER));
965
            t.addComments(RelativePosition.INNER, copy.useComments(s.getComments(RelativePosition.INNER)));
966
        } else {
966
        } else {
967
            t.addComments(RelativePosition.INLINE, s.getComments(RelativePosition.INLINE));
967
            t.addComments(RelativePosition.INLINE, copy.useComments(s.getComments(RelativePosition.INLINE)));
968
            t.addComments(RelativePosition.TRAILING, s.getComments(RelativePosition.TRAILING));
968
            t.addComments(RelativePosition.TRAILING, copy.useComments(s.getComments(RelativePosition.TRAILING)));
969
        }
969
        }
970
    }
970
    }
971
971
(-)a/java.source/src/org/netbeans/api/java/source/TreeMaker.java (-3 / +86 lines)
Lines 1500-1506 Link Here
1500
     * @return  class tree with modified implements.
1500
     * @return  class tree with modified implements.
1501
     */
1501
     */
1502
    public ClassTree removeClassImplementsClause(ClassTree clazz, Tree implementsClause) {
1502
    public ClassTree removeClassImplementsClause(ClassTree clazz, Tree implementsClause) {
1503
        return delegate.removeClassImplementsClause(clazz, implementsClause);
1503
        return delegate.removeClassImplementsClause(clazz, asRemoved(implementsClause));
1504
    }
1504
    }
1505
    
1505
    
1506
    /**
1506
    /**
Lines 2677-2683 Link Here
2677
     */
2677
     */
2678
    public <N extends Tree> N setLabel(final N node, final CharSequence aLabel)
2678
    public <N extends Tree> N setLabel(final N node, final CharSequence aLabel)
2679
            throws IllegalArgumentException {
2679
            throws IllegalArgumentException {
2680
        N result = setLabelImpl(node, aLabel);
2680
        N result = asReplacementOf(setLabelImpl(node, aLabel), node, true);
2681
2681
2682
        GeneratorUtilities gu = GeneratorUtilities.get(copy);
2682
        GeneratorUtilities gu = GeneratorUtilities.get(copy);
2683
2683
Lines 3041-3047 Link Here
3041
            throw new IllegalArgumentException("Index out of bounds, index=" + index + ", length=" + comments.size());
3041
            throw new IllegalArgumentException("Index out of bounds, index=" + index + ", length=" + comments.size());
3042
        }
3042
        }
3043
    }
3043
    }
3044
3044
    
3045
    /**
3046
     * Marks the tree as new. This information serves as a hint to code generator, which may choose appropriate
3047
     * formatting for the tree, or suppress comments which might get associated to the tree.
3048
     * 
3049
     * @param <T>
3050
     * @param tree Tree instance
3051
     * @return the tree itself
3052
     */
3053
    public <T extends Tree> T asNew(T tree) {
3054
        if (handler == null) {
3055
            throw new IllegalStateException("Cannot work with comments outside runModificationTask.");
3056
        }
3057
        if (tree == null) {
3058
            throw new NullPointerException("tree");
3059
        }
3060
        copy.associateTree(tree, null, true);
3061
        return tree;
3062
    }
3063
    
3064
    /**
3065
     * Marks a tree as a replacement of some old one. The hint may cause surrounding whitespace to be 
3066
     * carried over to the new tree and comments to be attached to the same (similar) positions
3067
     * as in the old tree. Prevous hints are removed.
3068
     * 
3069
     * @param <T>
3070
     * @param treeNew the new tree instance
3071
     * @param treeOld the old tree instance
3072
     * @return the new tree instance unchanged
3073
     * @see #asReplacementOf(com.sun.source.tree.Tree, com.sun.source.tree.Tree, boolean) 
3074
     * @since 0.137
3075
     */
3076
    public <T extends Tree> T asReplacementOf(T treeNew, Tree treeOld) {
3077
        return asReplacementOf(treeNew, treeOld, false);
3078
    }
3079
    
3080
    /**
3081
     * Marks a tree as a replacement of some old one. The hint may cause surrounding whitespace to be 
3082
     * carried over to the new tree and comments to be attached to the same (similar) positions
3083
     * as in the old tree. 
3084
     * <p/>
3085
     * If 'defaultOnly' is true, the hint is only added if no previous hint exists. You generally want
3086
     * to force the hint, in code manipulation operations. Bulk tree transformers should preserve existing
3087
     * hints - the {@link TreeUtilities#translate} preserves existing relationships.
3088
     * 
3089
     * @param <T>
3090
     * @param treeNew
3091
     * @param treeOld
3092
     * @param defaultOnly
3093
     * @return a tree that corresponds to treeNew.
3094
     * @since 0.137
3095
     */
3096
    public <T extends Tree> T asReplacementOf(T treeNew, Tree treeOld, boolean defaultOnly) {
3097
        if (handler == null) {
3098
            throw new IllegalStateException("Cannot work with comments outside runModificationTask.");
3099
        }
3100
        if (treeOld == null) {
3101
            throw new NullPointerException("treeOld");
3102
        }
3103
        if (treeNew != null) {
3104
            copy.associateTree(treeNew, treeOld, !defaultOnly);
3105
        }
3106
        return treeNew;
3107
    }
3108
    
3109
    /**
3110
     * Marks the tree as removed. The mark may affect whitespace and comment handling. For example, if the tree was 
3111
     * rewritten by a different construct, and there's no direct replacement for the tree, the tree node should be marked
3112
     * as removed, so that comments do not end up with the replacement. The caller is then responsible for preserving
3113
     * comments or other whitespace in the removed node.
3114
     * 
3115
     * @param <T>
3116
     * @param tree tree that has been removed.
3117
     * @return the tree.
3118
     * @since 0.137
3119
     */
3120
    public <T extends Tree> T asRemoved(T tree) {
3121
        if (tree == null) {
3122
            throw new NullPointerException("tree");
3123
        }
3124
        copy.associateTree(tree, null, true);
3125
        return tree;
3126
    }
3127
    
3045
    /**
3128
    /**
3046
     * Creates a new BlockTree for provided <tt>bodyText</tt>.
3129
     * Creates a new BlockTree for provided <tt>bodyText</tt>.
3047
     * 
3130
     * 
(-)a/java.source/src/org/netbeans/api/java/source/WorkingCopy.java (-16 / +246 lines)
Lines 48-59 Link Here
48
import com.sun.source.doctree.DocTree;
48
import com.sun.source.doctree.DocTree;
49
import com.sun.source.tree.ClassTree;
49
import com.sun.source.tree.ClassTree;
50
import com.sun.source.tree.CompilationUnitTree;
50
import com.sun.source.tree.CompilationUnitTree;
51
import com.sun.source.tree.EnhancedForLoopTree;
51
import com.sun.source.tree.ExpressionStatementTree;
52
import com.sun.source.tree.ExpressionStatementTree;
52
import com.sun.source.tree.ExpressionTree;
53
import com.sun.source.tree.ExpressionTree;
54
import com.sun.source.tree.ForLoopTree;
53
import com.sun.source.tree.IdentifierTree;
55
import com.sun.source.tree.IdentifierTree;
54
import com.sun.source.tree.MethodInvocationTree;
56
import com.sun.source.tree.MethodInvocationTree;
55
import com.sun.source.tree.Tree;
57
import com.sun.source.tree.Tree;
56
import com.sun.source.tree.Tree.Kind;
58
import com.sun.source.tree.Tree.Kind;
59
import com.sun.source.tree.TreeVisitor;
60
import com.sun.source.tree.TryTree;
61
import com.sun.source.util.DocTrees;
57
import com.sun.source.util.TreePath;
62
import com.sun.source.util.TreePath;
58
import com.sun.source.util.TreePathScanner;
63
import com.sun.source.util.TreePathScanner;
59
import com.sun.source.util.TreeScanner;
64
import com.sun.source.util.TreeScanner;
Lines 79-106 Link Here
79
import javax.lang.model.element.ElementKind;
84
import javax.lang.model.element.ElementKind;
80
import javax.swing.text.BadLocationException;
85
import javax.swing.text.BadLocationException;
81
import javax.tools.JavaFileObject;
86
import javax.tools.JavaFileObject;
82
83
import com.sun.source.tree.EnhancedForLoopTree;
84
import com.sun.source.tree.ForLoopTree;
85
import com.sun.source.tree.TreeVisitor;
86
import com.sun.source.tree.TryTree;
87
import com.sun.source.util.DocTrees;
88
import org.netbeans.api.annotations.common.NonNull;
87
import org.netbeans.api.annotations.common.NonNull;
89
import org.netbeans.api.annotations.common.NullAllowed;
88
import org.netbeans.api.annotations.common.NullAllowed;
90
import org.netbeans.api.annotations.common.NullUnknown;
89
import org.netbeans.api.annotations.common.NullUnknown;
91
import org.netbeans.api.java.lexer.JavaTokenId;
90
import org.netbeans.api.java.lexer.JavaTokenId;
92
import org.netbeans.modules.java.source.parsing.JavacParserResult;
91
import org.netbeans.api.java.source.ModificationResult.CreateChange;
93
import org.netbeans.modules.parsing.spi.Parser;
92
import org.netbeans.api.java.source.ModificationResult.Difference;
94
import org.openide.util.Exceptions;
95
import org.openide.util.Parameters;
96
import static org.netbeans.api.java.source.ModificationResult.*;
97
import org.netbeans.api.lexer.TokenSequence;
93
import org.netbeans.api.lexer.TokenSequence;
98
import org.netbeans.modules.java.source.save.CasualDiff.Diff;
94
import org.netbeans.modules.java.source.builder.CommentHandlerService;
95
import org.netbeans.modules.java.source.builder.CommentSetImpl;
99
import org.netbeans.modules.java.source.builder.TreeFactory;
96
import org.netbeans.modules.java.source.builder.TreeFactory;
100
import org.netbeans.modules.java.source.parsing.CompilationInfoImpl;
97
import org.netbeans.modules.java.source.parsing.CompilationInfoImpl;
101
import org.netbeans.modules.java.source.parsing.FileObjects;
98
import org.netbeans.modules.java.source.parsing.FileObjects;
99
import org.netbeans.modules.java.source.parsing.JavacParserResult;
102
import org.netbeans.modules.java.source.pretty.ImportAnalysis2;
100
import org.netbeans.modules.java.source.pretty.ImportAnalysis2;
101
import org.netbeans.modules.java.source.query.CommentSet.RelativePosition;
103
import org.netbeans.modules.java.source.save.CasualDiff;
102
import org.netbeans.modules.java.source.save.CasualDiff;
103
import org.netbeans.modules.java.source.save.CasualDiff.Diff;
104
import org.netbeans.modules.java.source.save.DiffContext;
104
import org.netbeans.modules.java.source.save.DiffContext;
105
import org.netbeans.modules.java.source.save.DiffUtilities;
105
import org.netbeans.modules.java.source.save.DiffUtilities;
106
import org.netbeans.modules.java.source.save.ElementOverlay;
106
import org.netbeans.modules.java.source.save.ElementOverlay;
Lines 109-122 Link Here
109
import org.netbeans.modules.java.source.transform.ImmutableDocTreeTranslator;
109
import org.netbeans.modules.java.source.transform.ImmutableDocTreeTranslator;
110
import org.netbeans.modules.java.source.transform.ImmutableTreeTranslator;
110
import org.netbeans.modules.java.source.transform.ImmutableTreeTranslator;
111
import org.netbeans.modules.java.source.transform.TreeDuplicator;
111
import org.netbeans.modules.java.source.transform.TreeDuplicator;
112
import org.netbeans.modules.parsing.spi.Parser;
112
import org.openide.filesystems.FileObject;
113
import org.openide.filesystems.FileObject;
113
import org.openide.filesystems.FileUtil;
114
import org.openide.filesystems.FileUtil;
114
import org.openide.loaders.DataFolder;
115
import org.openide.loaders.DataFolder;
115
import org.openide.loaders.DataObject;
116
import org.openide.loaders.DataObject;
117
import org.openide.util.Exceptions;
116
import org.openide.util.NbBundle;
118
import org.openide.util.NbBundle;
117
import org.openide.util.Pair;
119
import org.openide.util.Parameters;
118
import org.openide.util.Utilities;
120
import org.openide.util.Utilities;
119
121
122
import static org.netbeans.api.java.source.ModificationResult.*;
123
120
/**XXX: extends CompilationController now, finish method delegation
124
/**XXX: extends CompilationController now, finish method delegation
121
 *
125
 *
122
 * @author Dusan Balek, Petr Hrebejk, Tomas Zezul
126
 * @author Dusan Balek, Petr Hrebejk, Tomas Zezul
Lines 139-144 Link Here
139
     */
143
     */
140
    private Map<Tree, Boolean> introducedTrees;
144
    private Map<Tree, Boolean> introducedTrees;
141
    
145
    
146
    /**
147
     * Hint information on rewrites done. Can mark a Tree as `new' which means comments will not be copied for it
148
     * and it will not assume other tree's formatting on output. Can link a Tree to one or more other Trees, which
149
     * causes the old trees comments to be preserved (copied) should the old trees be removed from the source.
150
     */
151
    private Map<Tree, Tree> rewriteHints = new HashMap<Tree, Tree>();
152
    
142
    WorkingCopy(final CompilationInfoImpl impl, ElementOverlay overlay) {
153
    WorkingCopy(final CompilationInfoImpl impl, ElementOverlay overlay) {
143
        super(impl);
154
        super(impl);
144
        this.overlay = overlay;
155
        this.overlay = overlay;
Lines 226-231 Link Here
226
     * {@link TreeMaker} for tree element removal. If <code>oldTree</code> is
237
     * {@link TreeMaker} for tree element removal. If <code>oldTree</code> is
227
     * null, <code>newTree</code> must be of kind
238
     * null, <code>newTree</code> must be of kind
228
     * {@link Kind#COMPILATION_UNIT COMPILATION_UNIT}.
239
     * {@link Kind#COMPILATION_UNIT COMPILATION_UNIT}.
240
     * <p>
241
     * Since 0.137, comments in the rewritten node will be automatically assigned to the newTree
242
     * node. Use {@link TreeMaker#asRemoved} to discard comments from the oldTree explicitly.
229
     * 
243
     * 
230
     * 
244
     * 
231
     * @param oldTree  tree to be replaced, use tree already represented in
245
     * @param oldTree  tree to be replaced, use tree already represented in
Lines 239-244 Link Here
239
     *         method.
253
     *         method.
240
     * @see GeneratorUtilities#createFromTemplate
254
     * @see GeneratorUtilities#createFromTemplate
241
     * @see TreeMaker
255
     * @see TreeMaker
256
     * @since 0.137
242
     */
257
     */
243
    public synchronized void rewrite(@NullAllowed Tree oldTree, @NonNull Tree newTree) {
258
    public synchronized void rewrite(@NullAllowed Tree oldTree, @NonNull Tree newTree) {
244
        checkConfinement();
259
        checkConfinement();
Lines 255-260 Link Here
255
        }
270
        }
256
        if (oldTree == null || newTree == null)
271
        if (oldTree == null || newTree == null)
257
            throw new IllegalArgumentException("Null values are not allowed.");
272
            throw new IllegalArgumentException("Null values are not allowed.");
273
        Tree t = rewriteHints.get(oldTree);
274
        if (t == null) {
275
            // if the old tree does not have any association to a new generated node, make an implicit association
276
            associateTree(newTree, oldTree, false);
277
        }
258
        // Perf: trees are collected just when asserts are enabled.
278
        // Perf: trees are collected just when asserts are enabled.
259
        assert new TreeCollector(introducedTrees, true).scan(newTree, null) != null;
279
        assert new TreeCollector(introducedTrees, true).scan(newTree, null) != null;
260
        changes.put(oldTree, newTree);
280
        changes.put(oldTree, newTree);
Lines 433-438 Link Here
433
    
453
    
434
    // Package private methods -------------------------------------------------        
454
    // Package private methods -------------------------------------------------        
435
    
455
    
456
//    static final Collection<Tree>   NOT_LINKED = new ArrayList<Tree>(0);
457
    static final Tree NOT_LINKED = new Tree() {
458
459
        @Override
460
        public Kind getKind() {
461
            return Kind.OTHER;
462
        }
463
464
        @Override
465
        public <R, D> R accept(TreeVisitor<R, D> visitor, D data) {
466
            return visitor.visitOther(this, data);
467
        }
468
    };
469
    
470
    /**
471
     * Associates a new tree with the original. Only one association is supported,
472
     * the last association wins. 
473
     * 
474
     * @param nue the new tree
475
     * @param original the to-be-replaced original
476
     */
477
    synchronized void associateTree(Tree nue, Tree original, boolean force) {
478
        if (rewriteHints == null) {
479
            rewriteHints = new HashMap<Tree, Tree>(7);
480
        }
481
        
482
        if (original == null) {
483
            Tree ex = rewriteHints.get(nue);
484
            if (ex == null || force) {
485
                rewriteHints.put(nue, NOT_LINKED);
486
            }
487
        } else if (original == nue) {
488
            return;
489
        } else {
490
            Tree ex = rewriteHints.get(original);
491
            if (ex == null || force) {
492
                rewriteHints.put(original, nue);
493
            }
494
        }
495
    }
496
    
436
    private static String codeForCompilationUnit(CompilationUnitTree topLevel) throws IOException {
497
    private static String codeForCompilationUnit(CompilationUnitTree topLevel) throws IOException {
437
        return ((JCTree.JCCompilationUnit) topLevel).sourcefile.getCharContent(true).toString();
498
        return ((JCTree.JCCompilationUnit) topLevel).sourcefile.getCharContent(true).toString();
438
    }
499
    }
Lines 500-505 Link Here
500
        Map<Integer, String> userInfo = new HashMap<Integer, String>();
561
        Map<Integer, String> userInfo = new HashMap<Integer, String>();
501
        final Set<Tree> oldTrees = new HashSet<Tree>();
562
        final Set<Tree> oldTrees = new HashSet<Tree>();
502
563
564
        final Map<Tree, Boolean> presentInResult = new IdentityHashMap<Tree, Boolean>();
503
        if (CasualDiff.OLD_TREES_VERBATIM) {
565
        if (CasualDiff.OLD_TREES_VERBATIM) {
504
            new TreeScanner<Void, Void>() {
566
            new TreeScanner<Void, Void>() {
505
                private boolean synthetic = false;
567
                private boolean synthetic = false;
Lines 552-557 Link Here
552
                @Override
614
                @Override
553
                public Void scan(Tree node, Void p) {
615
                public Void scan(Tree node, Void p) {
554
                    addSyntheticTrees(diffContext, node);
616
                    addSyntheticTrees(diffContext, node);
617
                    addPresentInResult(presentInResult, node, true);
555
                    return super.scan(node, p);
618
                    return super.scan(node, p);
556
                }
619
                }
557
            }.scan(diffContext.origUnit, null);
620
            }.scan(diffContext.origUnit, null);
Lines 603-620 Link Here
603
                                parent2Rewrites.put(currentParent, new IdentityHashMap<Tree, Tree>());
666
                                parent2Rewrites.put(currentParent, new IdentityHashMap<Tree, Tree>());
604
                            }
667
                            }
605
                        }
668
                        }
606
                        if(changes.containsKey(tree)) {
669
                        Tree rev = changes.get(tree);
670
                        Tree hint = resolveRewriteHint(tree);
671
                        if(rev != null) {
607
                            Map<Tree, Tree> rewrites = parent2Rewrites.get(currentParent);
672
                            Map<Tree, Tree> rewrites = parent2Rewrites.get(currentParent);
673
                            
674
                            changes.remove(tree);
608
675
609
                            Tree rev = changes.remove(tree);
676
                            if (hint == null) {
610
677
                                addPresentInResult(presentInResult, rev, false);
678
                            }
679
                            //presentInResult.remove(tree);
611
                            rewrites.put(tree, rev);
680
                            rewrites.put(tree, rev);
612
681
613
                            scan(rev, p);
682
                            scan(rev, p);
614
                        } else {
683
                        } else {
684
                            if (hint == null) {
685
                                addPresentInResult(presentInResult, rev, false);
686
                            }
687
                            addPresentInResult(presentInResult, tree, true);
615
                            super.scan(tree, p);
688
                            super.scan(tree, p);
616
                        }
689
                        }
617
                    } else {
690
                    } else {
691
                        addPresentInResult(presentInResult, tree, true);
618
                        super.scan(tree, p);
692
                        super.scan(tree, p);
619
                    }
693
                    }
620
                    if (currentParent != null && currentParent.getLeaf() == tree) {
694
                    if (currentParent != null && currentParent.getLeaf() == tree) {
Lines 763-768 Link Here
763
837
764
            //tagging debug
838
            //tagging debug
765
            //System.err.println("brandNew=" + brandNew);
839
            //System.err.println("brandNew=" + brandNew);
840
            new CommentReplicator(presentInResult.keySet()).scan(diffContext.origUnit, null);
766
            
841
            
767
            for (ClassTree ct : classes) {
842
            for (ClassTree ct : classes) {
768
                ia.classLeft();
843
                ia.classLeft();
Lines 798-803 Link Here
798
        }
873
        }
799
    }
874
    }
800
    
875
    
876
    private void addPresentInResult(Map<Tree, Boolean> present, Tree t, boolean mark) {
877
        present.put(t, Boolean.valueOf(mark));
878
        CommentSetImpl csi = CommentHandlerService.instance(impl.getJavacTask().getContext()).getComments(t);
879
        for (RelativePosition pos : RelativePosition.values()) {
880
            useComments(csi.getComments(pos));
881
        }
882
    }
883
    
884
    private Set<Comment> usedComments;
885
    
886
    /* package-private */ List<Comment> useComments(List<Comment> comments) {
887
        if (usedComments == null) {
888
            usedComments = new HashSet<>();
889
        }
890
        usedComments.addAll(comments);
891
        return comments;
892
    }
893
    
894
    /**
895
     * Copies comments according to rewrite hints:<ul>
896
     * <li>copies comments from a tree to an associated tree
897
     * <li>
898
     */
899
    private class CommentReplicator extends TreePathScanner<Object, Object> {
900
        private final Set<Tree>   stillPresent;
901
        
902
        private boolean collectCommentsFromRemovedNodes;
903
        private Map<Tree, Tree> copyTo = new IdentityHashMap<Tree, Tree>();
904
        private final CommentHandlerService commentHandler;
905
        private Tree parentToCopy;
906
        
907
        CommentReplicator(Set<Tree> presentNodes) {
908
            this.stillPresent = presentNodes;
909
            this.commentHandler = CommentHandlerService.instance(impl.getJavacTask().getContext());
910
        }
911
        
912
        private Object scanAndReduce(Tree node, Object p, Object r) {
913
            return reduce(scan(node, p), r);
914
        }
915
        
916
        private RelativePosition collectToPosition;
917
918
        /** Scan a list of nodes.
919
         */
920
        @Override
921
        public Object scan(Iterable<? extends Tree> nodes, Object p) {
922
            Object r = null;
923
            if (nodes != null) {
924
                boolean first = true;
925
                Tree saveParent = parentToCopy;
926
                RelativePosition savePosition = this.collectToPosition;
927
                collectToPosition = RelativePosition.INNER;
928
                if (collectCommentsFromRemovedNodes) {
929
                    // find first such node that it either survived, or has a mapping to the new state. Join all 
930
                    // comments to PRECEDING of such node.
931
                    for (Tree node : nodes) {
932
                        Tree target = resolveRewriteHint(node);
933
                        if (target != null) {
934
                            if (target != NOT_LINKED) {
935
                                parentToCopy = target;
936
                                collectToPosition = RelativePosition.PRECEDING;
937
                                break;
938
                            }
939
                        } else if (stillPresent.contains(node)) {
940
                            parentToCopy = node;
941
                            collectToPosition = RelativePosition.PRECEDING;
942
                            break;
943
                        }
944
                    }
945
                }
946
                boolean reset = false;
947
                for (Tree node : nodes) {
948
                    if (collectCommentsFromRemovedNodes) {
949
                        Tree target = resolveRewriteHint(node);
950
                        if (target != null && target != NOT_LINKED) {
951
                            parentToCopy = target;
952
                            this.collectToPosition = RelativePosition.INNER;
953
                            reset = true;
954
                        } else if (stillPresent.contains(node)) {
955
                            parentToCopy = node;
956
                            this.collectToPosition = RelativePosition.INNER;
957
                            reset = true;
958
                        }
959
                    }
960
                    r = (first ? scan(node, p) : scanAndReduce(node, p, r));
961
                    first = false;
962
                    // reset to trailing, output goes to the anchor node.
963
                    if (reset) {
964
                        this.collectToPosition = RelativePosition.TRAILING;
965
                    }
966
                }
967
                parentToCopy = saveParent;
968
                collectToPosition = savePosition;
969
            }
970
            return r;
971
        }
972
973
        @Override
974
        public Object scan(Tree l, Object p) {
975
            boolean collectChildren = false;
976
            Tree newParentCopy = null;
977
            
978
            boolean saveCollect = this.collectCommentsFromRemovedNodes;
979
            Tree target = resolveRewriteHint(l);
980
            if (target == NOT_LINKED) {
981
                // do not copy anything from this node and its children
982
                collectChildren = false;
983
            } else if (target != null) {
984
                if (!commentHandler.getComments(target).hasComments()) {
985
                    if (!stillPresent.contains(l)) {
986
                        commentHandler.copyComments(l, target, null, usedComments);
987
                        newParentCopy = target;
988
                        collectChildren = true;
989
                    }
990
                }
991
            } else if (!stillPresent.contains(l)) { // target == null, node removed
992
                collectChildren = collectCommentsFromRemovedNodes;
993
                if (collectCommentsFromRemovedNodes) {
994
                    if (parentToCopy != null) {
995
                        commentHandler.copyComments(l, parentToCopy, collectToPosition, usedComments);
996
                    }
997
                }
998
            }
999
            if (stillPresent.contains(l)) {
1000
                newParentCopy = l;
1001
            }
1002
            Tree saveParent = parentToCopy;
1003
            this.collectCommentsFromRemovedNodes = collectChildren;
1004
            if (newParentCopy != null) {
1005
                parentToCopy = newParentCopy;
1006
            }
1007
            Object v = super.scan(l, p);
1008
            this.parentToCopy = saveParent;
1009
            this.collectCommentsFromRemovedNodes = saveCollect;
1010
            return v;
1011
        }
1012
        
1013
    }
1014
    
1015
    private Tree resolveRewriteHint(Tree orig) {
1016
        Tree last;
1017
        Tree target = null;
1018
        Tree from = orig;
1019
        do {
1020
            last = target;
1021
            target = from;
1022
            target = rewriteHints.get(target);
1023
            if (target == NOT_LINKED) {
1024
                return target;
1025
            }
1026
            from = target;
1027
        } while (target != null);
1028
        return last;
1029
    }
1030
    
801
    private List<Difference> processExternalCUs(Map<?, int[]> tag2Span, Set<Tree> syntheticTrees) {
1031
    private List<Difference> processExternalCUs(Map<?, int[]> tag2Span, Set<Tree> syntheticTrees) {
802
        if (externalChanges == null) {
1032
        if (externalChanges == null) {
803
            return Collections.<Difference>emptyList();
1033
            return Collections.<Difference>emptyList();
(-)a/java.source/src/org/netbeans/modules/java/source/builder/CommentHandlerService.java (-1 / +10 lines)
Lines 108-113 Link Here
108
     * appending the new entries to the existing comment lists.
108
     * appending the new entries to the existing comment lists.
109
     */
109
     */
110
    public void copyComments(Tree fromTree, Tree toTree) {
110
    public void copyComments(Tree fromTree, Tree toTree) {
111
        copyComments(fromTree, toTree, null, null);
112
    }
113
        
114
    public void copyComments(Tree fromTree, Tree toTree, RelativePosition copyToPos, Collection<Comment> copied) {
111
        if (fromTree == toTree) {
115
        if (fromTree == toTree) {
112
            return;
116
            return;
113
        }
117
        }
Lines 119-125 Link Here
119
                    map.put(toTree, to = new CommentSetImpl());
123
                    map.put(toTree, to = new CommentSetImpl());
120
                }
124
                }
121
                for (RelativePosition pos : RelativePosition.values()) {
125
                for (RelativePosition pos : RelativePosition.values()) {
122
                    to.addComments(pos, from.getComments(pos));
126
                    for (Comment c : from.getComments(pos)) {
127
                        if (copied != null && copied.contains(c)) {
128
                            continue;
129
                        }
130
                        to.addComment(copyToPos == null ? pos : copyToPos, c, true);
131
                    }
123
                }
132
                }
124
            }
133
            }
125
        }
134
        }
(-)a/java.source/src/org/netbeans/modules/java/source/pretty/VeryPretty.java (-7 / +43 lines)
Lines 394-400 Link Here
394
            return o1[0] - o2[0];
394
            return o1[0] - o2[0];
395
        }
395
        }
396
    });
396
    });
397
    private boolean commentsEnabled;
397
    private final Set<Tree> trailingCommentsHandled = Collections.newSetFromMap(new IdentityHashMap<Tree, Boolean>());
398
    private final Set<Tree> trailingCommentsHandled = Collections.newSetFromMap(new IdentityHashMap<Tree, Boolean>());
399
    private final Set<Tree> innerCommentsHandled = Collections.newSetFromMap(new IdentityHashMap<Tree, Boolean>());
398
    private void doAccept(JCTree t, boolean printComments/*XXX: should ideally always print comments?*/) {
400
    private void doAccept(JCTree t, boolean printComments/*XXX: should ideally always print comments?*/) {
399
        if (!handlePossibleOldTrees(Collections.singletonList(t), printComments)) {
401
        if (!handlePossibleOldTrees(Collections.singletonList(t), printComments)) {
400
            if (printComments) printPrecedingComments(t, true);
402
            if (printComments) printPrecedingComments(t, true);
Lines 421-427 Link Here
421
                    }
423
                    }
422
                }
424
                }
423
            } else {
425
            } else {
426
                boolean saveComments = this.commentsEnabled;
427
                this.commentsEnabled = printComments;
424
                t.accept(this);
428
                t.accept(this);
429
                this.commentsEnabled = saveComments;
425
            }
430
            }
426
431
427
            int end = toString().length();
432
            int end = toString().length();
Lines 432-438 Link Here
432
                tag2Span.put(tag, new int[]{start + initialOffset, end + initialOffset});
437
                tag2Span.put(tag, new int[]{start + initialOffset, end + initialOffset});
433
            }
438
            }
434
        
439
        
435
            if (printComments) printTrailingComments(t, true);
440
            if (printComments) {
441
                printInnerCommentsAsTrailing(t, true);
442
                printTrailingComments(t, true);
443
            }
436
        }
444
        }
437
    }
445
    }
438
    
446
    
Lines 488-494 Link Here
488
        return true;
496
        return true;
489
    }
497
    }
490
    
498
    
491
    private void doPrintOriginalTree(java.util.List<? extends JCTree> toPrint, boolean includeComments) {
499
    private void doPrintOriginalTree(java.util.List<? extends JCTree> toPrint, final boolean includeComments) {
492
        if (out.isWhitespaceLine()) toLeftMargin();
500
        if (out.isWhitespaceLine()) toLeftMargin();
493
        
501
        
494
        JCTree firstTree = toPrint.get(0);
502
        JCTree firstTree = toPrint.get(0);
Lines 512-519 Link Here
512
            public Void scan(Tree node, Void p) {
520
            public Void scan(Tree node, Void p) {
513
                if (node != null) {
521
                if (node != null) {
514
                    CommentSetImpl old = comments.getComments(node);
522
                    CommentSetImpl old = comments.getComments(node);
515
                    realEnd[0] = Math.max(realEnd[0], Math.max(CasualDiff.commentEnd(old, CommentSet.RelativePosition.INLINE), CasualDiff.commentEnd(old, CommentSet.RelativePosition.TRAILING)));
523
                    if (includeComments) {
516
                    trailingCommentsHandled.add(node);
524
                        realEnd[0] = Math.max(realEnd[0], Math.max(CasualDiff.commentEnd(old, CommentSet.RelativePosition.INLINE), CasualDiff.commentEnd(old, CommentSet.RelativePosition.TRAILING)));
525
                        trailingCommentsHandled.add(node);
526
                    }
517
                    
527
                    
518
                    Object tag = tree2Tag != null ? tree2Tag.get(node) : null;
528
                    Object tag = tree2Tag != null ? tree2Tag.get(node) : null;
519
529
Lines 2579-2585 Link Here
2579
       } else {
2589
       } else {
2580
           int prevPrec = this.prec;
2590
           int prevPrec = this.prec;
2581
           this.prec = prec;
2591
           this.prec = prec;
2582
            doAccept(tree, false);
2592
            doAccept(tree, commentsEnabled);
2583
           this.prec = prevPrec;
2593
           this.prec = prevPrec;
2584
       }
2594
       }
2585
    }
2595
    }
Lines 2633-2642 Link Here
2633
            if (col) {
2643
            if (col) {
2634
                toColExactly(out.leftMargin);
2644
                toColExactly(out.leftMargin);
2635
            }
2645
            }
2636
           printPrecedingComments(tree, !member);
2646
            // because of comment duplication 
2647
//         printPrecedingComments(tree, !member);
2648
            printInnerCommentsAsTrailing(tree, !member);
2637
            printExpr(tree, TreeInfo.notExpression);
2649
            printExpr(tree, TreeInfo.notExpression);
2638
           int tag = tree.getTag().ordinal();//XXX: comparing ordinals!!!
2650
           int tag = tree.getTag().ordinal();//XXX: comparing ordinals!!!
2639
           if(JCTree.Tag.APPLY.ordinal()<=tag && tag<=JCTree.Tag.MOD_ASG.ordinal()) print(';');
2651
           if(JCTree.Tag.APPLY.ordinal()<=tag && tag<=JCTree.Tag.MOD_ASG.ordinal()) print(';');
2652
            
2640
            printTrailingComments(tree, !member);
2653
            printTrailingComments(tree, !member);
2641
            blankLines(tree, false);
2654
            blankLines(tree, false);
2642
            if (nl) {
2655
            if (nl) {
Lines 2761-2766 Link Here
2761
       if (emptyBlock) {
2774
       if (emptyBlock) {
2762
            printEmptyBlockComments(tree, members);
2775
            printEmptyBlockComments(tree, members);
2763
        } else {
2776
        } else {
2777
            innerCommentsHandled.add(tree);
2778
            java.util.List<Comment> comments = commentHandler.getComments(tree).getComments(CommentSet.RelativePosition.INNER);
2779
            for (Comment c : comments) {
2780
                printComment(c, false, members);
2781
            }
2764
            if (members)
2782
            if (members)
2765
                blankLines(enclClassName.isEmpty() ? cs.getBlankLinesAfterAnonymousClassHeader() : cs.getBlankLinesAfterClassHeader());
2783
                blankLines(enclClassName.isEmpty() ? cs.getBlankLinesAfterAnonymousClassHeader() : cs.getBlankLinesAfterClassHeader());
2766
            else
2784
            else
Lines 2788-2795 Link Here
2788
            print('>');
2806
            print('>');
2789
        }
2807
        }
2790
    }
2808
    }
2809
    
2810
    private Set<Tree> precedingCommentsHandled = new HashSet<Tree>();
2791
2811
2792
    private void printPrecedingComments(JCTree tree, boolean printWhitespace) {
2812
    private void printPrecedingComments(JCTree tree, boolean printWhitespace) {
2813
        if (!precedingCommentsHandled.add(tree)) {
2814
            return;
2815
        }
2793
        CommentSet commentSet = commentHandler.getComments(tree);
2816
        CommentSet commentSet = commentHandler.getComments(tree);
2794
        java.util.List<Comment> pc = commentSet.getComments(CommentSet.RelativePosition.PRECEDING);
2817
        java.util.List<Comment> pc = commentSet.getComments(CommentSet.RelativePosition.PRECEDING);
2795
        DocCommentTree doc = tree2Doc.get(tree);
2818
        DocCommentTree doc = tree2Doc.get(tree);
Lines 2814-2833 Link Here
2814
        }
2837
        }
2815
    }
2838
    }
2816
2839
2840
    private void printInnerCommentsAsTrailing(JCTree tree, boolean printWhitespace) {
2841
        if (innerCommentsHandled.contains(tree)) return ;
2842
        CommentSet commentSet = commentHandler.getComments(tree);
2843
        java.util.List<Comment> cl = commentSet.getComments(CommentSet.RelativePosition.INNER);
2844
        innerCommentsHandled.add(tree);
2845
        for (Comment comment : cl) {
2846
            printComment(comment, true, printWhitespace);
2847
        }
2848
    }
2849
2817
    private void printTrailingComments(JCTree tree, boolean printWhitespace) {
2850
    private void printTrailingComments(JCTree tree, boolean printWhitespace) {
2818
        if (trailingCommentsHandled.contains(tree)) return ;
2851
        if (trailingCommentsHandled.contains(tree)) return ;
2819
        CommentSet commentSet = commentHandler.getComments(tree);
2852
        CommentSet commentSet = commentHandler.getComments(tree);
2820
        java.util.List<Comment> cl = commentSet.getComments(CommentSet.RelativePosition.INLINE);
2853
        java.util.List<Comment> cl = commentSet.getComments(CommentSet.RelativePosition.INLINE);
2821
        for (Comment comment : cl) {
2854
        for (Comment comment : cl) {
2855
            trailingCommentsHandled.add(tree);
2822
            printComment(comment, true, printWhitespace);
2856
            printComment(comment, true, printWhitespace);
2823
        }
2857
        }
2824
        java.util.List<Comment> tc = commentSet.getComments(CommentSet.RelativePosition.TRAILING);
2858
        java.util.List<Comment> tc = commentSet.getComments(CommentSet.RelativePosition.TRAILING);
2825
        if (!tc.isEmpty()) {
2859
        if (!tc.isEmpty()) {
2860
            trailingCommentsHandled.add(tree);
2826
            for (Comment c : tc)
2861
            for (Comment c : tc)
2827
                printComment(c, false, printWhitespace);
2862
                printComment(c, false, printWhitespace);
2828
        }
2863
        }
2829
    }
2864
    }
2830
2865
    
2831
    private void printEmptyBlockComments(JCTree tree, boolean printWhitespace) {
2866
    private void printEmptyBlockComments(JCTree tree, boolean printWhitespace) {
2832
//        LinkedList<Comment> comments = new LinkedList<Comment>();
2867
//        LinkedList<Comment> comments = new LinkedList<Comment>();
2833
//        if (cInfo != null) {
2868
//        if (cInfo != null) {
Lines 2869-2874 Link Here
2869
//                }
2904
//                }
2870
//            }
2905
//            }
2871
//        }
2906
//        }
2907
        innerCommentsHandled.add(tree);
2872
        java.util.List<Comment> comments = commentHandler.getComments(tree).getComments(CommentSet.RelativePosition.INNER);
2908
        java.util.List<Comment> comments = commentHandler.getComments(tree).getComments(CommentSet.RelativePosition.INNER);
2873
        for (Comment c : comments)
2909
        for (Comment c : comments)
2874
            printComment(c, false, printWhitespace);
2910
            printComment(c, false, printWhitespace);
(-)a/java.source/src/org/netbeans/modules/java/source/save/CasualDiff.java (-23 / +74 lines)
Lines 1341-1346 Link Here
1341
                diffContext
1341
                diffContext
1342
        );
1342
        );
1343
        int old = printer.indent();
1343
        int old = printer.indent();
1344
        localPointer = diffInnerComments(oldT, newT, localPointer);
1344
        Name oldEnclosing = printer.enclClassName;
1345
        Name oldEnclosing = printer.enclClassName;
1345
        printer.enclClassName = null;
1346
        printer.enclClassName = null;
1346
        List<JCTree> oldstats = filterHidden(oldT.stats);
1347
        List<JCTree> oldstats = filterHidden(oldT.stats);
Lines 1872-1878 Link Here
1872
        }
1873
        }
1873
        if (!listsMatch(oldT.args, newT.args)) {
1874
        if (!listsMatch(oldT.args, newT.args)) {
1874
            if (oldT.args.nonEmpty()) {
1875
            if (oldT.args.nonEmpty()) {
1875
                copyTo(localPointer, localPointer = getCommentCorrectedOldPos(oldT.args.head));
1876
                int startArg1 = getCommentCorrectedOldPos(oldT.args.head);
1877
                tokenSequence.move(startArg1);
1878
                moveToSrcRelevant(tokenSequence, Direction.BACKWARD);
1879
                tokenSequence.moveNext();
1880
                copyTo(localPointer, localPointer = tokenSequence.offset());
1876
            } else {
1881
            } else {
1877
                copyTo(localPointer, localPointer = methBounds[1]);
1882
                copyTo(localPointer, localPointer = methBounds[1]);
1878
                tokenSequence.move(localPointer);
1883
                tokenSequence.move(localPointer);
Lines 1998-2004 Link Here
1998
2003
1999
    protected int diffParens(JCParens oldT, JCParens newT, int[] bounds) {
2004
    protected int diffParens(JCParens oldT, JCParens newT, int[] bounds) {
2000
        int localPointer = bounds[0];
2005
        int localPointer = bounds[0];
2001
        copyTo(localPointer, getOldPos(oldT.expr));
2006
        copyTo(localPointer, getCommentCorrectedOldPos(oldT.expr));
2002
        localPointer = diffTree(oldT.expr, newT.expr, getBounds(oldT.expr));
2007
        localPointer = diffTree(oldT.expr, newT.expr, getBounds(oldT.expr));
2003
        copyTo(localPointer, bounds[1]);
2008
        copyTo(localPointer, bounds[1]);
2004
        return bounds[1];
2009
        return bounds[1];
Lines 3094-3104 Link Here
3094
                    JCTree tree = oldList.get(oldIndex++);
3099
                    JCTree tree = oldList.get(oldIndex++);
3095
                    int[] bounds = getCommentCorrectedBounds(tree);
3100
                    int[] bounds = getCommentCorrectedBounds(tree);
3096
                    tokenSequence.move(bounds[0]);
3101
                    tokenSequence.move(bounds[0]);
3102
                    int start = -1;
3097
                    if (oldIndex != 1 && !separator.isEmpty()) {
3103
                    if (oldIndex != 1 && !separator.isEmpty()) {
3098
                        moveToSrcRelevant(tokenSequence, Direction.BACKWARD);
3104
                        if (wasLeadingDelete) {
3105
                            start = Math.max(offsetToSrcWiteOnLine(tokenSequence, Direction.BACKWARD), pos);
3106
                        } else {
3107
                            moveToSrcRelevant(tokenSequence, Direction.BACKWARD);
3108
                        }
3099
                    }
3109
                    }
3100
                    tokenSequence.moveNext();
3110
                    if (start == -1) {
3101
                    int start = Math.max(tokenSequence.offset(), pos);
3111
                        tokenSequence.moveNext();
3112
                        start = Math.max(tokenSequence.offset(), pos);
3113
                    }
3114
                    
3102
                    // in case when invoked through diffFieldGroup for enums, comments are already handled.
3115
                    // in case when invoked through diffFieldGroup for enums, comments are already handled.
3103
                    if (start < bounds[0]) {
3116
                    if (start < bounds[0]) {
3104
                        copyTo(start, bounds[0], printer);
3117
                        copyTo(start, bounds[0], printer);
Lines 3137-3143 Link Here
3137
                    moveToSrcRelevant(tokenSequence, Direction.FORWARD);
3150
                    moveToSrcRelevant(tokenSequence, Direction.FORWARD);
3138
                    if (tokenSequence.token().id() == JavaTokenId.COMMA) {
3151
                    if (tokenSequence.token().id() == JavaTokenId.COMMA) {
3139
                        if (tokenSequence.moveNext()) {
3152
                        if (tokenSequence.moveNext()) {
3140
                            moveToSrcRelevant(tokenSequence, Direction.FORWARD);
3153
//                            moveToSrcRelevant(tokenSequence, Direction.FORWARD);
3141
                        }
3154
                        }
3142
                    }
3155
                    }
3143
                    pos = Math.max(tokenSequence.offset(), endPos);
3156
                    pos = Math.max(tokenSequence.offset(), endPos);
Lines 3151-3161 Link Here
3151
                    }
3164
                    }
3152
                    int[] bounds = getCommentCorrectedBounds(item.element);
3165
                    int[] bounds = getCommentCorrectedBounds(item.element);
3153
                    tokenSequence.move(bounds[0]);
3166
                    tokenSequence.move(bounds[0]);
3154
                    if (oldIndex != 1 && !wasLeadingDelete && !separator.isEmpty()) {
3167
                    int start = -1;
3155
                        moveToSrcRelevant(tokenSequence, Direction.BACKWARD);
3168
                    if (oldIndex != 1 && !separator.isEmpty()) {
3169
                        if (wasLeadingDelete) {
3170
                            start = offsetToSrcWiteOnLine(tokenSequence, Direction.BACKWARD);
3171
                        } else {
3172
                            moveToSrcRelevant(tokenSequence, Direction.BACKWARD);
3173
                        }
3156
                    }
3174
                    }
3157
                    tokenSequence.moveNext();
3175
                    if (start == -1) {
3158
                    int start = tokenSequence.offset();
3176
                        tokenSequence.moveNext();
3177
                        start = tokenSequence.offset();
3178
                    }
3159
                    tokenSequence.move(bounds[1]);
3179
                    tokenSequence.move(bounds[1]);
3160
                    moveToSrcRelevant(tokenSequence, Direction.FORWARD);
3180
                    moveToSrcRelevant(tokenSequence, Direction.FORWARD);
3161
                    int end;
3181
                    int end;
Lines 3718-3723 Link Here
3718
        return comments.getComments(t);
3738
        return comments.getComments(t);
3719
    }
3739
    }
3720
    
3740
    
3741
    protected int diffInnerComments(JCTree oldT, JCTree newT, int localPointer) {
3742
        CommentSet cs = getCommentsForTree(newT, true);
3743
        CommentSet old = getCommentsForTree(oldT, true);
3744
        List<Comment> oldPrecedingComments = old.getComments(CommentSet.RelativePosition.INNER);
3745
        List<Comment> newPrecedingComments = cs.getComments(CommentSet.RelativePosition.INNER);
3746
        if (sameComments(oldPrecedingComments, newPrecedingComments)) {
3747
            if (oldPrecedingComments.isEmpty()) {
3748
                return localPointer;
3749
            }
3750
            int newP = oldPrecedingComments.get(oldPrecedingComments.size() - 1).endPos();
3751
            copyTo(localPointer, newP);
3752
            return newP;
3753
        }
3754
        return diffCommentLists(-1, oldPrecedingComments, newPrecedingComments, null, null, true, false, true,
3755
                false,
3756
                localPointer);
3757
    }
3758
    
3721
    protected int diffPrecedingComments(JCTree oldT, JCTree newT, int oldTreeStartPos, int localPointer, boolean doNotDelete) {
3759
    protected int diffPrecedingComments(JCTree oldT, JCTree newT, int oldTreeStartPos, int localPointer, boolean doNotDelete) {
3722
        CommentSet cs = getCommentsForTree(newT, true);
3760
        CommentSet cs = getCommentsForTree(newT, true);
3723
        CommentSet old = getCommentsForTree(oldT, true);
3761
        CommentSet old = getCommentsForTree(oldT, true);
Lines 3728-3750 Link Here
3728
            if (oldPrecedingComments.isEmpty()) {
3766
            if (oldPrecedingComments.isEmpty()) {
3729
                return localPointer;
3767
                return localPointer;
3730
            }
3768
            }
3731
            return -oldPrecedingComments.get(oldPrecedingComments.size() - 1).endPos();
3769
            int newP = oldPrecedingComments.get(oldPrecedingComments.size() - 1).endPos();
3770
            if (newP > localPointer) {
3771
                copyTo(localPointer, newP);
3772
                return newP;
3773
            } else {
3774
                return localPointer;
3775
            }
3776
            
3732
        }
3777
        }
3733
        DocCommentTree oldD = oldTopLevel.docComments.getCommentTree(oldT);
3778
        DocCommentTree oldD = oldTopLevel.docComments.getCommentTree(oldT);
3734
        return diffCommentLists(oldTreeStartPos, oldPrecedingComments, newPrecedingComments, oldD, newD, false, true, 
3779
        return diffCommentLists(oldTreeStartPos, oldPrecedingComments, newPrecedingComments, oldD, newD, false, true, false,
3735
                doNotDelete,
3780
                doNotDelete,
3736
                localPointer);
3781
                localPointer);
3737
    }
3782
    }
3738
3783
3739
    protected int diffTrailingComments(JCTree oldT, JCTree newT, int localPointer) {
3784
    protected int diffTrailingComments(JCTree oldT, JCTree newT, int localPointer, int elementEndWithComments) {
3740
        CommentSet cs = getCommentsForTree(newT, false);
3785
        CommentSet cs = getCommentsForTree(newT, false);
3741
        CommentSet old = getCommentsForTree(oldT, false);
3786
        CommentSet old = getCommentsForTree(oldT, false);
3742
        List<Comment> oldInlineComments = old.getComments(CommentSet.RelativePosition.INLINE);
3787
        List<Comment> oldInlineComments = old.getComments(CommentSet.RelativePosition.INLINE);
3743
        List<Comment> newInlineComments = cs.getComments(CommentSet.RelativePosition.INLINE);
3788
        List<Comment> newInlineComments = cs.getComments(CommentSet.RelativePosition.INLINE);
3744
        List<Comment> oldTrailingComments = old.getComments(CommentSet.RelativePosition.TRAILING);
3789
        List<Comment> oldTrailingComments = old.getComments(CommentSet.RelativePosition.TRAILING);
3745
        List<Comment> newTrailingComments = cs.getComments(CommentSet.RelativePosition.TRAILING);
3790
        List<Comment> newTrailingComments = cs.getComments(CommentSet.RelativePosition.TRAILING);
3746
        if (sameComments(oldInlineComments, newInlineComments) && sameComments(oldTrailingComments, newTrailingComments))
3791
        if (sameComments(oldInlineComments, newInlineComments) && sameComments(oldTrailingComments, newTrailingComments)) {
3792
            // copy the comments
3793
            if (oldInlineComments.isEmpty() && oldTrailingComments.isEmpty()) {
3794
                return localPointer;
3795
            }
3796
            copyTo(localPointer, localPointer = elementEndWithComments);
3747
            return localPointer;
3797
            return localPointer;
3798
        }
3748
3799
3749
        //XXX: hack: the upper diff might already add '\n' to the result, need to skip it if diffing inline comments
3800
        //XXX: hack: the upper diff might already add '\n' to the result, need to skip it if diffing inline comments
3750
        if (!sameComments(oldInlineComments, newInlineComments)) {
3801
        if (!sameComments(oldInlineComments, newInlineComments)) {
Lines 3752-3758 Link Here
3752
                printer.eatChars(1);
3803
                printer.eatChars(1);
3753
        }
3804
        }
3754
        
3805
        
3755
        localPointer = diffCommentLists(getOldPos(oldT), oldInlineComments, newInlineComments, null, null, false, false, false, localPointer);
3806
        localPointer = diffCommentLists(getOldPos(oldT), oldInlineComments, newInlineComments, null, null, false, false, false, false, localPointer);
3756
3807
3757
        boolean containedEmbeddedNewLine = false;
3808
        boolean containedEmbeddedNewLine = false;
3758
        boolean containsEmbeddedNewLine = false;
3809
        boolean containsEmbeddedNewLine = false;
Lines 3769-3775 Link Here
3769
            printer.print("\n");
3820
            printer.print("\n");
3770
        }
3821
        }
3771
3822
3772
        return diffCommentLists(getOldPos(oldT), oldTrailingComments, newTrailingComments, null, null, true, false, false, localPointer);
3823
        return diffCommentLists(getOldPos(oldT), oldTrailingComments, newTrailingComments, null, null, true, false, false, false, localPointer);
3773
    }
3824
    }
3774
3825
3775
    private boolean sameComments(List<Comment> oldList, List<Comment> newList) {
3826
    private boolean sameComments(List<Comment> oldList, List<Comment> newList) {
Lines 3789-3795 Link Here
3789
    
3840
    
3790
    // refactor it! make it better
3841
    // refactor it! make it better
3791
    private int diffCommentLists(int oldTreeStartPos, List<Comment> oldList,
3842
    private int diffCommentLists(int oldTreeStartPos, List<Comment> oldList,
3792
            List<Comment> newList, DocCommentTree oldDoc, DocCommentTree newDoc, boolean trailing, boolean preceding, 
3843
            List<Comment> newList, DocCommentTree oldDoc, DocCommentTree newDoc, boolean trailing, boolean preceding, boolean inner,
3793
            boolean doNotDeleteIfMissing,
3844
            boolean doNotDeleteIfMissing,
3794
            int localPointer) {
3845
            int localPointer) {
3795
        Comment javadoc = null;
3846
        Comment javadoc = null;
Lines 3865-3871 Link Here
3865
                printer.printComment(newC, !trailing, false, !preceding && !trailing);
3916
                printer.printComment(newC, !trailing, false, !preceding && !trailing);
3866
                firstNewCommentPrinted = true;
3917
                firstNewCommentPrinted = true;
3867
            }
3918
            }
3868
            newC = safeNext(oldIter);
3919
            newC = safeNext(newIter);
3869
        }
3920
        }
3870
        if(preceding && javadoc == null && newDoc != null) {
3921
        if(preceding && javadoc == null && newDoc != null) {
3871
            if (!firstNewCommentPrinted && preceding) {
3922
            if (!firstNewCommentPrinted && preceding) {
Lines 4721-4731 Link Here
4721
        int predComments = diffPrecedingComments(oldT, newT, getOldPos(oldT), elementBounds[0], 
4772
        int predComments = diffPrecedingComments(oldT, newT, getOldPos(oldT), elementBounds[0], 
4722
                oldT.getTag() == Tag.TOPLEVEL && diffContext.forceInitialComment);
4773
                oldT.getTag() == Tag.TOPLEVEL && diffContext.forceInitialComment);
4723
        int retVal = -1;
4774
        int retVal = -1;
4724
        if (predComments < 0 && elementBounds[0] < -predComments) {
4775
//        if (predComments < 0 && elementBounds[0] < -predComments) {
4725
            copyTo(elementBounds[0], -predComments);
4776
//            copyTo(elementBounds[0], -predComments);
4726
        }
4777
//        }
4727
        elementBounds[0] = Math.abs(predComments);
4778
        elementBounds[0] = Math.abs(predComments);
4728
        
4779
        int elementEnd = elementBounds[1];
4729
        int commentsStart = Math.min(commentStart(diffContext, comments.getComments(oldT), CommentSet.RelativePosition.INLINE, endPos(oldT)), commentStart(diffContext, comments.getComments(oldT), CommentSet.RelativePosition.TRAILING, endPos(oldT)));
4780
        int commentsStart = Math.min(commentStart(diffContext, comments.getComments(oldT), CommentSet.RelativePosition.INLINE, endPos(oldT)), commentStart(diffContext, comments.getComments(oldT), CommentSet.RelativePosition.TRAILING, endPos(oldT)));
4730
        if (commentsStart < elementBounds[1]) {
4781
        if (commentsStart < elementBounds[1]) {
4731
            int lastIndex;
4782
            int lastIndex;
Lines 4950-4956 Link Here
4950
        if (handleImplicitLambda) {
5001
        if (handleImplicitLambda) {
4951
            printer.print(")");
5002
            printer.print(")");
4952
        }
5003
        }
4953
        return diffTrailingComments(oldT, newT, retVal);
5004
        return diffTrailingComments(oldT, newT, retVal, elementEnd);
4954
    }
5005
    }
4955
5006
4956
    /**
5007
    /**
(-)a/java.source/src/org/netbeans/modules/java/source/save/PositionEstimator.java (+52 lines)
Lines 1666-1671 Link Here
1666
        return moveToDifferentThan(seq, dir, nonRelevant);
1666
        return moveToDifferentThan(seq, dir, nonRelevant);
1667
    }
1667
    }
1668
    
1668
    
1669
    /**
1670
     * 
1671
     * @param seq the token sequence
1672
     * @param dir direction
1673
     * @return 
1674
     */
1675
    public static int offsetToSrcWiteOnLine(TokenSequence<JavaTokenId> seq,
1676
                                                 Direction dir)
1677
    {
1678
        boolean notBound = false;
1679
        seq.moveNext();
1680
        int savePos = seq.offset();
1681
        switch (dir) {
1682
            case BACKWARD:
1683
                while ((notBound = seq.movePrevious())) {
1684
                    JavaTokenId tid = seq.token().id();
1685
                    if (tid == WHITESPACE) {
1686
                        int nl = seq.token().text().toString().indexOf('\n');
1687
                        if (nl > -1) {
1688
                            // return the position after newline:
1689
                            return seq.offset() + nl + 1;
1690
                        }
1691
                    } else if (!nonRelevant.contains(tid)) {
1692
                        break;
1693
                    } else {
1694
                        savePos = seq.offset();
1695
                    }
1696
                }
1697
                break;
1698
            case FORWARD:
1699
                while ((notBound = seq.moveNext())) {
1700
                    JavaTokenId tid = seq.token().id();
1701
                    if (tid == WHITESPACE) {
1702
                        int nl = seq.token().text().toString().indexOf('\n');
1703
                        if (nl > -1) {
1704
                            // return the position after newline:
1705
                            return seq.offset() + nl;
1706
                        }
1707
                    } else if (!nonRelevant.contains(tid)) {
1708
                        break;
1709
                    } else {
1710
                        savePos = seq.offset() + seq.token().length();
1711
                    }
1712
                }
1713
                break;
1714
        }
1715
        if (!notBound) {
1716
            return -1;
1717
        }
1718
        return savePos;
1719
    }
1720
1669
    @SuppressWarnings("empty-statement")
1721
    @SuppressWarnings("empty-statement")
1670
    public static JavaTokenId moveToDifferentThan(
1722
    public static JavaTokenId moveToDifferentThan(
1671
        TokenSequence<JavaTokenId> seq,
1723
        TokenSequence<JavaTokenId> seq,
(-)a/java.source/src/org/netbeans/modules/java/source/transform/ImmutableTreeTranslator.java (-4 / +13 lines)
Lines 45-52 Link Here
45
45
46
package org.netbeans.modules.java.source.transform;
46
package org.netbeans.modules.java.source.transform;
47
47
48
import javax.lang.model.util.Elements;
49
import org.netbeans.modules.java.source.query.CommentHandler;
50
48
51
import com.sun.source.tree.*;
49
import com.sun.source.tree.*;
52
import com.sun.source.tree.Tree.Kind;
50
import com.sun.source.tree.Tree.Kind;
Lines 54-74 Link Here
54
import com.sun.tools.javac.model.JavacElements;
52
import com.sun.tools.javac.model.JavacElements;
55
import com.sun.tools.javac.tree.JCTree.JCModifiers;
53
import com.sun.tools.javac.tree.JCTree.JCModifiers;
56
import com.sun.tools.javac.util.Context;
54
import com.sun.tools.javac.util.Context;
57
import javax.lang.model.element.Element;
58
59
import java.util.ArrayList;
55
import java.util.ArrayList;
60
import java.util.List;
56
import java.util.List;
61
import java.util.Map;
57
import java.util.Map;
62
import java.util.Set;
58
import java.util.Set;
59
import javax.lang.model.element.Element;
63
import javax.lang.model.element.ElementKind;
60
import javax.lang.model.element.ElementKind;
64
import javax.lang.model.element.QualifiedNameable;
61
import javax.lang.model.element.QualifiedNameable;
62
import javax.lang.model.util.Elements;
65
import org.netbeans.api.java.source.GeneratorUtilities;
63
import org.netbeans.api.java.source.GeneratorUtilities;
64
import org.netbeans.api.java.source.TreeMaker;
66
import org.netbeans.api.java.source.WorkingCopy;
65
import org.netbeans.api.java.source.WorkingCopy;
67
import org.netbeans.modules.java.source.builder.ASTService;
66
import org.netbeans.modules.java.source.builder.ASTService;
68
import org.netbeans.modules.java.source.builder.CommentHandlerService;
67
import org.netbeans.modules.java.source.builder.CommentHandlerService;
69
import org.netbeans.modules.java.source.builder.QualIdentTree;
68
import org.netbeans.modules.java.source.builder.QualIdentTree;
70
import org.netbeans.modules.java.source.builder.TreeFactory;
69
import org.netbeans.modules.java.source.builder.TreeFactory;
71
import org.netbeans.modules.java.source.pretty.ImportAnalysis2;
70
import org.netbeans.modules.java.source.pretty.ImportAnalysis2;
71
import org.netbeans.modules.java.source.query.CommentHandler;
72
import org.netbeans.modules.java.source.save.ElementOverlay;
72
import org.netbeans.modules.java.source.save.ElementOverlay;
73
73
74
import static org.netbeans.modules.java.source.save.PositionEstimator.*;
74
import static org.netbeans.modules.java.source.save.PositionEstimator.*;
Lines 90-95 Link Here
90
 *  <br>&nbsp;&nbsp;return new_tree_to_replace_old
90
 *  <br>&nbsp;&nbsp;return new_tree_to_replace_old
91
 *  <br>&nbsp;&nbsp;// (returning the original tree leaves it unchanged)
91
 *  <br>&nbsp;&nbsp;// (returning the original tree leaves it unchanged)
92
 *  <br>}
92
 *  <br>}
93
 * <p/>
94
 * To help code formatting and comment preservation, translated nodes are marked
95
 * as replacements for their originals.
93
 */
96
 */
94
public class ImmutableTreeTranslator implements TreeVisitor<Tree,Object> {
97
public class ImmutableTreeTranslator implements TreeVisitor<Tree,Object> {
95
98
Lines 102-107 Link Here
102
    private ElementOverlay overlay;
105
    private ElementOverlay overlay;
103
    private ImportAnalysis2 importAnalysis;
106
    private ImportAnalysis2 importAnalysis;
104
    private Map<Tree, Object> tree2Tag;
107
    private Map<Tree, Object> tree2Tag;
108
    /**
109
     * Newly created nodes will be mareked as replacements for the old ones
110
     */
111
    private TreeMaker tmaker;
105
    
112
    
106
    private WorkingCopy copy;
113
    private WorkingCopy copy;
107
114
Lines 115-120 Link Here
115
        comments = CommentHandlerService.instance(context);
122
        comments = CommentHandlerService.instance(context);
116
        model = ASTService.instance(context);
123
        model = ASTService.instance(context);
117
        overlay = context.get(ElementOverlay.class);
124
        overlay = context.get(ElementOverlay.class);
125
        tmaker = copy.getTreeMaker();
118
        this.importAnalysis = importAnalysis;
126
        this.importAnalysis = importAnalysis;
119
        this.tree2Tag = tree2Tag;
127
        this.tree2Tag = tree2Tag;
120
    }
128
    }
Lines 136-141 Link Here
136
           Tree t = tree.accept(this, null);
144
           Tree t = tree.accept(this, null);
137
            
145
            
138
            if (tree2Tag != null && tree != t) {
146
            if (tree2Tag != null && tree != t) {
147
                t = tmaker.asReplacementOf(t, tree, true);
139
                tree2Tag.put(t, tree2Tag.get(tree));
148
                tree2Tag.put(t, tree2Tag.get(tree));
140
            }
149
            }
141
            
150
            
(-)a/java.source/test/unit/src/org/netbeans/api/java/source/gen/CommentsTest.java (-1 / +16 lines)
Lines 1218-1224 Link Here
1218
                        "         * f\n" +
1218
                        "         * f\n" +
1219
                        "         */\n" +
1219
                        "         */\n" +
1220
                        "        int f;\n" +
1220
                        "        int f;\n" +
1221
                        "\n" +
1221
                        "        \n" +
1222
                        "        /**\n" +
1222
                        "        /**\n" +
1223
                        "         * run\n" +
1223
                        "         * run\n" +
1224
                        "         */\n" +
1224
                        "         */\n" +
Lines 1340-1345 Link Here
1340
                        "import java.io.FileNotFoundException;\n" +
1340
                        "import java.io.FileNotFoundException;\n" +
1341
                        "public abstract class Test {\n" +
1341
                        "public abstract class Test {\n" +
1342
                        "    public void test() {\n" +
1342
                        "    public void test() {\n" +
1343
                        "        //t1\n" +
1344
                        "        //t2\n" +
1345
                        "        //t3\n" +
1346
                        "        \n" +
1343
                        "        name();\n" +
1347
                        "        name();\n" +
1344
                        "    }\n" +
1348
                        "    }\n" +
1345
                        "}\n";
1349
                        "}\n";
Lines 1391-1396 Link Here
1391
                        "import java.io.FileNotFoundException;\n" +
1395
                        "import java.io.FileNotFoundException;\n" +
1392
                        "public abstract class Test {\n" +
1396
                        "public abstract class Test {\n" +
1393
                        "    public void test() {\n" +
1397
                        "    public void test() {\n" +
1398
                        "        //t1\n" +
1399
                        "        //t2\n" +
1400
                        "        //t3\n" +
1401
                        "        //t4\n" +
1402
                        "        \n" +
1394
                        "        name();\n" +
1403
                        "        name();\n" +
1395
                        "    }\n" +
1404
                        "    }\n" +
1396
                        "}\n";
1405
                        "}\n";
Lines 1442-1447 Link Here
1442
                        "import java.io.FileNotFoundException;\n" +
1451
                        "import java.io.FileNotFoundException;\n" +
1443
                        "public abstract class Test {\n" +
1452
                        "public abstract class Test {\n" +
1444
                        "    public void test() {\n" +
1453
                        "    public void test() {\n" +
1454
                        "        //t1\n" +
1455
                        "        //t2\n" +
1456
                        "        //test1\n" +
1457
                        "        //t3\n" +
1458
                        "        //t4\n" +
1459
                        "        \n" +
1445
                        "        name();\n" +
1460
                        "        name();\n" +
1446
                        "    }\n" +
1461
                        "    }\n" +
1447
                        "}\n";
1462
                        "}\n";
(-)a/java.source/test/unit/src/org/netbeans/api/java/source/gen/Method1Test.java (-1 / +50 lines)
Lines 70-75 Link Here
70
    
70
    
71
    public static NbTestSuite suite() {
71
    public static NbTestSuite suite() {
72
        NbTestSuite suite = new NbTestSuite();
72
        NbTestSuite suite = new NbTestSuite();
73
        suite.addTest(new Method1Test("testMethodParametersWithComments"));
73
        suite.addTest(new Method1Test("testMethodModifiers"));
74
        suite.addTest(new Method1Test("testMethodModifiers"));
74
        suite.addTest(new Method1Test("testMethodName"));
75
        suite.addTest(new Method1Test("testMethodName"));
75
        suite.addTest(new Method1Test("testMethodParameters"));
76
        suite.addTest(new Method1Test("testMethodParameters"));
Lines 421-427 Link Here
421
                "        return x + y;\n" +
422
                "        return x + y;\n" +
422
                "    }\n" +
423
                "    }\n" +
423
                "    void m2() {\n" +
424
                "    void m2() {\n" +
424
                "        plus( /*bar*/ plus(2, 3), /*foo*/ 1 );\n" +
425
                "        plus(/*bar*/ plus(2, 3), /*foo*/ 1 );\n" +
425
                "    }\n" +
426
                "    }\n" +
426
                "}";
427
                "}";
427
        testFile = new File(getWorkDir(), "Test.java");
428
        testFile = new File(getWorkDir(), "Test.java");
Lines 661-666 Link Here
661
//        });
662
//        });
662
//        assertFiles("testAddRemoveInOneTrans.pass");
663
//        assertFiles("testAddRemoveInOneTrans.pass");
663
//    }
664
//    }
665
    
666
    public void testMethodParametersWithComments() throws Exception {
667
      // XXX should also test annotations
668
      // XXX expected whitespace might not be correct in golden
669
      String test =
670
              "class Test {\n" +
671
              "    void m() {\n" +
672
              "        plus(|/*foo*/ Math.abs(1), /*bar*/ Math.abs(2));\n" +
673
              "    }\n" +
674
              "}";
675
      String golden =
676
              "class Test {\n" +
677
              "    void m() {\n" +
678
              "        plus(/*bar*/ Math.abs(2), /*foo*/ Math.abs(1));\n" +
679
              "    }\n" +
680
              "}";
681
      File file = new File(getWorkDir(), "Test.java");
682
      final int indexA = test.indexOf("|");
683
      assertTrue(indexA != -1);
684
      TestUtilities.copyStringToFile(file, test.replace("|", ""));
685
      JavaSource src = getJavaSource(file);
686
      Task<WorkingCopy> task = new Task<WorkingCopy>() {
687
688
          public void run(WorkingCopy copy) throws Exception {
689
              if (copy.toPhase(Phase.RESOLVED).compareTo(Phase.RESOLVED) < 0) {
690
                  return;
691
              }
692
              Tree node = copy.getTreeUtilities().pathFor(indexA).getLeaf();
693
              GeneratorUtilities.get(copy).importComments(node, copy.getCompilationUnit());
694
              assertEquals(Kind.METHOD_INVOCATION, node.getKind());
695
              TreeMaker make = copy.getTreeMaker();
696
              MethodInvocationTree original = (MethodInvocationTree) node;
697
              List<? extends ExpressionTree> oldArgs = original.getArguments();
698
              List<ExpressionTree> newArgs = new ArrayList<ExpressionTree>();
699
              newArgs.add(oldArgs.get(1));
700
              newArgs.add(oldArgs.get(0));
701
              @SuppressWarnings("unchecked")
702
              MethodInvocationTree modified = make.MethodInvocation(
703
                      (List<? extends ExpressionTree>) original.getTypeArguments(),
704
                      original.getMethodSelect(), newArgs);
705
              System.out.println("original: " + node);
706
              System.out.println("modified: " + modified);
707
              copy.rewrite(node, modified);            }
708
      };
709
      src.runModificationTask(task).commit();
710
      String res = TestUtilities.copyFileToString(file);
711
      assertEquals(golden, res);
712
  }
664
713
665
    ////////////////////////////////////////////////////////////////////////////
714
    ////////////////////////////////////////////////////////////////////////////
666
    /**
715
    /**

Return to bug 245348