001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041:
042: package org.netbeans.modules.form;
043:
044: import java.util.ArrayList;
045: import java.util.Collection;
046: import java.util.HashMap;
047: import java.util.List;
048: import java.util.Map;
049: import java.util.Set;
050: import java.util.TreeSet;
051: import org.netbeans.api.editor.guards.SimpleSection;
052: import org.netbeans.api.java.source.ModificationResult;
053: import org.netbeans.modules.refactoring.api.AbstractRefactoring;
054: import org.netbeans.modules.refactoring.api.Problem;
055: import org.netbeans.modules.refactoring.spi.GuardedBlockHandler;
056: import org.netbeans.modules.refactoring.spi.GuardedBlockHandlerFactory;
057: import org.netbeans.modules.refactoring.spi.RefactoringElementImplementation;
058: import org.netbeans.modules.refactoring.spi.Transaction;
059: import org.openide.filesystems.FileObject;
060:
061: /**
062: * Used by java refactoring to delegate changes in guarded blocks. Registered
063: * in META-INF/services. Creates one GuardedBlockHandlerImpl instance per
064: * refactoring (so it can handle more forms).
065: *
066: * @author Tomas Pavek
067: */
068: public class GuardedBlockHandlerFactoryImpl implements
069: GuardedBlockHandlerFactory {
070:
071: public GuardedBlockHandlerFactoryImpl() {
072: }
073:
074: public GuardedBlockHandler createInstance(
075: AbstractRefactoring refactoring) {
076: RefactoringInfo refInfo = refactoring.getContext().lookup(
077: RefactoringInfo.class);
078: return new GuardedBlockHandlerImpl(refInfo);
079: }
080:
081: // -----
082:
083: private static class GuardedBlockHandlerImpl implements
084: GuardedBlockHandler {
085: private RefactoringInfo refInfo;
086: private Map<FileObject, GuardedBlockUpdate> guardedUpdates;
087:
088: private boolean first = true;
089:
090: public GuardedBlockHandlerImpl(RefactoringInfo refInfo) {
091: this .refInfo = refInfo;
092: }
093:
094: public Problem handleChange(
095: RefactoringElementImplementation proposedChange,
096: Collection<RefactoringElementImplementation> replacements,
097: Collection<Transaction> transactions) {
098: if (refInfo == null) {
099: return null; // unsupported
100: }
101:
102: FileObject changedFile = proposedChange.getParentFile();
103: if (!RefactoringInfo.isJavaFileOfForm(changedFile)) {
104: // This guarded block does not belong to form.
105: return null;
106: }
107: FormRefactoringUpdate update = refInfo
108: .getUpdateForFile(changedFile);
109: update.setGaurdedCodeChanging(true);
110:
111: boolean preloadForm = false;
112: boolean canRegenerate = false;
113:
114: if (refInfo.getPrimaryFile().equals(changedFile)
115: && refInfo.isForm()) {
116: // the change started in this form
117: switch (refInfo.getChangeType()) {
118: case VARIABLE_RENAME: // renaming field or local variable of initComponents
119: case CLASS_RENAME: // renaming form class, need to regenarate use of MyForm.this
120: case EVENT_HANDLER_RENAME: // renaming event handler - change the method and calls
121: preloadForm = true;
122: canRegenerate = true;
123: break;
124: case CLASS_MOVE:
125: // can't preload the form - it must be loaded and
126: // regenareted *after* moved to the new location
127: canRegenerate = true;
128: }
129: } else { // change originated in another class
130: if (first) {
131: // add the preview element for the overall guarded block change
132: // (for direct form change it was added by our plugin)
133: replacements.add(update.getPreviewElement());
134: first = false;
135: }
136: // other changes may render the form unloadable (missing component
137: // classes), will change the .form file directly...
138: }
139:
140: // load the form in advance to be sure it can be loaded
141: if (preloadForm && !update.prepareForm(true)) {
142: return new Problem(true,
143: "Error loading form. Cannot update generated code.");
144: }
145:
146: if (!canRegenerate) { // guarded block gets changed but it is not safe to load the form
147: // remember the change and modify the guarded block directly later
148: ModificationResult.Difference diff = proposedChange
149: .getLookup().lookup(
150: ModificationResult.Difference.class);
151: if (diff != null) {
152: GuardedBlockUpdate gbUpdate;
153: if (guardedUpdates == null) {
154: guardedUpdates = new HashMap<FileObject, GuardedBlockUpdate>();
155: gbUpdate = null;
156: } else {
157: gbUpdate = guardedUpdates.get(changedFile);
158: }
159: if (gbUpdate == null) {
160: gbUpdate = new GuardedBlockUpdate(update
161: .getFormDataObject()
162: .getFormEditorSupport());
163: guardedUpdates.put(changedFile, gbUpdate);
164: }
165: gbUpdate.addChange(diff);
166: transactions.add(gbUpdate);
167: }
168: }
169:
170: // we must add some transaction or element (even if it can be redundant)
171: // so it looks like we care about this guarded block change...
172: transactions.add(update);
173:
174: return null;
175: }
176: }
177:
178: // -----
179:
180: /**
181: * A transaction for updating guarded blocks directly with changes that came
182: * from java refactoring. I.e. no regenerating by form editor.
183: */
184: private static class GuardedBlockUpdate implements Transaction {
185: private FormEditorSupport formEditorSupport;
186: private List<GuardedBlockInfo> guardedInfos; // there can be multiple guarded blocks affected
187:
188: GuardedBlockUpdate(FormEditorSupport fes) {
189: this .formEditorSupport = fes;
190: guardedInfos = new ArrayList<GuardedBlockInfo>(2);
191: guardedInfos.add(new GuardedBlockInfo(fes
192: .getInitComponentSection()));
193: guardedInfos.add(new GuardedBlockInfo(fes
194: .getVariablesSection()));
195: }
196:
197: void addChange(ModificationResult.Difference diff) {
198: for (GuardedBlockInfo block : guardedInfos) {
199: if (block.containsPosition(diff)) {
200: block.addChange(diff);
201: break;
202: }
203: }
204: }
205:
206: public void commit() {
207: for (GuardedBlockInfo block : guardedInfos) {
208: String newText = block.getNewSectionText();
209: if (newText != null) {
210: formEditorSupport.getGuardedSectionManager()
211: .findSimpleSection(block.getName())
212: .setText(newText);
213: }
214: }
215: }
216:
217: public void rollback() {
218: // rollback not needed - should be reverted by java refactoring as a whole file
219: /* for (GuardedBlockInfo block : guardedInfos) {
220: formEditorSupport.getGuardedSectionManager()
221: .findSimpleSection(block.getName())
222: .setText(block.originalText);
223: } */
224: }
225: }
226:
227: /**
228: * Collects all changes for one guarded block.
229: */
230: private static class GuardedBlockInfo {
231: private String blockName;
232: private int originalPosition;
233: private String originalText;
234:
235: /**
236: * Represents one change in the guarded block.
237: */
238: private static class ChangeInfo implements
239: Comparable<ChangeInfo> {
240: private int startPos;
241: private int length;
242: private String newText;
243:
244: ChangeInfo(int startPos, int len, String newText) {
245: this .startPos = startPos;
246: this .length = len;
247: this .newText = newText;
248: }
249:
250: public int compareTo(ChangeInfo ch) {
251: return startPos - ch.startPos;
252: }
253: }
254:
255: private Set<ChangeInfo> changes = new TreeSet<ChangeInfo>();
256:
257: GuardedBlockInfo(SimpleSection section) {
258: blockName = section.getName();
259: originalPosition = section.getStartPosition().getOffset();
260: originalText = section.getText();
261: }
262:
263: boolean containsPosition(ModificationResult.Difference diff) {
264: int pos = diff.getStartPosition().getOffset();
265: return pos >= originalPosition
266: && pos < originalPosition + originalText.length();
267: }
268:
269: void addChange(ModificationResult.Difference diff) {
270: changes.add(new ChangeInfo(diff.getStartPosition()
271: .getOffset()
272: - originalPosition,
273: diff.getOldText() != null ? diff.getOldText()
274: .length() : 0, diff.getNewText()));
275: }
276:
277: String getName() {
278: return blockName;
279: }
280:
281: String getNewSectionText() {
282: if (changes.size() > 0) {
283: StringBuilder buf = new StringBuilder();
284: int lastOrigPos = 0;
285: for (ChangeInfo change : changes) {
286: buf.append(originalText.substring(lastOrigPos,
287: change.startPos));
288: if (change.newText != null) {
289: buf.append(change.newText);
290: }
291: lastOrigPos = change.startPos + change.length;
292: }
293: buf.append(originalText.substring(lastOrigPos));
294: return buf.toString();
295: } else {
296: return null;
297: }
298: }
299: }
300:
301: }
|