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: package org.netbeans.modules.refactoring.java.plugins;
042:
043: import java.io.IOException;
044: import java.text.MessageFormat;
045: import java.util.*;
046: import javax.lang.model.element.*;
047: import javax.lang.model.type.TypeMirror;
048: import org.netbeans.api.java.source.*;
049: import org.netbeans.api.java.source.SourceUtils;
050: import org.netbeans.api.java.source.TreePathHandle;
051: import org.netbeans.modules.refactoring.api.*;
052: import org.netbeans.modules.refactoring.java.RetoucheUtils;
053: import org.netbeans.modules.refactoring.java.api.ChangeParametersRefactoring;
054: import org.netbeans.modules.refactoring.java.api.ChangeParametersRefactoring.ParameterInfo;
055: import org.netbeans.modules.refactoring.java.spi.JavaRefactoringPlugin;
056: import org.netbeans.modules.refactoring.spi.RefactoringElementsBag;
057: import org.openide.filesystems.FileObject;
058: import org.openide.util.NbBundle;
059: import org.openide.util.Utilities;
060:
061: /**
062: * Refactoring used for changing method signature. It changes method declaration
063: * and also all its references (callers).
064: *
065: * @author Pavel Flaska
066: * @author Tomas Hurka
067: * @author Jan Becicka
068: */
069: public class ChangeParametersPlugin extends JavaRefactoringPlugin {
070:
071: private ChangeParametersRefactoring refactoring;
072: private TreePathHandle treePathHandle;
073:
074: /**
075: * Creates a new instance of change parameters refactoring.
076: *
077: * @param method refactored object, i.e. method or constructor
078: */
079: public ChangeParametersPlugin(
080: ChangeParametersRefactoring refactoring) {
081: this .refactoring = refactoring;
082: this .treePathHandle = refactoring.getRefactoringSource()
083: .lookup(TreePathHandle.class);
084: }
085:
086: @Override
087: public Problem checkParameters() {
088: //TODO:
089: return null;
090: }
091:
092: @Override
093: public Problem fastCheckParameters(CompilationController javac)
094: throws IOException {
095: javac.toPhase(JavaSource.Phase.RESOLVED);
096: ParameterInfo paramTable[] = refactoring.getParameterInfo();
097: Problem p = null;
098: for (int i = 0; i < paramTable.length; i++) {
099: int origIndex = paramTable[i].getOriginalIndex();
100:
101: if (origIndex == -1) {
102: // check parameter name
103: String s;
104: s = paramTable[i].getName();
105: if ((s == null || s.length() < 1))
106: p = createProblem(p, true,
107: newParMessage("ERR_parname")); // NOI18N
108: else {
109: if (!Utilities.isJavaIdentifier(s)) {
110: p = createProblem(p, true, NbBundle.getMessage(
111: ChangeParametersPlugin.class,
112: "ERR_InvalidIdentifier", s)); // NOI18N
113: }
114: }
115:
116: // check parameter type
117: String t = paramTable[i].getType();
118: if (t == null)
119: p = createProblem(p, true,
120: newParMessage("ERR_partype")); // NOI18N
121:
122: // check the default value
123: s = paramTable[i].getDefaultValue();
124: if ((s == null || s.length() < 1))
125: p = createProblem(p, true,
126: newParMessage("ERR_pardefv")); // NOI18N
127:
128: }
129: ParameterInfo in = paramTable[i];
130:
131: if (in.getType() != null && in.getType().endsWith("...")
132: && i != paramTable.length - 1) {//NOI18N
133: p = createProblem(p, true, org.openide.util.NbBundle
134: .getMessage(ChangeParametersPlugin.class,
135: "ERR_VarargsFinalPosition",
136: new Object[] {}));
137: }
138: }
139: return p;
140: }
141:
142: private static String newParMessage(String par) {
143: return new MessageFormat(getString("ERR_newpar"))
144: .format(new Object[] { getString(par) } // NOI18N
145: );
146: }
147:
148: private static String getString(String key) {
149: return NbBundle.getMessage(ChangeParametersPlugin.class, key);
150: }
151:
152: private Set<ElementHandle<ExecutableElement>> allMethods;
153:
154: private Set<FileObject> getRelevantFiles() {
155: ClasspathInfo cpInfo = getClasspathInfo(refactoring);
156: final Set<FileObject> set = new HashSet<FileObject>();
157: JavaSource source = JavaSource.create(cpInfo, refactoring
158: .getRefactoringSource().lookup(TreePathHandle.class)
159: .getFileObject());
160:
161: try {
162: source.runUserActionTask(
163: new CancellableTask<CompilationController>() {
164:
165: public void cancel() {
166: throw new UnsupportedOperationException(
167: "Not supported yet."); // NOI18N
168: }
169:
170: public void run(CompilationController info)
171: throws Exception {
172: final ClassIndex idx = info
173: .getClasspathInfo().getClassIndex();
174: info.toPhase(JavaSource.Phase.RESOLVED);
175:
176: //add all references of overriding methods
177: TreePathHandle treePathHandle = refactoring
178: .getRefactoringSource().lookup(
179: TreePathHandle.class);
180: Element el = treePathHandle
181: .resolveElement(info);
182: ElementHandle<TypeElement> enclosingType = ElementHandle
183: .create(SourceUtils
184: .getEnclosingTypeElement(el));
185: allMethods = new HashSet<ElementHandle<ExecutableElement>>();
186: allMethods.add(ElementHandle
187: .create((ExecutableElement) el));
188: for (ExecutableElement e : RetoucheUtils
189: .getOverridingMethods(
190: (ExecutableElement) el,
191: info)) {
192: set.add(SourceUtils.getFile(e, info
193: .getClasspathInfo()));
194: ElementHandle<TypeElement> encl = ElementHandle
195: .create(SourceUtils
196: .getEnclosingTypeElement(e));
197: set
198: .addAll(idx
199: .getResources(
200: encl,
201: EnumSet
202: .of(ClassIndex.SearchKind.METHOD_REFERENCES),
203: EnumSet
204: .of(ClassIndex.SearchScope.SOURCE)));
205: allMethods.add(ElementHandle.create(e));
206: }
207: //add all references of overriden methods
208: for (ExecutableElement e : RetoucheUtils
209: .getOverridenMethods(
210: (ExecutableElement) el,
211: info)) {
212: set.add(SourceUtils.getFile(e, info
213: .getClasspathInfo()));
214: ElementHandle<TypeElement> encl = ElementHandle
215: .create(SourceUtils
216: .getEnclosingTypeElement(e));
217: set
218: .addAll(idx
219: .getResources(
220: encl,
221: EnumSet
222: .of(ClassIndex.SearchKind.METHOD_REFERENCES),
223: EnumSet
224: .of(ClassIndex.SearchScope.SOURCE)));
225: allMethods.add(ElementHandle.create(e));
226: }
227: set
228: .addAll(idx
229: .getResources(
230: enclosingType,
231: EnumSet
232: .of(ClassIndex.SearchKind.METHOD_REFERENCES),
233: EnumSet
234: .of(ClassIndex.SearchScope.SOURCE)));
235: set.add(SourceUtils.getFile(el, info
236: .getClasspathInfo()));
237: }
238: }, true);
239: } catch (IOException ioe) {
240: throw (RuntimeException) new RuntimeException()
241: .initCause(ioe);
242: }
243: return set;
244: }
245:
246: public Problem prepare(RefactoringElementsBag elements) {
247: Set<FileObject> a = getRelevantFiles();
248: fireProgressListenerStart(ProgressEvent.START, a.size());
249: if (!a.isEmpty()) {
250: TransformTask transform = new TransformTask(
251: new ChangeParamsTransformer(refactoring, allMethods),
252: treePathHandle);
253: createAndAddElements(a, transform, elements, refactoring);
254: }
255: fireProgressListenerStop();
256: return null;
257: // JavaMetamodel.getManager().getProgressSupport().addProgressListener(this);
258: // Problem problem = null;
259: // try {
260: // // get the original access modifier and check, if the original
261: // // modifier is weaker than the new modifier. If so, set checkMod
262: // // to true and in following code check, if all usages will have
263: // // sufficient access privileges.
264: // int origAccessMods = method.getModifiers() & (Modifier.PUBLIC | Modifier.PROTECTED | Modifier.PRIVATE);
265: // boolean checkMod = compareModifiers(origAccessMods, modifier) == -1;
266: // // get all the callers and usages of the callable and add them
267: // // the the collection of refactored elements
268: // referencesIterator = ((CallableFeatureImpl) method).findDependencies(true, true, true).iterator();
269: // elements.add(refactoring, new SignatureElement(method, paramTable, modifier));
270: // int parNum = ((CallableFeature) refactoring.getRefactoredObject()).getParameters().size();
271: // while (referencesIterator.hasNext()) {
272: // if (cancelRequest) {
273: // return null;
274: // }
275: // Object ref = referencesIterator.next();
276: // if (ref instanceof Invocation) {
277: // // Callers. Refactored method has to have the same number
278: // // of parameters as its caller. (see issue #53041 for details)
279: // if (((Invocation) ref).getParameters().size() == parNum) {
280: // if (problem == null && checkMod) {
281: // // check the access to refactored method
282: // Feature f = JavaModelUtil.getDeclaringFeature((Invocation)ref);
283: // if (Modifier.isPrivate(modifier)) { // changing to private
284: // if (!Utilities.compareObjects(getOutermostClass(f), getOutermostClass(method))) {
285: // String msg = getString("ERR_StrongAccMod"); // NOI18N
286: // problem = new Problem(false, new MessageFormat(msg).format(new Object[] { "private" })); // NOI18N
287: // }
288: // } else if (Modifier.isProtected(modifier)) { // changing to protected
289: // if (!method.getResource().getPackageName().equals(f.getResource().getPackageName())) {
290: // String msg = getString("ERR_StrongAccMod"); // NOI18N
291: // problem = new Problem(false, new MessageFormat(msg).format(new Object[] { "protected" })); // NOI18N
292: // }
293: // } else if (modifier == 0) { // default modifier check
294: // if (!f.getResource().getPackageName().equals(method.getResource().getPackageName())) {
295: // String msg = getString("ERR_StrongAccMod"); // NOI18N
296: // problem = new Problem(false, new MessageFormat(msg).format(new Object[] { "default" })); // NOI18N
297: // }
298: // }
299: // }
300: // elements.add(refactoring, new CallerElement((Invocation) ref, paramTable));
301: // }
302: // } else {
303: // // declaration/declarations (in case of overriden or overrides)
304: // elements.add(refactoring, new SignatureElement((CallableFeature) ref, paramTable, modifier));
305: // }
306: // }
307: // return problem;
308: // }
309: // finally {
310: // referencesIterator = null;
311: // JavaMetamodel.getManager().getProgressSupport().removeProgressListener(this);
312: // }
313: }
314:
315: protected JavaSource getJavaSource(JavaRefactoringPlugin.Phase p) {
316: switch (p) {
317: case CHECKPARAMETERS:
318: case FASTCHECKPARAMETERS:
319: case PRECHECK:
320: ClasspathInfo cpInfo = getClasspathInfo(refactoring);
321: return JavaSource.create(cpInfo, treePathHandle
322: .getFileObject());
323: }
324: return null;
325: }
326:
327: /**
328: * Returns list of problems. For the change method signature, there are two
329: * possible warnings - if the method is overriden or if it overrides
330: * another method.
331: *
332: * @return overrides or overriden problem or both
333: */
334: @Override
335: public Problem preCheck(CompilationController info)
336: throws IOException {
337: fireProgressListenerStart(refactoring.PRE_CHECK, 4);
338: Problem preCheckProblem = null;
339: info.toPhase(JavaSource.Phase.RESOLVED);
340: preCheckProblem = isElementAvail(treePathHandle, info);
341: if (preCheckProblem != null) {
342: return preCheckProblem;
343: }
344: Element el = treePathHandle.resolveElement(info);
345: if (!(el.getKind() == ElementKind.METHOD || el.getKind() == ElementKind.CONSTRUCTOR)) {
346: preCheckProblem = createProblem(preCheckProblem, true,
347: NbBundle.getMessage(ChangeParametersPlugin.class,
348: "ERR_ChangeParamsWrongType"));
349: return preCheckProblem;
350: }
351:
352: FileObject fo = SourceUtils
353: .getFile(el, info.getClasspathInfo());
354: if (RetoucheUtils.isFromLibrary(el, info.getClasspathInfo())) { //NOI18N
355: preCheckProblem = createProblem(preCheckProblem, true,
356: getCannotRefactor(fo));
357: }
358:
359: if (!RetoucheUtils.isElementInOpenProject(fo)) {
360: preCheckProblem = new Problem(true, NbBundle.getMessage(
361: ChangeParametersPlugin.class,
362: "ERR_ProjectNotOpened"));
363: return preCheckProblem;
364: }
365:
366: if (SourceUtils.getEnclosingTypeElement(el).getKind() == ElementKind.ANNOTATION_TYPE) {
367: preCheckProblem = new Problem(true, NbBundle.getMessage(
368: ChangeParametersPlugin.class,
369: "ERR_MethodsInAnnotationsNotSupported"));
370: return preCheckProblem;
371: }
372:
373: for (ExecutableElement e : RetoucheUtils.getOverridenMethods(
374: (ExecutableElement) el, info)) {
375: if (RetoucheUtils.isFromLibrary(e, info.getClasspathInfo())) { //NOI18N
376: preCheckProblem = createProblem(preCheckProblem, true,
377: NbBundle.getMessage(
378: ChangeParametersPlugin.class,
379: "ERR_CannnotRefactorLibrary", el));
380: }
381: }
382:
383: fireProgressListenerStop();
384: return preCheckProblem;
385: }
386:
387: private static final String getCannotRefactor(FileObject r) {
388: return new MessageFormat(NbBundle.getMessage(
389: ChangeParametersPlugin.class, "ERR_CannotRefactorFile"))
390: .format(new Object[] { r.getName() });
391: }
392: }
|