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

(-)a/editor.lib/src/org/netbeans/editor/BaseDocument.java (-1 lines)
Lines 574-580 Link Here
574
        this.addUpdateDocumentListener(modElementRoot);
574
        this.addUpdateDocumentListener(modElementRoot);
575
        modElementRoot.setEnabled(true);
575
        modElementRoot.setEnabled(true);
576
576
577
        TrailingWhitespaceRemove.ensureRegistered();
578
        BeforeSaveTasks.get(this); // Ensure that "beforeSaveRunnable" gets initialized
577
        BeforeSaveTasks.get(this); // Ensure that "beforeSaveRunnable" gets initialized
579
578
580
        undoEditWrappers = MimeLookup.getLookup(mimeType).lookupAll(UndoableEditWrapper.class);
579
        undoEditWrappers = MimeLookup.getLookup(mimeType).lookupAll(UndoableEditWrapper.class);
(-)a/editor.lib/src/org/netbeans/modules/editor/lib/BeforeSaveTasks.java (-121 / +65 lines)
Lines 45-56 Link Here
45
package org.netbeans.modules.editor.lib;
45
package org.netbeans.modules.editor.lib;
46
46
47
import java.util.ArrayList;
47
import java.util.ArrayList;
48
import java.util.Collection;
48
import java.util.List;
49
import java.util.List;
49
import java.util.logging.Level;
50
import java.util.logging.Level;
50
import java.util.logging.Logger;
51
import java.util.logging.Logger;
51
import javax.swing.text.Document;
52
import javax.swing.undo.UndoableEdit;
52
import javax.swing.undo.UndoableEdit;
53
import org.netbeans.api.editor.mimelookup.MimeLookup;
53
import org.netbeans.editor.BaseDocument;
54
import org.netbeans.editor.BaseDocument;
55
import org.netbeans.lib.editor.util.swing.DocumentUtilities;
56
import org.netbeans.modules.editor.lib2.document.DocumentSpiPackageAccessor;
57
import org.netbeans.modules.editor.lib2.document.ModRootElement;
58
import org.netbeans.spi.editor.document.OnSaveTask;
54
59
55
/**
60
/**
56
 * Registration of tasks performed right before document save.
61
 * Registration of tasks performed right before document save.
Lines 62-69 Link Here
62
    
67
    
63
    private static final Logger LOG = Logger.getLogger(BeforeSaveTasks.class.getName());
68
    private static final Logger LOG = Logger.getLogger(BeforeSaveTasks.class.getName());
64
69
65
    private static final List<Task> tasks = new ArrayList<Task>(5);
66
    
67
    public static synchronized BeforeSaveTasks get(BaseDocument doc) {
70
    public static synchronized BeforeSaveTasks get(BaseDocument doc) {
68
        BeforeSaveTasks beforeSaveTasks = (BeforeSaveTasks) doc.getProperty(BeforeSaveTasks.class);
71
        BeforeSaveTasks beforeSaveTasks = (BeforeSaveTasks) doc.getProperty(BeforeSaveTasks.class);
69
        if (beforeSaveTasks == null) {
72
        if (beforeSaveTasks == null) {
Lines 91-220 Link Here
91
        doc.putProperty("beforeSaveRunnable", beforeSaveRunnable); // NOI18N
94
        doc.putProperty("beforeSaveRunnable", beforeSaveRunnable); // NOI18N
92
    }
95
    }
93
96
94
    /**
97
    void runTasks() {
95
     * Add a new task to be executed before save of the document.
98
        String mimeType = DocumentUtilities.getMimeType(doc);
96
     *
99
        Collection<? extends OnSaveTask.Factory> factories = MimeLookup.getLookup(mimeType).
97
     * @param task non-null task.
100
                lookupAll(OnSaveTask.Factory.class);
98
     */
101
        OnSaveTask.Context context = DocumentSpiPackageAccessor.get().createContext(doc);
99
    public static void addTask(Task task) {
102
        List<OnSaveTask> tasks = new ArrayList<OnSaveTask>(factories.size());
100
        if (task == null)
103
        for (OnSaveTask.Factory factory : factories) {
101
            throw new IllegalArgumentException("task must not be null"); // NOI18N
104
            OnSaveTask task = factory.createTask(context);
102
        synchronized (tasks) {
105
            if (task != null) {
103
            tasks.add(task);
106
                tasks.add(task);
107
            }
108
        }
109
        if (tasks.size() > 0) {
110
            new TaskRunnable(doc, tasks).run();
104
        }
111
        }
105
    }
112
    }
106
113
107
    /**
114
    private static final class TaskRunnable implements Runnable {
108
     * Remove a task from the list of existing before-save tasks.
115
        
109
     *
116
        BaseDocument doc;
110
     * @param task runnable to be removed.
117
111
     * @return true if the tasks was removed successfully or false if the task
118
        List<OnSaveTask> tasks;
112
     *  was not found (compared by <code>Object.equals()</code>).
119
113
     */
120
        int lockedTaskIndex;
114
    public static boolean removeTask(Task task) {
121
115
        synchronized (tasks) {
122
        public TaskRunnable(BaseDocument doc, List<OnSaveTask> tasks) {
116
            return tasks.remove(task);
123
            this.doc = doc;
124
            this.tasks = tasks;
117
        }
125
        }
118
    }
126
119
    
127
        @Override
120
    public static boolean removeTask(Class taskClass) {
128
        public void run() {
121
        synchronized (tasks) {
129
            if (lockedTaskIndex < tasks.size()) {
122
            int i = taskIndex(taskClass);
130
                OnSaveTask task = tasks.get(lockedTaskIndex++);
123
            if (i >= 0) {
131
                task.runLocked(this);
124
                tasks.remove(i);
132
125
                return true;
133
            } else {
134
                try {
135
                    doc.runAtomicAsUser(new Runnable() {
136
                        public @Override
137
                        void run() {
138
                            UndoableEdit atomicEdit = EditorPackageAccessor.get().BaseDocument_markAtomicEditsNonSignificant(doc);
139
                            // Since these are before-save actions they should generally not prevent
140
                            // the save operation to succeed. Thus the possible exceptions thrown
141
                            // by the tasks will be notified but they will not prevent the save to succeed.
142
                            try {
143
                                for (int i = 0; i < tasks.size(); i++) {
144
                                    OnSaveTask task = tasks.get(i);
145
                                    task.performTask(atomicEdit);
146
                                }
147
                                ModRootElement modRootElement = ModRootElement.get(doc);
148
                                if (modRootElement != null) {
149
                                    modRootElement.resetMods(atomicEdit);
150
                                }
151
                            } catch (Exception e) {
152
                                LOG.log(Level.WARNING, "Exception thrown in before-save tasks", e); // NOI18N
153
                            }
154
155
                        }
156
                    });
157
                } finally {
158
                    EditorPackageAccessor.get().BaseDocument_clearAtomicEdits(doc);
159
                }
126
            }
160
            }
127
        }
161
        }
128
        return false;
129
    }
