| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| |
| package org.netbeans.modules.form.refactoring; |
| |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| import java.util.TreeSet; |
| import org.netbeans.api.editor.guards.SimpleSection; |
| import org.netbeans.api.java.source.ModificationResult; |
| import org.netbeans.modules.form.FormDataObject; |
| import org.netbeans.modules.nbform.FormEditorSupport; |
| import org.netbeans.modules.refactoring.api.AbstractRefactoring; |
| import org.netbeans.modules.refactoring.api.Problem; |
| import org.netbeans.modules.refactoring.spi.GuardedBlockHandler; |
| import org.netbeans.modules.refactoring.spi.GuardedBlockHandlerFactory; |
| import org.netbeans.modules.refactoring.spi.RefactoringElementImplementation; |
| import org.netbeans.modules.refactoring.spi.Transaction; |
| import org.openide.filesystems.FileObject; |
| |
| /** |
| * Used by java refactoring to delegate changes in guarded blocks. Registered |
| * in META-INF/services. Creates one GuardedBlockHandlerImpl instance per |
| * refactoring (so it can handle more forms). |
| * |
| * @author Tomas Pavek |
| */ |
| @org.openide.util.lookup.ServiceProvider(service=org.netbeans.modules.refactoring.spi.GuardedBlockHandlerFactory.class) |
| public class GuardedBlockHandlerFactoryImpl implements GuardedBlockHandlerFactory { |
| |
| public GuardedBlockHandlerFactoryImpl() { |
| } |
| |
| @Override |
| public GuardedBlockHandler createInstance(AbstractRefactoring refactoring) { |
| RefactoringInfo refInfo = refactoring.getContext().lookup(RefactoringInfo.class); |
| return new GuardedBlockHandlerImpl(refInfo); |
| } |
| |
| // ----- |
| |
| private static class GuardedBlockHandlerImpl implements GuardedBlockHandler { |
| private RefactoringInfo refInfo; |
| private Map<FileObject, GuardedBlockUpdate> guardedUpdates; |
| |
| private boolean first = true; |
| |
| public GuardedBlockHandlerImpl(RefactoringInfo refInfo) { |
| this.refInfo = refInfo; |
| } |
| |
| @Override |
| public Problem handleChange(RefactoringElementImplementation proposedChange, |
| Collection<RefactoringElementImplementation> replacements, |
| Collection<Transaction> transactions) { |
| if (refInfo == null) { |
| return null; // unsupported |
| } |
| |
| FileObject changedFile = proposedChange.getParentFile(); |
| if (!RefactoringInfo.isJavaFileOfForm(changedFile)) { |
| // This guarded block does not belong to form. |
| return null; |
| } |
| FormRefactoringUpdate update = refInfo.getUpdateForFile(changedFile); |
| update.setGaurdedCodeChanging(true); |
| |
| boolean preloadForm = false; |
| boolean canRegenerate = false; |
| |
| if (refInfo.containsOriginalFile(changedFile)) { |
| // the change started in this form |
| switch (refInfo.getChangeType()) { |
| case VARIABLE_RENAME: // renaming field or local variable of initComponents |
| case CLASS_RENAME: // renaming form class, need to regenarate use of MyForm.this |
| case EVENT_HANDLER_RENAME: // renaming event handler - change the method and calls |
| preloadForm = true; |
| canRegenerate = true; |
| break; |
| case CLASS_MOVE: |
| // don't preload the form here - it should be loaded and |
| // regenareted *after* moved to the new location |
| if (refInfo.getOriginalFiles().length == 1) { |
| canRegenerate = true; |
| } // otherwise it is very likely the change is caused by moving |
| // some other class used in this form - needs to be replaced |
| // without loading (and regenerating) the form |
| } |
| } else { // change originated in another class |
| if (first) { |
| // add the preview element for the overall guarded block change |
| // (for direct form change it was added by our plugin) |
| replacements.add(update.getPreviewElement()); |
| first = false; |
| } |
| // other changes may render the form unloadable (missing component |
| // classes), will change the .form file directly... |
| } |
| |
| // load the form in advance to be sure it can be loaded |
| if (preloadForm && !update.prepareForm(true)) { |
| return new Problem(true, "Error loading form. Cannot update generated code."); |
| } |
| |
| if (!canRegenerate) { // guarded block gets changed but it is not safe to load the form |
| // remember the change and modify the guarded block directly later |
| ModificationResult.Difference diff = proposedChange.getLookup().lookup(ModificationResult.Difference.class); |
| if (diff != null) { |
| GuardedBlockUpdate gbUpdate; |
| if (guardedUpdates == null) { |
| guardedUpdates = new HashMap<FileObject, GuardedBlockUpdate>(); |
| gbUpdate = null; |
| } else { |
| gbUpdate = guardedUpdates.get(changedFile); |
| } |
| if (gbUpdate == null) { |
| FormDataObject formDataObject = update.getFormDataObject(); |
| FormEditorSupport fes = (FormEditorSupport)formDataObject.getFormEditorSupport(); |
| gbUpdate = new GuardedBlockUpdate(fes); |
| guardedUpdates.put(changedFile, gbUpdate); |
| } |
| gbUpdate.addChange(diff); |
| transactions.add(gbUpdate); |
| } |
| } |
| |
| // we must add some transaction or element (even if it can be redundant) |
| // so it looks like we care about this guarded block change... |
| transactions.add(update); |
| |
| return null; |
| } |
| } |
| |
| // ----- |
| |
| /** |
| * A transaction for updating guarded blocks directly with changes that came |
| * from java refactoring. I.e. no regenerating by form editor. |
| */ |
| private static class GuardedBlockUpdate implements Transaction { |
| private FormEditorSupport formEditorSupport; |
| private List<GuardedBlockInfo> guardedInfos; // there can be multiple guarded blocks affected |
| |
| GuardedBlockUpdate(FormEditorSupport fes) { |
| this.formEditorSupport = fes; |
| guardedInfos = new ArrayList<GuardedBlockInfo>(2); |
| guardedInfos.add(new GuardedBlockInfo(fes.getInitComponentSection())); |
| guardedInfos.add(new GuardedBlockInfo(fes.getVariablesSection())); |
| } |
| |
| void addChange(ModificationResult.Difference diff) { |
| for (GuardedBlockInfo block : guardedInfos) { |
| if (block.containsPosition(diff)) { |
| block.addChange(diff); |
| break; |
| } |
| } |
| } |
| |
| @Override |
| public void commit() { |
| for (GuardedBlockInfo block : guardedInfos) { |
| String newText = block.getNewSectionText(); |
| if (newText != null) { |
| formEditorSupport.getGuardedSectionManager() |
| .findSimpleSection(block.getName()) |
| .setText(newText); |
| } |
| } |
| } |
| |
| @Override |
| public void rollback() { |
| // rollback not needed - should be reverted by java refactoring as a whole file |
| /* for (GuardedBlockInfo block : guardedInfos) { |
| formEditorSupport.getGuardedSectionManager() |
| .findSimpleSection(block.getName()) |
| .setText(block.originalText); |
| } */ |
| } |
| } |
| |
| /** |
| * Collects all changes for one guarded block. |
| */ |
| private static class GuardedBlockInfo { |
| private String blockName; |
| private int originalPosition; |
| private String originalText; |
| |
| /** |
| * Represents one change in the guarded block. |
| */ |
| private static class ChangeInfo implements Comparable<ChangeInfo> { |
| private int startPos; |
| private int length; |
| private String newText; |
| ChangeInfo(int startPos, int len, String newText) { |
| this.startPos = startPos; |
| this.length = len; |
| this.newText = newText; |
| } |
| |
| @Override |
| public int compareTo(ChangeInfo ch) { |
| return startPos - ch.startPos; |
| } |
| } |
| |
| private Set<ChangeInfo> changes = new TreeSet<ChangeInfo>(); |
| |
| GuardedBlockInfo(SimpleSection section) { |
| blockName = section.getName(); |
| originalPosition = section.getStartPosition().getOffset(); |
| originalText = section.getText(); |
| } |
| |
| boolean containsPosition(ModificationResult.Difference diff) { |
| int pos = diff.getStartPosition().getOffset(); |
| return pos >= originalPosition && pos < originalPosition + originalText.length(); |
| } |
| |
| void addChange(ModificationResult.Difference diff) { |
| changes.add(new ChangeInfo( |
| diff.getStartPosition().getOffset() - originalPosition, |
| diff.getOldText() != null ? diff.getOldText().length() : 0, |
| diff.getNewText())); |
| } |
| |
| String getName() { |
| return blockName; |
| } |
| |
| String getNewSectionText() { |
| if (changes.size() > 0) { |
| StringBuilder buf = new StringBuilder(); |
| int lastOrigPos = 0; |
| for (ChangeInfo change : changes) { |
| buf.append(originalText.substring(lastOrigPos, change.startPos)); |
| if (change.newText != null) { |
| buf.append(change.newText); |
| } |
| lastOrigPos = change.startPos + change.length; |
| } |
| buf.append(originalText.substring(lastOrigPos)); |
| return buf.toString(); |
| } else { |
| return null; |
| } |
| } |
| } |
| |
| } |