2
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
4
* Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
6
* The contents of this file are subject to the terms of either the GNU
7
* General Public License Version 2 only ("GPL") or the Common
8
* Development and Distribution License("CDDL") (collectively, the
9
* "License"). You may not use this file except in compliance with the
10
* License. You can obtain a copy of the License at
11
* http://www.netbeans.org/cddl-gplv2.html
12
* or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
13
* specific language governing permissions and limitations under the
14
* License. When distributing the software, include this License Header
15
* Notice in each file and include the License file at
16
* nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
17
* particular file as subject to the "Classpath" exception as provided
18
* by Sun in the GPL Version 2 section of the License file that
19
* accompanied this code. If applicable, add the following below the
20
* License Header, with the fields enclosed by brackets [] replaced by
21
* your own identifying information:
22
* "Portions Copyrighted [year] [name of copyright owner]"
26
* The Original Software is NetBeans. The Initial Developer of the Original
27
* Software is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun
28
* Microsystems, Inc. All Rights Reserved.
30
* If you wish your version of this file to be governed by only the CDDL
31
* or only the GPL Version 2, indicate your decision by adding
32
* "[Contributor] elects to include this software in this distribution
33
* under the [CDDL or GPL Version 2] license." If you do not indicate a
34
* single choice of license, a recipient has the option to distribute
35
* your version of this file under either the CDDL, the GPL Version 2 or
36
* to extend the choice of license to its licensees as provided above.
37
* However, if you add GPL Version 2 code and therefore, elected the GPL
38
* Version 2 license, then the option applies only if the new code is
39
* made subject to such option by the copyright holder.
42
package org.netbeans.modules.form;
44
import java.util.ArrayList;
45
import java.util.Collection;
46
import java.util.HashMap;
47
import java.util.List;
50
import java.util.TreeSet;
51
import org.netbeans.api.editor.guards.SimpleSection;
52
import org.netbeans.api.java.source.ModificationResult;
53
import org.netbeans.modules.refactoring.api.AbstractRefactoring;
54
import org.netbeans.modules.refactoring.api.Problem;
55
import org.netbeans.modules.refactoring.spi.GuardedBlockHandler;
56
import org.netbeans.modules.refactoring.spi.GuardedBlockHandlerFactory;
57
import org.netbeans.modules.refactoring.spi.RefactoringElementImplementation;
58
import org.netbeans.modules.refactoring.spi.Transaction;
59
import org.openide.filesystems.FileObject;
62
* Used by java refactoring to delegate changes in guarded blocks. Registered
63
* in META-INF/services. Creates one GuardedBlockHandlerImpl instance per
64
* refactoring (so it can handle more forms).
68
public class GuardedBlockHandlerFactoryImpl implements GuardedBlockHandlerFactory {
70
public GuardedBlockHandlerFactoryImpl() {
73
public GuardedBlockHandler createInstance(AbstractRefactoring refactoring) {
74
RefactoringInfo refInfo = refactoring.getContext().lookup(RefactoringInfo.class);
75
return new GuardedBlockHandlerImpl(refInfo);
80
private static class GuardedBlockHandlerImpl implements GuardedBlockHandler {
81
private RefactoringInfo refInfo;
82
private Map<FileObject, GuardedBlockUpdate> guardedUpdates;
84
private boolean first = true;
86
public GuardedBlockHandlerImpl(RefactoringInfo refInfo) {
87
this.refInfo = refInfo;
90
public Problem handleChange(RefactoringElementImplementation proposedChange,
91
Collection<RefactoringElementImplementation> replacements,
92
Collection<Transaction> transactions) {
93
if (refInfo == null) {
94
return null; // unsupported
97
FileObject changedFile = proposedChange.getParentFile();
98
FormRefactoringUpdate update = refInfo.getUpdateForFile(changedFile);
99
update.setGaurdedCodeChanging(true);
101
boolean preloadForm = false;
102
boolean canRegenerate = false;
104
if (refInfo.getPrimaryFile().equals(changedFile) && refInfo.isForm()) {
105
// the change started in this form
106
switch (refInfo.getChangeType()) {
107
case VARIABLE_RENAME: // renaming field or local variable of initComponents
108
case CLASS_RENAME: // renaming form class, need to regenarate use of MyForm.this
109
case EVENT_HANDLER_RENAME: // renaming event handler - change the method and calls
111
canRegenerate = true;
114
// can't preload the form - it must be loaded and
115
// regenareted *after* moved to the new location
116
canRegenerate = true;
118
} else { // change originated in another class
120
// add the preview element for the overall guarded block change
121
// (for direct form change it was added by our plugin)
122
replacements.add(update.getPreviewElement());
125
// other changes may render the form unloadable (missing component
126
// classes), will change the .form file directly...
129
// load the form in advance to be sure it can be loaded
130
if (preloadForm && !update.prepareForm(true)) {
131
return new Problem(true, "Error loading form. Cannot update generated code.");
134
if (!canRegenerate) { // guarded block gets changed but it is not safe to load the form
135
// remember the change and modify the guarded block directly later
136
ModificationResult.Difference diff = proposedChange.getLookup().lookup(ModificationResult.Difference.class);
138
GuardedBlockUpdate gbUpdate;
139
if (guardedUpdates == null) {
140
guardedUpdates = new HashMap<FileObject, GuardedBlockUpdate>();
143
gbUpdate = guardedUpdates.get(changedFile);
145
if (gbUpdate == null) {
146
gbUpdate = new GuardedBlockUpdate(
147
update.getFormDataObject().getFormEditorSupport());
148
guardedUpdates.put(changedFile, gbUpdate);
150
gbUpdate.addChange(diff);
151
transactions.add(gbUpdate);
155
// we must add some transaction or element (even if it can be redundant)
156
// so it looks like we care about this guarded block change...
157
transactions.add(update);
166
* A transaction for updating guarded blocks directly with changes that came
167
* from java refactoring. I.e. no regenerating by form editor.
169
private static class GuardedBlockUpdate implements Transaction {
170
private FormEditorSupport formEditorSupport;
171
private List<GuardedBlockInfo> guardedInfos; // there can be multiple guarded blocks affected
173
GuardedBlockUpdate(FormEditorSupport fes) {
174
this.formEditorSupport = fes;
175
guardedInfos = new ArrayList<GuardedBlockInfo>(2);
176
guardedInfos.add(new GuardedBlockInfo(fes.getInitComponentSection()));
177
guardedInfos.add(new GuardedBlockInfo(fes.getVariablesSection()));
180
void addChange(ModificationResult.Difference diff) {
181
for (GuardedBlockInfo block : guardedInfos) {
182
if (block.containsPosition(diff)) {
183
block.addChange(diff);
189
public void commit() {
190
for (GuardedBlockInfo block : guardedInfos) {
191
String newText = block.getNewSectionText();
192
if (newText != null) {
193
formEditorSupport.getGuardedSectionManager()
194
.findSimpleSection(block.getName())
200
public void rollback() {
201
// rollback not needed - should be reverted by java refactoring as a whole file
202
/* for (GuardedBlockInfo block : guardedInfos) {
203
formEditorSupport.getGuardedSectionManager()
204
.findSimpleSection(block.getName())
205
.setText(block.originalText);
211
* Collects all changes for one guarded block.
213
private static class GuardedBlockInfo {
214
private String blockName;
215
private int originalPosition;
216
private String originalText;
219
* Represents one change in the guarded block.
221
private static class ChangeInfo implements Comparable<ChangeInfo> {
222
private int startPos;
224
private String newText;
225
ChangeInfo(int startPos, int len, String newText) {
226
this.startPos = startPos;
228
this.newText = newText;
231
public int compareTo(ChangeInfo ch) {
232
return startPos - ch.startPos;
236
private Set<ChangeInfo> changes = new TreeSet<ChangeInfo>();
238
GuardedBlockInfo(SimpleSection section) {
239
blockName = section.getName();
240
originalPosition = section.getStartPosition().getOffset();
241
originalText = section.getText();
244
boolean containsPosition(ModificationResult.Difference diff) {
245
int pos = diff.getStartPosition().getOffset();
246
return pos >= originalPosition && pos < originalPosition + originalText.length();
249
void addChange(ModificationResult.Difference diff) {
250
changes.add(new ChangeInfo(
251
diff.getStartPosition().getOffset() - originalPosition,
252
diff.getOldText() != null ? diff.getOldText().length() : 0,
260
String getNewSectionText() {
261
if (changes.size() > 0) {
262
StringBuilder buf = new StringBuilder();
264
for (ChangeInfo change : changes) {
265
buf.append(originalText.substring(lastOrigPos, change.startPos));
266
buf.append(change.newText);
267
lastOrigPos = change.startPos + change.length;
269
buf.append(originalText.substring(lastOrigPos));
270
return buf.toString();