130
131
    public static Task getTask(Class taskClass) {
132
        synchronized (tasks) {
133
            int i = taskIndex(taskClass);
134
            return (i >= 0) ? tasks.get(i) : null;
135
        }
136
    }
137
138
    private static int taskIndex(Class taskClass) {
139
        for (int i = tasks.size() - 1; i >= 0; i--) {
140
            Task task = tasks.get(i);
141
            if (taskClass == task.getClass()) {
142
                return i;
143
            }
144
        }
145
        return -1;
146
    }
147
148
    void runTasks() {
149
        final List<Task> tasksCopy = new ArrayList<Task>(tasks);
150
        final List<Object> locks = new ArrayList<Object>(tasksCopy.size());
151
        int taskCount = tasksCopy.size();
152
        int lockedTaskEndIndex = 0;
153
        for (;lockedTaskEndIndex < taskCount; lockedTaskEndIndex++) {
154
            Task task = tasksCopy.get(lockedTaskEndIndex);
155
            locks.add(task.lock(doc));
156
        }
157
        try {
158
            doc.runAtomicAsUser (new Runnable () {
159
                public @Override void run () {
160
                    UndoableEdit atomicEdit = EditorPackageAccessor.get().BaseDocument_markAtomicEditsNonSignificant(doc);
161
                    // Since these are before-save actions they should generally not prevent
162
                    // the save operation to succeed. Thus the possible exceptions thrown
163
                    // by the tasks will be notified but they will not prevent the save to succeed.
164
                    try {
165
                        for (int i = 0; i < tasksCopy.size(); i++) {
166
                            Task task = tasksCopy.get(i);
167
                            task.run(locks.get(i), doc, atomicEdit);
168
                        }
169
                    } catch (Exception e) {
170
                        LOG.log(Level.WARNING, "Exception thrown in before-save tasks", e); // NOI18N
171
                    }
172
173
                }
174
            });
175
        } finally {
176
            while (lockedTaskEndIndex > 0) {
177
                Task task = tasksCopy.get(--lockedTaskEndIndex);
178
                task.unlock(locks.get(lockedTaskEndIndex), doc);
179
            }
180
181
            EditorPackageAccessor.get().BaseDocument_clearAtomicEdits(doc);
182
        }
183
    }
184
185
    /**
186
     * Task run right before document saving such as reformatting or trailing whitespace removal.
187
     */
188
    public interface Task {
189
190
        /**
191
         * Perform an extra lock for task if necessary.
192
         * Locks for all tasks will be obtained first then atomic document lock will be obtained
193
         * and then all the tasks will be run. Then all the locks will be released by running unlock()
194
         * in all tasks in a reverse order of locking.
195
         * @param doc non-null document.
196
         */
197
        Object lock(Document doc);
198
199
        /**
200
         * Run the before-save task.
201
         *
202
         * @param lockInfo lock object produced by {@link #lock(javax.swing.text.Document) }
203
         *  that may contain an arbitrary info.
204
         * @param doc non-null document.
205
         * @param edit a non-ended edit to which undoable edits of the task should be added.
206
         *  It may be null in which case the produced tasks should not be added to anything.
207
         */
208
        void run(Object lockInfo, Document doc, UndoableEdit edit);
209
210
        /**
211
         * Perform an extra unlock for task if necessary.
212
         *
213
         * @param lockInfo lock object produced by {@link #lock(javax.swing.text.Document) }
214
         *  that may contain an arbitrary info.
215
         * @param doc non-null document.
216
         */
217
        void unlock(Object lockInfo, Document doc);
218
162
219
    }
163
    }
220
164
(-)a/editor.lib/src/org/netbeans/modules/editor/lib/TrailingWhitespaceRemove.java (-16 / +19 lines)
Lines 50-59 Link Here
50
import javax.swing.undo.UndoableEdit;
50
import javax.swing.undo.UndoableEdit;
51
import org.netbeans.api.editor.mimelookup.MimeLookup;
51
import org.netbeans.api.editor.mimelookup.MimeLookup;
52
import org.netbeans.api.editor.mimelookup.MimePath;
52
import org.netbeans.api.editor.mimelookup.MimePath;
53
import org.netbeans.api.editor.mimelookup.MimeRegistration;
53
import org.netbeans.api.editor.settings.SimpleValueNames;
54
import org.netbeans.api.editor.settings.SimpleValueNames;
54
import org.netbeans.lib.editor.util.swing.DocumentUtilities;
55
import org.netbeans.lib.editor.util.swing.DocumentUtilities;
55
import org.netbeans.modules.editor.lib2.document.ModRootElement;
56
import org.netbeans.modules.editor.lib2.document.ModRootElement;
56
import org.netbeans.modules.editor.lib2.document.TrailingWhitespaceRemoveProcessor;
57
import org.netbeans.modules.editor.lib2.document.TrailingWhitespaceRemoveProcessor;
58
import org.netbeans.spi.editor.document.OnSaveTask;
57
59
58
/**
60
/**
59
 * Removal of trailing whitespace
61
 * Removal of trailing whitespace
Lines 61-89 Link Here
61
 * @author Miloslav Metelka
63
 * @author Miloslav Metelka
62
 * @since 1.27
64
 * @since 1.27
63
 */
65
 */
