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.io.IOException;
045: import javax.lang.model.element.Element;
046: import javax.lang.model.element.ElementKind;
047: import javax.lang.model.element.ExecutableElement;
048: import org.netbeans.api.fileinfo.NonRecursiveFolder;
049: import org.netbeans.api.java.classpath.ClassPath;
050: import org.netbeans.api.java.source.CancellableTask;
051: import org.netbeans.api.java.source.CompilationController;
052: import org.netbeans.api.java.source.JavaSource;
053: import org.netbeans.api.java.source.TreePathHandle;
054: import org.netbeans.modules.refactoring.api.AbstractRefactoring;
055: import org.netbeans.modules.refactoring.api.MoveRefactoring;
056: import org.netbeans.modules.refactoring.api.Problem;
057: import org.netbeans.modules.refactoring.api.RenameRefactoring;
058: import org.netbeans.modules.refactoring.api.SafeDeleteRefactoring;
059: import org.netbeans.modules.refactoring.api.SingleCopyRefactoring;
060: import org.netbeans.modules.refactoring.spi.RefactoringElementsBag;
061: import org.netbeans.modules.refactoring.spi.RefactoringPlugin;
062: import org.netbeans.modules.refactoring.spi.RefactoringPluginFactory;
063: import org.openide.filesystems.FileObject;
064: import org.openide.util.Exceptions;
065: import org.openide.util.Lookup;
066:
067: /**
068: * Entry point for refactoring, registered in META-INF/services. Whenever
069: * a refactoring is about to start, createInstance is called where we analyze
070: * the type of refactoring and attach RefactoringInfo object to the refactoring,
071: * which can be later accessed from various places where we prepare or perform
072: * the additional changes in forms and resources.
073: *
074: * @author Tomas Pavek
075: */
076: public class RefactoringPluginFactoryImpl implements
077: RefactoringPluginFactory {
078:
079: public RefactoringPlugin createInstance(
080: AbstractRefactoring refactoring) {
081: RefactoringInfo.ChangeType changeType = null;
082: FileObject primaryFile = null;
083: String oldName = null;
084:
085: // We must do some more analysis here, though it would be better to do
086: // it later in the plugin's prepare method, but we can't be sure the
087: // guarded handler is not called sooner from java plugin than our plugin.
088:
089: if (refactoring instanceof RenameRefactoring) {
090: Lookup sourceLookup = refactoring.getRefactoringSource();
091: FileObject file = sourceLookup.lookup(FileObject.class);
092: NonRecursiveFolder pkgFolder = sourceLookup
093: .lookup(NonRecursiveFolder.class);
094: final TreePathHandle tpHandle = sourceLookup
095: .lookup(TreePathHandle.class);
096: // assumption: if file is being renamed (even as result of renaming
097: // a class) then file != null, and if something inside the class
098: // is renamed then file == null
099:
100: if (file != null && RefactoringInfo.isJavaFile(file)) {
101: // renaming a java file within the same package
102: // (can be a form, or a component used in a form, or both)
103: if (isOnSourceClasspath(file)) {
104: changeType = RefactoringInfo.ChangeType.CLASS_RENAME;
105: primaryFile = file;
106: oldName = file.getName();
107: }
108: } else if (file == null && tpHandle != null) {
109: // renaming an element inside a java file
110: primaryFile = tpHandle.getFileObject();
111: if (RefactoringInfo.isJavaFileOfForm(primaryFile)) {
112: JavaSource source = JavaSource
113: .forFileObject(tpHandle.getFileObject());
114: final RefactoringInfo.ChangeType[] changeTypes = new RefactoringInfo.ChangeType[1];
115: final String[] oldNames = new String[1];
116: try {
117: source
118: .runUserActionTask(
119: new CancellableTask<CompilationController>() {
120: public void cancel() {
121: }
122:
123: public void run(
124: CompilationController controller)
125: throws Exception {
126: controller
127: .toPhase(JavaSource.Phase.RESOLVED);
128: Element el = tpHandle
129: .resolveElement(controller);
130: if (el != null) {
131: switch (el
132: .getKind()) {
133: case FIELD:
134: changeTypes[0] = RefactoringInfo.ChangeType.VARIABLE_RENAME;
135: break;
136: case LOCAL_VARIABLE:
137: Element parentEl = el
138: .getEnclosingElement();
139: if (parentEl
140: .getKind() == ElementKind.METHOD
141: && "initComponents"
142: .equals(parentEl
143: .getSimpleName()
144: .toString()) // NOI18N
145: && ((ExecutableElement) parentEl)
146: .getParameters()
147: .size() == 0) {
148: changeTypes[0] = RefactoringInfo.ChangeType.VARIABLE_RENAME;
149: }
150: break;
151: }
152: // [should we also check if it really matches an existing component in the form?]
153: oldNames[0] = el
154: .getSimpleName()
155: .toString();
156: }
157: }
158: }, true);
159: } catch (IOException ex) {
160: Exceptions.printStackTrace(ex);
161: }
162: changeType = changeTypes[0];
163: oldName = oldNames[0];
164: }
165: // TBD: changing a property method of a component
166: } else if (file != null && file.isFolder()) {
167: // renaming a folder (incl. subfolders)
168: if (isOnSourceClasspath(file)) {
169: changeType = RefactoringInfo.ChangeType.FOLDER_RENAME;
170: primaryFile = file;
171: oldName = file.getName();
172: }
173: } else if (pkgFolder != null) {
174: // renaming a package (without subfolders)
175: if (isOnSourceClasspath(pkgFolder.getFolder())) {
176: changeType = RefactoringInfo.ChangeType.PACKAGE_RENAME;
177: primaryFile = pkgFolder.getFolder();
178: oldName = primaryFile.getName();
179: }
180: }
181: } else if (refactoring instanceof MoveRefactoring) {
182: FileObject file = refactoring.getRefactoringSource()
183: .lookup(FileObject.class);
184: if (file != null && RefactoringInfo.isJavaFile(file)
185: && isOnSourceClasspath(file)) {
186: // moving a java file (between packages)
187: changeType = RefactoringInfo.ChangeType.CLASS_MOVE;
188: primaryFile = file;
189: ClassPath cp = ClassPath.getClassPath(file,
190: ClassPath.SOURCE);
191: oldName = cp.getResourceName(file, '.', false);
192: }
193: } else if (refactoring instanceof SingleCopyRefactoring) {
194: FileObject file = refactoring.getRefactoringSource()
195: .lookup(FileObject.class);
196: if (file != null && RefactoringInfo.isJavaFileOfForm(file)
197: && isOnSourceClasspath(file)) {
198: // moving a java file (between packages)
199: changeType = RefactoringInfo.ChangeType.CLASS_COPY;
200: primaryFile = file;
201: ClassPath cp = ClassPath.getClassPath(file,
202: ClassPath.SOURCE);
203: oldName = cp.getResourceName(file, '.', false);
204: }
205: } else if (refactoring instanceof SafeDeleteRefactoring) {
206: FileObject file = refactoring.getRefactoringSource()
207: .lookup(FileObject.class);
208: if (file != null && RefactoringInfo.isJavaFileOfForm(file)
209: && isOnSourceClasspath(file)) {
210: // deleting a form
211: changeType = RefactoringInfo.ChangeType.CLASS_DELETE;
212: primaryFile = file;
213: }
214: }
215:
216: if (changeType != null) {
217: RefactoringInfo refInfo = new RefactoringInfo(refactoring,
218: changeType, primaryFile, oldName);
219: refactoring.getContext().add(refInfo); // to be accessible to the GuardedBlockHandlerFactoryImpl
220: return new RefactoringPluginImpl(refInfo);
221: }
222: return null;
223: }
224:
225: private static boolean isOnSourceClasspath(FileObject fo) {
226: // TBD
227: return true;
228: }
229:
230: // -----
231:
232: private static class RefactoringPluginImpl implements
233: RefactoringPlugin {
234:
235: private RefactoringInfo refInfo;
236:
237: RefactoringPluginImpl(RefactoringInfo refInfo) {
238: this .refInfo = refInfo;
239: }
240:
241: public Problem preCheck() {
242: return null;
243: }
244:
245: public Problem checkParameters() {
246: return null;
247: }
248:
249: public Problem fastCheckParameters() {
250: return null;
251: }
252:
253: public void cancelRequest() {
254: }
255:
256: public Problem prepare(
257: RefactoringElementsBag refactoringElements) {
258: // even if guarded blocks are not affected directly we might want some changes
259: if (refInfo.isForm()) {
260: FormRefactoringUpdate update = refInfo
261: .getUpdateForFile(refInfo.getPrimaryFile());
262: switch (refInfo.getChangeType()) {
263: case CLASS_DELETE: // in case of delete we only backup the form file
264: refactoringElements.registerTransaction(update);
265: return null;
266: case CLASS_RENAME: // renaming form class, always needs to load - auto-i18n
267: if (!update.prepareForm(true)) {
268: return new Problem(true,
269: "Error loading form. Cannot update generated code.");
270: }
271: break;
272: // for VARIABLE_RENAME and EVENT_HANDLER_RENAME we don't know yet
273: // if they affect the form - guarded block handler will take care
274: }
275: refactoringElements.add(refInfo.getRefactoring(),
276: update.getPreviewElement());
277: refactoringElements.addFileChange(refInfo
278: .getRefactoring(), update);
279: } else if (refInfo.getChangeType() == RefactoringInfo.ChangeType.PACKAGE_RENAME
280: || refInfo.getChangeType() == RefactoringInfo.ChangeType.FOLDER_RENAME) {
281: boolean anyForm = false;
282: for (FileObject fo : refInfo.getPrimaryFile()
283: .getChildren()) {
284: if (RefactoringInfo.isJavaFileOfForm(fo)) {
285: anyForm = true;
286: FormRefactoringUpdate update = refInfo
287: .getUpdateForFile(fo);
288: refactoringElements.addFileChange(refInfo
289: .getRefactoring(), update);
290: }
291: }
292: if (anyForm) {
293: // TODO add refactoring element informing about updating references to resources in GUI forms in this package
294: }
295: }
296: return null;
297: }
298: }
299: }
|