64
public final class TrailingWhitespaceRemove implements BeforeSaveTasks.Task {
66
public final class TrailingWhitespaceRemove implements OnSaveTask {
65
67
66
    // -J-Dorg.netbeans.modules.editor.lib.TrailingWhitespaceRemove.level=FINE
68
    // -J-Dorg.netbeans.modules.editor.lib.TrailingWhitespaceRemove.level=FINE
67
    static final Logger LOG = Logger.getLogger(TrailingWhitespaceRemove.class.getName());
69
    static final Logger LOG = Logger.getLogger(TrailingWhitespaceRemove.class.getName());
68
70
69
    private static final TrailingWhitespaceRemove INSTANCE = new TrailingWhitespaceRemove();
71
    private final Document doc;
70
72
71
    public static void ensureRegistered() {
73
    TrailingWhitespaceRemove(Document doc) {
72
        if (BeforeSaveTasks.getTask(TrailingWhitespaceRemove.class) == null) {
74
        this.doc = doc;
73
            BeforeSaveTasks.addTask(INSTANCE);
74
        }
75
    }
76
77
    private TrailingWhitespaceRemove() {
78
    }
75
    }
79
76
80
    @Override
77
    @Override
81
    public Object lock(Document doc) {
78
    public void performTask(UndoableEdit undoEdit) {
82
        return null; // No extra locking
83
    }
84
85
    @Override
86
    public void run(Object lock, Document doc, UndoableEdit compoundEdit) {
87
        Preferences prefs = MimeLookup.getLookup(DocumentUtilities.getMimeType(doc)).lookup(Preferences.class);
79
        Preferences prefs = MimeLookup.getLookup(DocumentUtilities.getMimeType(doc)).lookup(Preferences.class);
88
        if (prefs.getBoolean(SimpleValueNames.ON_SAVE_USE_GLOBAL_SETTINGS, Boolean.TRUE)) {
80
        if (prefs.getBoolean(SimpleValueNames.ON_SAVE_USE_GLOBAL_SETTINGS, Boolean.TRUE)) {
89
            prefs = MimeLookup.getLookup(MimePath.EMPTY).lookup(Preferences.class);
81
            prefs = MimeLookup.getLookup(MimePath.EMPTY).lookup(Preferences.class);
Lines 104-110 Link Here
104
    }
96
    }
105
97
106
    @Override
98
    @Override
107
    public void unlock(Object lock, Document doc) {
99
    public void runLocked(Runnable run) {
100
        run.run();
101
    }
102
103
    @MimeRegistration(mimeType="", service=OnSaveTask.Factory.class, position=1000)
104
    public static final class FactoryImpl implements Factory {
105
106
        @Override
107
        public OnSaveTask createTask(Context context) {
108
            return new TrailingWhitespaceRemove(context.getDocument());
109
        }
110
108
    }
111
    }
109
112
110
}
113
}
(-)cbdf0aa62e1e (+159 lines)
Added Link Here
1
/*
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3
 *
4
 * Copyright 2012 Oracle and/or its affiliates. All rights reserved.
5
 *
6
 * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
7
 * Other names may be trademarks of their respective owners.
8
 *
9
 * The contents of this file are subject to the terms of either the GNU
10
 * General Public License Version 2 only ("GPL") or the Common
11
 * Development and Distribution License("CDDL") (collectively, the
12
 * "License"). You may not use this file except in compliance with the
13
 * License. You can obtain a copy of the License at
14
 * http://www.netbeans.org/cddl-gplv2.html
15
 * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
16
 * specific language governing permissions and limitations under the
17
 * License.  When distributing the software, include this License Header
18
 * Notice in each file and include the License file at
19
 * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
20
 * particular file as subject to the "Classpath" exception as provided
21
 * by Oracle in the GPL Version 2 section of the License file that
22
 * accompanied this code. If applicable, add the following below the
23
 * License Header, with the fields enclosed by brackets [] replaced by
24
 * your own identifying information:
25
 * "Portions Copyrighted [year] [name of copyright owner]"
26
 *
27
 * If you wish your version of this file to be governed by only the CDDL
28
 * or only the GPL Version 2, indicate your decision by adding
29
 * "[Contributor] elects to include this software in this distribution
30
 * under the [CDDL or GPL Version 2] license." If you do not indicate a
31
 * single choice of license, a recipient has the option to distribute
32
 * your version of this file under either the CDDL, the GPL Version 2 or
33
 * to extend the choice of license to its licensees as provided above.
34
 * However, if you add GPL Version 2 code and therefore, elected the GPL
35
 * Version 2 license, then the option applies only if the new code is
36
 * made subject to such option by the copyright holder.
37
 *
38
 * Contributor(s):
39
 *
40
 * Portions Copyrighted 2012 Sun Microsystems, Inc.
41
 */
42
package org.netbeans.modules.editor.lib;
43
44
import javax.swing.undo.UndoableEdit;
45
import org.netbeans.api.editor.mimelookup.MimePath;
46
import org.netbeans.api.editor.mimelookup.test.MockMimeLookup;
47
import org.netbeans.editor.BaseDocument;
48
import org.netbeans.junit.MockServices;
49
import org.netbeans.junit.NbTestCase;
50
import org.netbeans.spi.editor.document.OnSaveTask;
51
52
/**
53
 *
54
 * @author Miloslav Metelka
55
 */
56
public class BeforeSaveTasksTest extends NbTestCase {
57
58
    private static final String MIME_TYPE = "text/x-test-on-save";
59
60
    public BeforeSaveTasksTest(String name) {
61
        super(name);
62
    }
63
64
    public void testOnSaveTasks() {
65
        MockServices.setServices(MockMimeLookup.class);
66
        MockMimeLookup.setInstances(MimePath.parse(MIME_TYPE),
67
                new TestOnSaveTask1.TestFactory1(),
68
                new TestOnSaveTask2.TestFactory2()
69
        );
70
        BaseDocument doc = new BaseDocument(false, MIME_TYPE);
71
        BeforeSaveTasks.get(doc);
72
        Runnable beforeSaveRunnable = (Runnable) doc.getProperty("beforeSaveRunnable");
73
        beforeSaveRunnable.run();
74
        assertNotNull("TestOnSaveTask2 not created", TestOnSaveTask2.TestFactory2.lastCreatedTask);
75
        assertTrue("TestOnSaveTask2 not run", TestOnSaveTask2.TestFactory2.lastCreatedTask.taskPerformed);
76
    }
77
78
    private static final class TestOnSaveTask1 implements OnSaveTask {
79
80
        boolean taskLocked;
81
82
        boolean taskPerformed;
83
84
        TestOnSaveTask1(Context context) {
85
        }
86
87
        @Override
88
        public void performTask(UndoableEdit undoEdit) {
89
            assertTrue("Task not locked", taskLocked);
90
            assertFalse("Task run multiple times", taskPerformed);
91
            taskPerformed = true;
92
        }
93
94
        @Override
95
        public void runLocked(Runnable run) {
96
            taskLocked = true;
97
            try {
98
                run.run();
99
            } finally {
100
                taskLocked = false;
101
            }
102
        }
103
104
        static final class TestFactory1 implements OnSaveTask.Factory {
105
106
            static TestOnSaveTask1 lastCreatedTask;
107
108
            public OnSaveTask createTask(Context context) {
109
                assertNotNull("Context null", context);
110
                return (lastCreatedTask = new TestOnSaveTask1(context));
111
            }
112
113
        }
114
115
    }
116
117
    private static final class TestOnSaveTask2 implements OnSaveTask {
118
119
        boolean taskLocked;
120
121
        boolean taskPerformed;
122
123
        TestOnSaveTask2(OnSaveTask.Context context) {
124
        }
125
126
        @Override
127
        public void performTask(UndoableEdit undoEdit) {
128
            assertTrue("Task1 not locked", TestOnSaveTask1.TestFactory1.lastCreatedTask.taskLocked);
129
            assertTrue("Task1 not performed yet", TestOnSaveTask1.TestFactory1.lastCreatedTask.taskPerformed);
130
131
            assertTrue("Task not locked", taskLocked);
132
            assertFalse("Task run multiple times", taskPerformed);
133
            taskPerformed = true;
134
        }
135
136
        @Override
137
        public void runLocked(Runnable run) {
138
            taskLocked = true;
139
            try {
140
                run.run();
141
            } finally {
142
                taskLocked = false;
143
            }
144
        }
145
146
        static final class TestFactory2 implements OnSaveTask.Factory {
147
148
            static TestOnSaveTask2 lastCreatedTask;
149
150
            public OnSaveTask createTask(OnSaveTask.Context context) {
151
                assertNotNull("Context null", context);
152
                return (lastCreatedTask = new TestOnSaveTask2(context));
153
            }
154
155
        }
156
157
    }
158
159
}
(-)a/editor.lib/test/unit/src/org/netbeans/modules/editor/lib/TrailingWhitespaceRemoveTest.java (+7 lines)
Lines 52-60 Link Here
52
import javax.swing.undo.UndoManager;
52
import javax.swing.undo.UndoManager;
53
import org.netbeans.api.editor.mimelookup.MimeLookup;
53
import org.netbeans.api.editor.mimelookup.MimeLookup;
54
import org.netbeans.api.editor.mimelookup.MimePath;
54
import org.netbeans.api.editor.mimelookup.MimePath;
55
import org.netbeans.api.editor.mimelookup.test.MockMimeLookup;
55
import org.netbeans.api.editor.settings.SimpleValueNames;
56
import org.netbeans.api.editor.settings.SimpleValueNames;
56
import org.netbeans.editor.BaseDocument;
57
import org.netbeans.editor.BaseDocument;
57
import org.netbeans.editor.BaseKit;
58
import org.netbeans.editor.BaseKit;
59
import org.netbeans.junit.MockServices;
58
import org.netbeans.junit.NbTestCase;
60
import org.netbeans.junit.NbTestCase;
59
import org.netbeans.lib.editor.util.ArrayUtilities;
61
import org.netbeans.lib.editor.util.ArrayUtilities;
60
import org.netbeans.lib.editor.util.CharSequenceUtilities;
62
import org.netbeans.lib.editor.util.CharSequenceUtilities;
Lines 94-99 Link Here
94
    }
96
    }
95
97
96
    private void checkTrailingWhitespaceRemove(String policy, String result) throws Exception {
98
    private void checkTrailingWhitespaceRemove(String policy, String result) throws Exception {
99
        MockServices.setServices(MockMimeLookup.class);
100
        MockMimeLookup.setInstances(MimePath.parse(""),
101
                new TrailingWhitespaceRemove.FactoryImpl()
102
        );
103
        
97
        RandomTestContainer container = DocumentTesting.initContainer(null);
104
        RandomTestContainer container = DocumentTesting.initContainer(null);
98
        container.setName(this.getName());
105
        container.setName(this.getName());
99
//        container.putProperty(RandomTestContainer.LOG_OP, Boolean.TRUE);
106
//        container.putProperty(RandomTestContainer.LOG_OP, Boolean.TRUE);
(-)a/editor.lib2/apichanges.xml (+15 lines)
Lines 107-112 Link Here
107
    <!-- ACTUAL CHANGES BEGIN HERE: -->
107
    <!-- ACTUAL CHANGES BEGIN HERE: -->
108
108
109
    <changes>
109
    <changes>
110
        <change id="OnSaveTask">
111
            <summary>OnSaveTask interface added</summary>
112
            <version major="1" minor="66"/>
113
            <date day="5" month="9" year="2012"/>
114
            <author login="mmetelka"/>
115
            <compatibility binary="compatible" source="compatible" semantic="compatible" addition="yes"/>
116
            <description>
117
                <p>
118
                    Added OnSaveTask interface which allows modules to register
119
                    tasks into MimeLookup that will be performed right before document saving.
120
                </p>
121
            </description>
122
            <issue number="217904"/>
123
        </change>
124
110
        <change id="UndoableEditWrapper">
125
        <change id="UndoableEditWrapper">
111
            <summary>UndoableEditWrapper interface added</summary>
126
            <summary>UndoableEditWrapper interface added</summary>
112
            <version major="1" minor="60"/>
127
            <version major="1" minor="60"/>
(-)a/editor.lib2/manifest.mf (-1 / +1 lines)
Lines 1-6 Link Here
1
Manifest-Version: 1.0
1
Manifest-Version: 1.0
2
OpenIDE-Module: org.netbeans.modules.editor.lib2/1
2
OpenIDE-Module: org.netbeans.modules.editor.lib2/1
3
OpenIDE-Module-Implementation-Version: 33
3
OpenIDE-Module-Implementation-Version: 34
4
OpenIDE-Module-Localizing-Bundle: org/netbeans/modules/editor/lib2/Bundle.properties
4
OpenIDE-Module-Localizing-Bundle: org/netbeans/modules/editor/lib2/Bundle.properties
5
OpenIDE-Module-Layer: org/netbeans/modules/editor/lib2/resources/layer.xml
5
OpenIDE-Module-Layer: org/netbeans/modules/editor/lib2/resources/layer.xml
6
OpenIDE-Module-Needs: org.netbeans.modules.editor.actions
6
OpenIDE-Module-Needs: org.netbeans.modules.editor.actions
(-)a/editor.lib2/nbproject/project.properties (-1 / +1 lines)
Lines 43-49 Link Here
43
is.autoload=true
43
is.autoload=true
44
javac.source=1.6
44
javac.source=1.6
45
javac.compilerargs=-Xlint:unchecked
45
javac.compilerargs=-Xlint:unchecked
46
spec.version.base=1.65.0
46
spec.version.base=1.66.0
47
47
48
javadoc.arch=${basedir}/arch.xml
48
javadoc.arch=${basedir}/arch.xml
49
javadoc.apichanges=${basedir}/apichanges.xml
49
javadoc.apichanges=${basedir}/apichanges.xml
(-)a/editor.lib2/nbproject/project.xml (+6 lines)
Lines 148-153 Link Here
148
                        <compile-dependency/>
148
                        <compile-dependency/>
149
                    </test-dependency>
149
                    </test-dependency>
150
                    <test-dependency>
150
                    <test-dependency>
151
                        <code-name-base>org.netbeans.modules.editor.mimelookup</code-name-base>
152
                        <recursive/>
153
                        <compile-dependency/>
154
                        <test/>
155
                    </test-dependency>
156
                    <test-dependency>
151
                        <code-name-base>org.netbeans.modules.editor.mimelookup.impl</code-name-base>
157
                        <code-name-base>org.netbeans.modules.editor.mimelookup.impl</code-name-base>
152
                        <recursive/>
158
                        <recursive/>
153
                    </test-dependency>
159
                    </test-dependency>
(-)a/editor.lib2/src/org/netbeans/modules/editor/lib2/document/DocumentInternalUtils.java (-5 / +23 lines)
Lines 56-69 Link Here
56
    }
56
    }
57
57
58
    public static Element customElement(Document doc, int startOffset, int endOffset) {
58
    public static Element customElement(Document doc, int startOffset, int endOffset) {
59
        return new CustomElement(doc, startOffset, endOffset);
59
        return new CustomRootElement(doc, startOffset, endOffset);
60
    }
60
    }
61
61
62
    private static final class CustomElement extends AbstractPositionElement {
62
    private static final class CustomElement extends AbstractPositionElement {
63
63
64
        CustomElement(Document doc, int startOffset, int endOffset) {
64
        CustomElement(Element parent, int startOffset, int endOffset) {
65
            super(new CustomRootElement(doc), startOffset, endOffset);
65
            super(parent, startOffset, endOffset);
66
            CharSequenceUtilities.checkIndexesValid(startOffset, endOffset, doc.getLength() + 1);
66
            CharSequenceUtilities.checkIndexesValid(startOffset, endOffset,
67
                    parent.getDocument().getLength() + 1);
67
        }
68
        }
68
69
69
        @Override
70
        @Override
Lines 76-83 Link Here
76
77
77
    private static final class CustomRootElement extends AbstractRootElement<CustomElement> {
78
    private static final class CustomRootElement extends AbstractRootElement<CustomElement> {
78
79
79
        public CustomRootElement(Document doc) {
80
        private final CustomElement customElement;
81
82
        public CustomRootElement(Document doc, int startOffset, int endOffset) {
80
            super(doc);
83
            super(doc);
84
            customElement = new CustomElement(this, startOffset, endOffset);
81
        }
85
        }
82
86
83
        @Override
87
        @Override
Lines 85-89 Link Here
85
            return "CustomRootElement";
89
            return "CustomRootElement";
86
        }
90
        }
87
91
92
        @Override
93
        public Element getElement(int index) {
94
            if (index == 0) {
95
                return customElement;
96
            } else {
97
                return null;
98
            }
99
        }
100
101
        @Override
102
        public int getElementCount() {
103
            return 1;
104
        }
105
88
    }
106
    }
89
}
107
}
(-)cbdf0aa62e1e (+76 lines)
Added Link Here
1
/*
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3
 *
4
 * Copyright 2012 Oracle and/or its affiliates. All rights reserved.
5
 *
6
 * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
7
 * Other names may be trademarks of their respective owners.
8
 *
9
 * The contents of this file are subject to the terms of either the GNU
10
 * General Public License Version 2 only ("GPL") or the Common
11
 * Development and Distribution License("CDDL") (collectively, the
12
 * "License"). You may not use this file except in compliance with the
13
 * License. You can obtain a copy of the License at
14
 * http://www.netbeans.org/cddl-gplv2.html
15
 * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
16
 * specific language governing permissions and limitations under the
17
 * License.  When distributing the software, include this License Header
18
 * Notice in each file and include the License file at
19
 * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
20
 * particular file as subject to the "Classpath" exception as provided
21
 * by Oracle in the GPL Version 2 section of the License file that
22
 * accompanied this code. If applicable, add the following below the
23
 * License Header, with the fields enclosed by brackets [] replaced by
24
 * your own identifying information:
25
 * "Portions Copyrighted [year] [name of copyright owner]"
26
 *
27
 * If you wish your version of this file to be governed by only the CDDL
28
 * or only the GPL Version 2, indicate your decision by adding
29
 * "[Contributor] elects to include this software in this distribution
30
 * under the [CDDL or GPL Version 2] license." If you do not indicate a
31
 * single choice of license, a recipient has the option to distribute
32
 * your version of this file under either the CDDL, the GPL Version 2 or
33
 * to extend the choice of license to its licensees as provided above.
34
 * However, if you add GPL Version 2 code and therefore, elected the GPL
35
 * Version 2 license, then the option applies only if the new code is
36
 * made subject to such option by the copyright holder.
37
 *
38
 * Contributor(s):
39
 *
40
 * Portions Copyrighted 2012 Sun Microsystems, Inc.
41
 */
42
package org.netbeans.modules.editor.lib2.document;
43
44
import javax.swing.text.Document;
45
import org.netbeans.spi.editor.document.OnSaveTask;
46
import org.openide.util.Exceptions;
47
48
/**
49
 * Package accessor for o.n.spi.editor.document package.
50
 *
51
 * @author Miloslav Metelka
52
 */
53
public abstract class DocumentSpiPackageAccessor {
54
55
    private static DocumentSpiPackageAccessor INSTANCE;
56
57
    public static DocumentSpiPackageAccessor get() {
58
        if (INSTANCE == null) {
59
            // Cause api accessor impl to get initialized
60
            try {
61
                Class.forName(OnSaveTask.Context.class.getName(), true, DocumentSpiPackageAccessor.class.getClassLoader());
62
            } catch (ClassNotFoundException e) {
63
                Exceptions.printStackTrace(e);
64
            }
65
            assert (INSTANCE != null) : "Registration failed"; // NOI18N
66
        }
67
        return INSTANCE;
68
    }
69
70
    public static void register(DocumentSpiPackageAccessor accessor) {
71
        INSTANCE = accessor;
72
    }
73
74
    public abstract OnSaveTask.Context createContext(Document doc);
75
76
}
(-)cbdf0aa62e1e (+129 lines)
Added Link Here
1
/*
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3
 *
4
 * Copyright 2012 Oracle and/or its affiliates. All rights reserved.
5
 *
6
 * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
7
 * Other names may be trademarks of their respective owners.
8
 *
9
 * The contents of this file are subject to the terms of either the GNU
10
 * General Public License Version 2 only ("GPL") or the Common
11
 * Development and Distribution License("CDDL") (collectively, the
12
 * "License"). You may not use this file except in compliance with the
13
 * License. You can obtain a copy of the License at
14
 * http://www.netbeans.org/cddl-gplv2.html
15
 * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
16
 * specific language governing permissions and limitations under the
17
 * License.  When distributing the software, include this License Header
18
 * Notice in each file and include the License file at
19
 * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
20
 * particular file as subject to the "Classpath" exception as provided
21
 * by Oracle in the GPL Version 2 section of the License file that
22
 * accompanied this code. If applicable, add the following below the
23
 * License Header, with the fields enclosed by brackets [] replaced by
24
 * your own identifying information:
25
 * "Portions Copyrighted [year] [name of copyright owner]"
26
 *
27
 * If you wish your version of this file to be governed by only the CDDL
28
 * or only the GPL Version 2, indicate your decision by adding
29
 * "[Contributor] elects to include this software in this distribution
30
 * under the [CDDL or GPL Version 2] license." If you do not indicate a
31
 * single choice of license, a recipient has the option to distribute
32
 * your version of this file under either the CDDL, the GPL Version 2 or
33
 * to extend the choice of license to its licensees as provided above.
34
 * However, if you add GPL Version 2 code and therefore, elected the GPL
35
 * Version 2 license, then the option applies only if the new code is
36
 * made subject to such option by the copyright holder.
37
 *
38
 * Contributor(s):
39
 *
40
 * Portions Copyrighted 2012 Sun Microsystems, Inc.
41
 */
42
package org.netbeans.spi.editor.document;
43
44
import javax.swing.text.Document;
45
import javax.swing.undo.UndoableEdit;
46
import org.netbeans.api.annotations.common.NonNull;
47
import org.netbeans.modules.editor.lib2.document.DocumentSpiPackageAccessor;
48
import org.netbeans.spi.editor.mimelookup.MimeLocation;
49
50
/**
51
 * Task done right before document is saved.
52
 * Factories need to be registered in MimeLookup.
53
 *
54
 * @author Miloslav Metelka
55
 * @since 1.66
56
 */
57
public interface OnSaveTask {
58
59
    /**
60
     * Perform the task on the context (given to factory that produced this task).
61
     *
62
     * @param undoEdit undoable edit to which the task may add extra things that
63
     *  should be undoable by {@link UndoableEdit#addEdit(javax.swing.undo.UndoableEdit)}.
64
     *  Modification changes performed by the task on underlying document
65
     *  will be undone automatically.
66
     */
67
    void performTask(UndoableEdit undoEdit);
68
69
    /**
70
     * Perform the given runnable under a lock specific for this task.
71
     * The runnable will include a call to {@link #performTask() } but it may
72
     * also call other tasks if there are multiple ones.
73
     * <br/>
74
     * For multiple task factories registered their {@link #runLocked(java.lang.Runnable) }
75
     * methods will be called subsequently (according to their registration order) in a nested way.
76
     *
77
     * @param run non-null runnable provided by the infrastructure.
78
     */
79
    void runLocked(@NonNull Runnable run);
80
81
    /**
82
     * Factory for creation of on-save task.
83
     * It should be registered in MimeLookup via xml layer in "/Editors/&lt;mime-type&gt;"
84
     * folder.
85
     */
86
    @MimeLocation(subfolderName="OnSave")
87
    public interface Factory {
88
89
        /**
90
         * Create on-save task.
91
         *
92
         * @param context non-null context containing info for the task.
93
         * @return task instance or null if the task is not appropriate for the given context.
94
         */
95
        OnSaveTask createTask(@NonNull Context context);
96
97
    }
98
99
    /**
100
     * Context given to factory for production of on-save task.
101
     */
102
    public static final class Context {
103
104
        static {
105
            DocumentSpiPackageAccessor.register(new PackageAccessor());
106
        }
107
108
        private final Document doc;
109
110
        Context(Document doc) {
111
            this.doc = doc;
112
        }
113
114
        public Document getDocument() {
115
            return doc;
116
        }
117
118
    }
119
120
    static final class PackageAccessor extends DocumentSpiPackageAccessor {
121
122
        @Override
123
        public Context createContext(Document doc) {
124
            return new Context(doc);
125
        }
126
127
    }
128
129
}
(-)a/editor/src/org/netbeans/modules/editor/EditorModule.java (-2 lines)
Lines 307-314 Link Here
307
                }
307
                }
308
            }
308
            }
309
        });
309
        });
310
311
        ReformatBeforeSaveTask.ensureRegistered();
312
    }
310
    }
313
    
311
    
314
    /** Called when module is uninstalled. Overrides superclass method. */
312
    /** Called when module is uninstalled. Overrides superclass method. */
(-)a/editor/src/org/netbeans/modules/editor/impl/ReformatBeforeSaveTask.java (-170 / +157 lines)
Lines 42-48 Link Here
42
package org.netbeans.modules.editor.impl;
42
package org.netbeans.modules.editor.impl;
43
43
44
import java.util.ArrayList;
44
import java.util.ArrayList;
45
import java.util.LinkedList;
46
import java.util.List;
45
import java.util.List;
47
import java.util.logging.Level;
46
import java.util.logging.Level;
48
import java.util.logging.Logger;
47
import java.util.logging.Logger;
Lines 54-59 Link Here
54
import javax.swing.undo.UndoableEdit;
53
import javax.swing.undo.UndoableEdit;
55
import org.netbeans.api.editor.mimelookup.MimeLookup;
54
import org.netbeans.api.editor.mimelookup.MimeLookup;
56
import org.netbeans.api.editor.mimelookup.MimePath;
55
import org.netbeans.api.editor.mimelookup.MimePath;
56
import org.netbeans.api.editor.mimelookup.MimeRegistration;
57
import org.netbeans.api.editor.settings.SimpleValueNames;
57
import org.netbeans.api.editor.settings.SimpleValueNames;
58
import org.netbeans.editor.GuardedDocument;
58
import org.netbeans.editor.GuardedDocument;
59
import org.netbeans.editor.MarkBlock;
59
import org.netbeans.editor.MarkBlock;
Lines 62-71 Link Here
62
import org.netbeans.lib.editor.util.swing.DocumentUtilities;
62
import org.netbeans.lib.editor.util.swing.DocumentUtilities;
63
import org.netbeans.lib.editor.util.swing.PositionRegion;
63
import org.netbeans.lib.editor.util.swing.PositionRegion;
64
import org.netbeans.modules.editor.indent.api.Reformat;
64
import org.netbeans.modules.editor.indent.api.Reformat;
65
import org.netbeans.modules.editor.lib.BeforeSaveTasks;
66
import org.netbeans.modules.editor.lib2.document.DocumentInternalUtils;
65
import org.netbeans.modules.editor.lib2.document.DocumentInternalUtils;
67
import org.netbeans.modules.editor.lib2.document.ModRootElement;
66
import org.netbeans.modules.editor.lib2.document.ModRootElement;
68
import org.netbeans.modules.editor.lib2.document.TrailingWhitespaceRemoveProcessor;
67
import org.netbeans.spi.editor.document.OnSaveTask;
69
import org.openide.util.Exceptions;
68
import org.openide.util.Exceptions;
70
69
71
/**
70
/**
Lines 73-267 Link Here
73
 *
72
 *
74
 * @author Miloslav Metelka
73
 * @author Miloslav Metelka
75
 */
74
 */
76
public class ReformatBeforeSaveTask implements BeforeSaveTasks.Task {
75
public class ReformatBeforeSaveTask implements OnSaveTask {
77
78
    private static final ReformatBeforeSaveTask INSTANCE = new ReformatBeforeSaveTask();
79
80
    public static void ensureRegistered() {
81
        if (BeforeSaveTasks.getTask(ReformatBeforeSaveTask.class) == null) {
82
            BeforeSaveTasks.addTask(INSTANCE);
83
        }
84
    }
85
76
86
    // -J-Dorg.netbeans.modules.editor.impl.ReformatAtSaveTask.level=FINE
77
    // -J-Dorg.netbeans.modules.editor.impl.ReformatAtSaveTask.level=FINE
87
    private static final Logger LOG = Logger.getLogger(ReformatBeforeSaveTask.class.getName());
78
    private static final Logger LOG = Logger.getLogger(ReformatBeforeSaveTask.class.getName());
88
79
89
    private ReformatBeforeSaveTask() {
80
    private final Document doc;
81
82
    private Reformat reformat;
83
84
    private boolean modifiedLinesOnly;
85
86
    private List<PositionRegion> guardedBlocks;
87
88
    private int guardedBlockIndex;
89
90
    private Position guardedBlockStartPos;
91
92
    private Position guardedBlockEndPos;
93
94
    ReformatBeforeSaveTask(Document doc) {
95
        this.doc = doc;
90
    }
96
    }
91
97
92
    @Override
98
    @Override
93
    public Object lock(Document doc) {
99
    public void performTask(UndoableEdit undoEdit) {
100
        if (reformat != null)
101
            reformat();
102
    }
103
104
    @Override
105
    public void runLocked(Runnable run) {
94
        Preferences prefs = MimeLookup.getLookup(DocumentUtilities.getMimeType(doc)).lookup(Preferences.class);
106
        Preferences prefs = MimeLookup.getLookup(DocumentUtilities.getMimeType(doc)).lookup(Preferences.class);
95
        if (prefs.getBoolean(SimpleValueNames.ON_SAVE_USE_GLOBAL_SETTINGS, Boolean.TRUE)) {
107
        if (prefs.getBoolean(SimpleValueNames.ON_SAVE_USE_GLOBAL_SETTINGS, Boolean.TRUE)) {
96
            prefs = MimeLookup.getLookup(MimePath.EMPTY).lookup(Preferences.class);
108
            prefs = MimeLookup.getLookup(MimePath.EMPTY).lookup(Preferences.class);
97
        }
109
        }
98
        String policy = prefs.get(SimpleValueNames.ON_SAVE_REFORMAT, "never"); //NOI18N
110
        String policy = prefs.get(SimpleValueNames.ON_SAVE_REFORMAT, "never"); //NOI18N
99
        if (!"never".equals(policy)) { //NOI18N
111
        if (!"never".equals(policy)) { //NOI18N
100
            Reformat reformat = Reformat.get(doc);
112
            modifiedLinesOnly = "modified-lines".equals(policy);
113
            reformat = Reformat.get(doc);
101
            reformat.lock();
114
            reformat.lock();
102
            LockInfo lockInfo = new LockInfo(reformat, "modified-lines".equals(policy));
115
            try {
103
            return lockInfo;
116
                run.run();
104
        }
117
            } finally {
105
        return null;
118
                reformat.unlock();
106
    }
119
            }
107
120
        } else {
108
    @Override
121
            run.run();
109
    public void run(Object lock, Document doc, UndoableEdit compoundEdit) {
110
        if (lock != null) {
111
            ((LockInfo)lock).reformat(doc);
112
        }
122
        }
113
    }
123
    }
114
124
115
    @Override
125
    void reformat() {
116
    public void unlock(Object lock, Document doc) {
126
        ModRootElement modRootElement = ModRootElement.get(doc);
117
        if (lock != null) {
127
        if (modRootElement != null) {
118
            ((LockInfo)lock).unlock();
128
            boolean origEnabled = modRootElement.isEnabled();
129
            modRootElement.setEnabled(false);
130
            try {
131
                // Read all guarded blocks
132
                guardedBlocks = new GapList<PositionRegion>();
133
                if (doc instanceof GuardedDocument) {
134
                    MarkBlock block = ((GuardedDocument) doc).getGuardedBlockChain().getChain();
135
                    while (block != null) {
136
                        try {
137
                            guardedBlocks.add(new PositionRegion(doc, block.getStartOffset(), block.getEndOffset()));
138
                        } catch (BadLocationException ex) {
139
                            Exceptions.printStackTrace(ex);
140
                        }
141
                        block = block.getNext();
142
                    }
143
144
                }
145
146
                guardedBlockIndex = 0;
147
                fetchNextGuardedBlock();
148
                Element modRootOrDocElement = (modifiedLinesOnly)
149
                        ? modRootElement
150
                        : DocumentInternalUtils.customElement(doc, 0, doc.getLength());
151
                int modElementCount = modRootOrDocElement.getElementCount();
152
                List<PositionRegion> formatBlocks = new ArrayList<PositionRegion>(modElementCount);
153
                for (int i = 0; i < modElementCount; i++) {
154
                    Element modElement = modRootOrDocElement.getElement(i);
155
                    boolean modElementFinished;
156
                    boolean add;
157
                    int startOffset = modElement.getStartOffset();
158
                    int modElementEndOffset = modElement.getEndOffset();
159
                    int endOffset = modElementEndOffset;
160
                    do {
161
                        if (guardedBlockStartPos != null) {
162
                            BlockCompare blockCompare = BlockCompare.get(
163
                                    startOffset,
164
                                    endOffset,
165
                                    guardedBlockStartPos.getOffset(),
166
                                    guardedBlockEndPos.getOffset());
167
                            if (blockCompare.before()) {
168
                                add = true;
169
                                modElementFinished = true;
170
                            } else if (blockCompare.after()) {
171
                                fetchNextGuardedBlock();
172
                                add = false;
173
                                modElementFinished = false;
174
                            } else if (blockCompare.equal()) {
175
                                fetchNextGuardedBlock();
176
                                add = false;
177
                                modElementFinished = true;
178
                            } else if (blockCompare.overlapStart()) {
179
                                endOffset = guardedBlockStartPos.getOffset();
180
                                add = true;
181
                                modElementFinished = true;
182
                            } else if (blockCompare.overlapEnd()) {
183
                                // Skip part covered by guarded block
184
                                endOffset = guardedBlockEndPos.getOffset();
185
                                fetchNextGuardedBlock();
186
                                add = false;
187
                                modElementFinished = false;
188
                            } else if (blockCompare.contains()) {
189
                                endOffset = guardedBlockStartPos.getOffset();
190
                                add = true;
191
                                modElementFinished = false;
192
                            } else if (blockCompare.inside()) {
193
                                add = false;
194
                                modElementFinished = true;
195
                            } else {
196
                                LOG.info("Unexpected blockCompare=" + blockCompare);
197
                                add = false;
198
                                modElementFinished = true;
199
                            }
200
                        } else {
201
                            add = true;
202
                            modElementFinished = true;
203
                        }
204
                        if (add) {
205
                            try {
206
                                if (startOffset != endOffset) {
207
                                    PositionRegion block = new PositionRegion(doc, startOffset, endOffset);
208
                                    if (LOG.isLoggable(Level.FINE)) {
209
                                        LOG.fine("Reformat-at-save: add block=" + block);
210
                                    }
211
                                    formatBlocks.add(block);
212
                                }
213
                            } catch (BadLocationException ex) {
214
                                Exceptions.printStackTrace(ex);
215
                            }
216
                        }
217
                        startOffset = endOffset;
218
                        endOffset = modElementEndOffset;
219
                    } while (!modElementFinished);
220
                }
221
222
                try {
223
                    for (PositionRegion block : formatBlocks) {
224
                        reformat.reformat(block.getStartOffset(), block.getEndOffset());
225
                    }
226
                } catch (Exception ex) {
227
                    Exceptions.printStackTrace(ex);
228
                }
229
230
            } finally {
231
                modRootElement.setEnabled(origEnabled);
232
            }
119
        }
233
        }
120
    }
234
    }
121
235
122
    private static final class LockInfo {
236
    private void fetchNextGuardedBlock() {
123
        
237
        if (guardedBlockIndex < guardedBlocks.size()) {
124
        final Reformat reformat;
238
            PositionRegion guardedBlock = guardedBlocks.get(guardedBlockIndex++);
239
            guardedBlockStartPos = guardedBlock.getStartPosition();
240
            guardedBlockEndPos = guardedBlock.getEndPosition();
241
        } else {
242
            guardedBlockEndPos = guardedBlockStartPos = null;
243
        }
244
    }
125
245
126
        final boolean modifiedLinesOnly;
246
    @MimeRegistration(mimeType="", service=OnSaveTask.Factory.class, position=500)
127
        
247
    public static final class FactoryImpl implements Factory {
128
        List<PositionRegion> guardedBlocks;
129
248
130
        int guardedBlockIndex;
249
        @Override
131
250
        public OnSaveTask createTask(Context context) {
132
        Position guardedBlockStartPos;
251
            return new ReformatBeforeSaveTask(context.getDocument());
133
134
        Position guardedBlockEndPos;
135
136
137
        public LockInfo(Reformat reformat, boolean modifiedLinesOnly) {
138
            this.reformat = reformat;
139
            this.modifiedLinesOnly = modifiedLinesOnly;
140
        }
141
142
        void reformat(Document doc) {
143
            ModRootElement modRootElement = ModRootElement.get(doc);
144
            if (modRootElement != null) {
145
                boolean origEnabled = modRootElement.isEnabled();
146
                modRootElement.setEnabled(false);
147
                try {
148
                    // Read all guarded blocks
149
                    guardedBlocks = new GapList<PositionRegion>();
150
                    if (doc instanceof GuardedDocument) {
151
                        MarkBlock block = ((GuardedDocument)doc).getGuardedBlockChain().getChain();
152
                        while (block != null) {
153
                            try {
154
                                guardedBlocks.add(new PositionRegion(doc, block.getStartOffset(), block.getEndOffset()));
155
                            } catch (BadLocationException ex) {
156
                                Exceptions.printStackTrace(ex);
157
                            }
158
                            block = block.getNext();
159
                        }
160
161
                    }
162
163
                    guardedBlockIndex = 0;
164
                    fetchNextGuardedBlock();
165
                    Element modRootOrDocElement = (modifiedLinesOnly)
166
                            ? modRootElement
167
                            : DocumentInternalUtils.customElement(doc, 0, doc.getLength());
168
                    int modElementCount = modRootOrDocElement.getElementCount();
169
                    List<PositionRegion> formatBlocks = new ArrayList<PositionRegion>(modElementCount);
170
                    for (int i = 0; i < modElementCount; i++) {
171
                        Element modElement = modRootOrDocElement.getElement(i);
172
                        boolean modElementFinished;
173
                        boolean add;
174
                        int startOffset = modElement.getStartOffset();
175
                        int modElementEndOffset = modElement.getEndOffset();
176
                        int endOffset = modElementEndOffset;
177
                        do {
178
                            if (guardedBlockStartPos != null) {
179
                                BlockCompare blockCompare = BlockCompare.get(
180
                                        startOffset,
181
                                        endOffset,
182
                                        guardedBlockStartPos.getOffset(),
183
                                        guardedBlockEndPos.getOffset());
184
                                if (blockCompare.before()) {
185
                                    add = true;
186
                                    modElementFinished = true;
187
                                } else if (blockCompare.after()) {
188
                                    fetchNextGuardedBlock();
189
                                    add = false;
190
                                    modElementFinished = false;
191
                                } else if (blockCompare.equal()) {
192
                                    fetchNextGuardedBlock();
193
                                    add = false;
194
                                    modElementFinished = true;
195
                                } else if (blockCompare.overlapStart()) {
196
                                    endOffset = guardedBlockStartPos.getOffset();
197
                                    add = true;
198
                                    modElementFinished = true;
199
                                } else if (blockCompare.overlapEnd()) {
200
                                    // Skip part covered by guarded block
201
                                    endOffset = guardedBlockEndPos.getOffset();
202
                                    fetchNextGuardedBlock();
203
                                    add = false;
204
                                    modElementFinished = false;
205
                                } else if (blockCompare.contains()) {
206
                                    endOffset = guardedBlockStartPos.getOffset();
207
                                    add = true;
208
                                    modElementFinished = false;
209
                                } else if (blockCompare.inside()) {
210
                                    add = false;
211
                                    modElementFinished = true;
212
                                } else {
213
                                    LOG.info("Unexpected blockCompare=" + blockCompare);
214
                                    add = false;
215
                                    modElementFinished = true;
216
                                }
217
                            } else {
218
                                add = true;
219
                                modElementFinished = true;
220
                            }
221
                            if (add) {
222
                                try {
223
                                    if (startOffset != endOffset) {
224
                                        PositionRegion block = new PositionRegion(doc, startOffset, endOffset);
225
                                        if (LOG.isLoggable(Level.FINE)) {
226
                                            LOG.fine("Reformat-at-save: add block=" + block);
227
                                        }
228
                                        formatBlocks.add(block);
229
                                    }
230
                                } catch (BadLocationException ex) {
231
                                    Exceptions.printStackTrace(ex);
232
                                }
233
                            }
234
                            startOffset = endOffset;
235
                            endOffset = modElementEndOffset;
236
                        } while (!modElementFinished);
237
                    }
238
239
                    try {
240
                        for (PositionRegion block : formatBlocks) {
241
                            reformat.reformat(block.getStartOffset(), block.getEndOffset());
242
                        }
243
                    } catch (Exception ex) {
244
                        Exceptions.printStackTrace(ex);
245
                    }
246
247
                } finally {
248
                    modRootElement.setEnabled(origEnabled);
249
                }
250
            }
251
        }
252
253
        private void fetchNextGuardedBlock() {
254
            if (guardedBlockIndex < guardedBlocks.size()) {
255
                PositionRegion guardedBlock = guardedBlocks.get(guardedBlockIndex++);
256
                guardedBlockStartPos = guardedBlock.getStartPosition();
257
                guardedBlockEndPos = guardedBlock.getEndPosition();
258
            } else {
259
                guardedBlockEndPos = guardedBlockStartPos = null;
260
            }
261
        }
262
263
        void unlock() {
264
            reformat.unlock();
265
        }
252
        }
266
253
267
    }
254
    }

Return to bug 217904