|
What this is
Other links
The source code/* * Sun Public License Notice * * The contents of this file are subject to the Sun Public License * Version 1.0 (the "License"). You may not use this file except in * compliance with the License. A copy of the License is available at * http://www.sun.com/ * * The Original Code is NetBeans. The Initial Developer of the Original * Code is Sun Microsystems, Inc. Portions Copyright 1997-2004 Sun * Microsystems, Inc. All Rights Reserved. */ package org.netbeans.modules.refactoring.api; import java.lang.reflect.Modifier; import org.netbeans.jmi.javamodel.*; import org.netbeans.modules.javacore.jmiimpl.javamodel.MethodImpl; import org.netbeans.modules.refactoring.CheckUtils; import org.netbeans.modules.refactoring.NbAbstractRefactoring; import org.netbeans.modules.refactoring.api.Problem; import org.openide.text.PositionBounds; import org.openide.util.NbBundle; import org.openide.util.Utilities; import javax.jmi.reflect.RefObject; import java.text.MessageFormat; import java.util.*; import java.util.ArrayList; import java.util.List; import org.netbeans.modules.javacore.internalapi.JavaMetamodel; import org.netbeans.modules.javacore.internalapi.ProgressListener; import org.netbeans.modules.javacore.jmiimpl.javamodel.CallableFeatureImpl; /** * Refactoring used for changing method signature. It changes method declaration * and also all its references (callers). * * @author Pavel Flaska * @author Tomas Hurka */ public class ChangeParameters extends NbAbstractRefactoring implements ProgressListener { RefObject selectedObject; // refatorected object - method or constructor CallableFeature method; // table of all the changes - it contains all the new parameters and also // changes in order ParameterInfo[] paramTable; // new modifier int modifier; /** * Creates a new instance of change parameters refactoring. * * @param method refactored object, i.e. method or constructor */ public ChangeParameters(RefObject method) { selectedObject = method; } public void setClassPath() { if (method != null && method instanceof Method) { Collection c = ((MethodImpl) method).getOverridenMethods(); if (!c.isEmpty()) { setClassPath(c); return; } } setClassPath((Element) selectedObject); } /** * Returns list of problems. For the change method signature, there are two * possible warnings - if the method is overriden or if it overrides * another method. * * @return overrides or overriden problem or both */ public Problem preCheck() { Problem result = null; if (!(selectedObject instanceof CallableFeature)) { return createProblem(result, true, NbBundle.getMessage(ChangeParameters.class, "ERR_ChangeParamsWrongType")); } Collection overridesMethod = null; Collection overridenMethod = null; method = (CallableFeature)selectedObject; // for the method, check, if the method overrides another method and // if the method is overriden by another method // todo (#pf): what about constructors? List pars = (List) method.getParameters(); List typeList = new ArrayList(); for (Iterator parIt = pars.iterator(); parIt.hasNext(); ) { Parameter par = (Parameter) parIt.next(); typeList.add(par.getType()); } if (method instanceof Method) { overridesMethod = CheckUtils.overrides((Method) method, method.getName(), typeList, true); overridenMethod = CheckUtils.isOverridden((Method) method, method.getName(), typeList); } // for 4.0, we not support methods with variable arguments. It is a // fatal error for the time being. if (CheckUtils.hasVarArgs(method)) { String msg = getString("ERR_HasVarArg");// NOI18N result = createProblem(result, true, msg); } if (overridesMethod != null) { for (Iterator iter = overridesMethod.iterator(); iter.hasNext(); ){ String msg = new MessageFormat(getString("ERR_MethodOverrides")).format( // NOI18N new Object[] { getDefClassName(((Method) iter.next()).getDeclaringClass()) } ); result = createProblem(result, false, msg); } } if (overridenMethod != null) { for (Iterator iter = overridenMethod.iterator(); iter.hasNext(); ){ String msg = new MessageFormat(getString("ERR_MethodIsOverridden")).format( // NOI18N new Object[] { getDefClassName(((Method) iter.next()).getDeclaringClass()) } ); result = createProblem(result, false, msg); } } return result; } public Problem checkParameters(ParameterInfo[] aParamTable, int modifier) { ParameterInfo[] oldParamTable = paramTable; int oldModifier = this.modifier; paramTable = aParamTable; this.modifier = modifier; // checks Problem emptyArgs = checkParameterAttributes(); // Note (#pf): little hacking - because of reverse problems sorting // (last fatal reported is first in the chain, not first fatal reported), // we have to use two problem chains. First for empty arguments and // second for invalid identifiers. Then they are joined together // at the end of method. Problem result = null; // check, if there is is varArg. Check also the parameter's name clash Set paramNames = new HashSet(3); for (int i = 0; i < paramTable.length; i++) { Type type = paramTable[i].getType(); if (type != null && type.getName() != null && type.getName().endsWith("...")) { // NOI18N // varargs are not supported in change parameters in 4.0. result = createProblem(result, true, getString("ERR_HasVarArg")); } String name = null; int orIdx = paramTable[i].getOrigIndex(); if (orIdx > -1) { // existing parameter name = ((Parameter) method.getParameters().get(orIdx)).getName(); } else { // new parameter name = paramTable[i].getName(); } if (name != null && paramNames.add(name) == false) { // set already contains the same name! result = createProblem(result, true, new MessageFormat(getString("ERR_DuplicateName")).format( // NOI18N new Object[] { name } )); } } // if there exists duplicate method (i.e. the method with the // name and parameters, create fatal problem - be careful and // do not create problem when found method is the same - // example: you have method 'int swap(int a, int b)'. When // user only swaps parameters in the method (i.e. new signature is // 'swap(int b, int a)', you have to allow him to do it! if (method instanceof Method) { Method duplicateMethod = methodClashes(paramTable); if (duplicateMethod != null && !method.equals(duplicateMethod)) { result = createProblem(result, true, new MessageFormat(getString("ERR_existingMethod")).format( // NOI18N new Object[] { duplicateMethod.getName(), getDefClassName(duplicateMethod.getDeclaringClass()) } ) ); } // otherwise it has to be constructor } else { Constructor duplicateConstr = constructorClashes(paramTable); if (duplicateConstr != null && !method.equals(duplicateConstr)) { result = createProblem(result, true, new MessageFormat(getString("ERR_existingConstr")).format( // NOI18N new Object[] { getDefClassName(duplicateConstr.getDeclaringClass()) } ) ); } } // sorting problems in correct order (different of createProblem method) if (emptyArgs != null) { Problem toAdd = emptyArgs; while (toAdd.getNext() != null) { toAdd = toAdd.getNext(); } toAdd.setNext(result); result = toAdd; } paramTable=oldParamTable; this.modifier=oldModifier; return result; } /** * Sets the parameters for refactoring. You can add new parameters or change * order of parameters or change existing parameter. * * @param aParamTable table representing new parameters list * @param modifier new modifier for the method * * @return problem or a chain of problems */ public Problem setParameters(ParameterInfo[] aParamTable, int modifier) { paramTable = aParamTable; this.modifier = modifier; return checkParameters(aParamTable, modifier); } /** * Prepares the collection of refactoring elements. It will be add * to elements collection, which is provided as a parameter. * * @param elements collection to which the elements will be append t * @return list of problems */ public Problem prepare(Collection elements) { JavaMetamodel.getManager().getProgressSupport().addProgressListener(this); fireProgressListenerStart(PREPARE, 9); try { fireProgressListenerStep(); // get all the callers and usages of the callable and add them // the the collection of refactored elements referencesIterator = ((CallableFeatureImpl) method).findDependencies(true, true, true).iterator(); if (method instanceof Constructor) { // we have to add declaration element in case of constructor. // For methods, usages return original declaration because they // are looking for overriden/overriding methods. (not case of // constructor) elements.add(new SignatureElement(method, paramTable, modifier)); } while (referencesIterator.hasNext()) { if (cancelRequest) { return null; } Object ref = referencesIterator.next(); if (ref instanceof Invocation) { // callers elements.add(new CallerElement((Invocation) ref, paramTable)); } else { // declaration/declarations (in case of overriden or overrides) elements.add(new SignatureElement((CallableFeature) ref, paramTable, modifier)); } } return null; } finally { referencesIterator = null; JavaMetamodel.getManager().getProgressSupport().removeProgressListener(this); fireProgressListenerStop(); } } //////////////////////////////////////////////////////////////////////////// // INNER CLASSES //////////////////////////////////////////////////////////////////////////// /** * Represents one item for setParameters(List params) list parameter. * Item contains information about changes in method parameters. * Parameter can be added, changed or moved to another position. */ public static class ParameterInfo { int origIndex; String name; Type type; String defaultVal; /** * Creates a new instanceof of ParameterInfo. This constructor can be * used for newly added parameters or changed original parameters. * When you call method with -1 origIndex, you have to provide not * null values in all other pamarameters, otherwise it throws an * IllegalArgumentException. * * @param origIndex for newly added parameters, use -1, otherwise * use index in original parameters list * @param name parameter name * @param type parameter type * @param defaultVal should be provided for the all new parameters. * For changed parameters, it is ignored. */ public ParameterInfo(int origIndex, String name, Type type, String defaultVal) { // new parameter // if (origIndex == -1 && (name == null || defaultVal == null || type == null || name.length() == 0 || defaultVal.length() == 0)) { // throw new IllegalArgumentException(NbBundle.getMessage(ChangeParameters.class, "ERR_NoValues")); // } this.origIndex = origIndex; this.name = name; this.type = type; // do not set default value for existing parameters this.defaultVal = origIndex == -1 ? defaultVal : null; } /** * Creates a new instance of ParameterInfo. This constructor is used * for existing non-changed parameters. All the values except original * position in parameters list is set to null. * * @param origIndex position index in original parameters list */ public ParameterInfo(int origIndex) { this(origIndex, null, null, null); } /** * Returns value of original parameter index. * * @return original index of parameter in parameters list */ public int getOrigIndex() { return origIndex; } /** * Returns value of the name of parameter. If the name was not * changed, returns null. * * @return new name for parameter or null in case that it was not changed. */ public String getName() { return name; } /** * Returns value of the type of parameter. If the name was not * changed, returns null. * * @return new type for parameter or null if it was not changed. */ public Type getType() { return type; } /** * Returns value of the default value in case of the new parameter. * Otherwise, it returns null. * * @return default value for new parameter, otherwise null. */ public String getDefaultVal() { return defaultVal; } } //////////////////////////////////////////////////////////////////////////// /** * Instance of this class represents call to refactored method, i.e. method, * whose signature is changed. */ static class CallerElement implements RefactoringElement { private PositionBounds bounds; private boolean enabled; private final Invocation element; private ParameterInfo[] paramTable; private final String text; private final String displayText; private int status; // contains bounds for all original parameters. Used for obtaining original // parameter value, when the parameter was moved to another place. private List paramList; // cache private RefObject comp = null; /** * Creates new CallerElement. It represents Invocation to be * refactored when signature is changed. * * @param element represents method invocation * @param paramTable array of reordered, changed and added parameters */ public CallerElement(Invocation element, ParameterInfo[] paramTable) { paramList = Collections.EMPTY_LIST; this.element = element; this.paramTable = paramTable; this.bounds = null; enabled = true; Element disp = getDisplayElement(element); int b = disp.getStartOffset(); int e = disp.getEndOffset(); int bb = element.getPartStartOffset(ElementPartKindEnum.NAME); int be = element.getEndOffset(); String src = element.getResource().getSourceText(); text = src.substring(b, e); displayText = src.substring(b, bb) + "" + src.substring(bb, be) + "" + src.substring(be, e); //NOI18N if (JavaMetamodel.getManager().isElementGuarded(element)) { status = RefactoringElement.GUARDED; } else { status = RefactoringElement.NORMAL; } } /** * Returns the text describing the change provided by element. * * @return the text describing element functionality */ public String getText() { return text; } /** * Returns text containing the description of the element * (i.e. 'Change declaration' and the current declaration of the method. * * @return description text with method declaration change text */ public String getDisplayText() { return displayText; } /** * If the refactoring of this element is enabled, it returns true. * * @return true, if the refactoring of this element is enabled */ public boolean isEnabled() { return enabled; } /** * Sets the enabled flag. It allows to stop refactoring on the * element, when you call setEnabled(false); * * @param enabled tell if the element can be refactored */ public void setEnabled(boolean enabled) { this.enabled = enabled; } /** * Performs change on element. It is method invocation, it goes through * the parameters provide in constructor in an array and creates new * parameters in model. */ public void performChange() { List parameters = element.getParameters(); Element[] origParameters = (Element[]) parameters.toArray(new Element[0]); parameters.clear(); for (int i = 0; i < paramTable.length; i++) { ParameterInfo parInfo = paramTable[i]; int origIndex = parInfo.getOrigIndex(); Element par; if (origIndex == -1) { JavaModelPackage jmp = (JavaModelPackage) getJavaElement().refImmediatePackage(); par = jmp.getMultipartId().createMultipartId(parInfo.getDefaultVal(), null, null); } else { par = origParameters[origIndex]; } parameters.add(par); } } /** * Do undo on the element. */ public void undoChange() { throw new UnsupportedOperationException(); } /** * Returns Java element associated with this refactoring element. * * @return MDR Java element. */ public Element getJavaElement() { if (comp == null) { comp = element; while (!((comp instanceof Feature) || (comp instanceof Resource))) { comp = (RefObject) comp.refImmediateComposite(); } } return (Element) comp; } /** * * @return bounds bordering the element */ public PositionBounds getPosition() { if (bounds == null) { bounds = getPositionBounds(element); } return bounds; } public int getStatus() { return status; } //////////////////////////////////////////////////////////////////////////// // PRIVATE MEMBERS //////////////////////////////////////////////////////////////////////////// private static Element getDisplayElement(Element obj) { Element result = obj; while (!((result instanceof Feature) || (result.refImmediateComposite() instanceof StatementBlock))) { result = (Element) result.refImmediateComposite(); } return result; } } //////////////////////////////////////////////////////////////////////////// /** * Instance of this class is part of the change method signature refactoring. * It is responsible for changing method declaration - it doesn't change * references. */ static class SignatureElement implements RefactoringElement { private PositionBounds bounds; private final CallableFeature element; boolean enabled; private ParameterInfo[] paramTable; private int modifier; private final String displayText; private final String text; private int status; /** * Creates refactoring elements, which is responsible for changing * method (constructor) declaration. * * @param element element, which will be changed * @param paramTable array of Lists, which contains information about * parameters changes * @param modifier contains information about modifier change */ public SignatureElement(CallableFeature element, ParameterInfo[] paramTable, int modifier) { this.element = element; this.paramTable = paramTable; this.bounds = null; this.modifier = modifier; enabled = true; // initialize text and displayText String decl = ChangeParameters.getString("LBL_chngsigdecl"); //NOI18N Object[] args = new Object[2]; String key = element instanceof Method ? "LBL_Method" : "LBL_Constructor"; //NOI18N args[0] = ChangeParameters.getString(key); int b = element.getPartStartOffset(ElementPartKindEnum.HEADER); int e = element.getPartEndOffset(ElementPartKindEnum.HEADER); args[1] = element.getResource().getSourceText().substring(b, e); text = MessageFormat.format(decl, args); args[1] = "" + args[1] + ""; //NOI18N displayText = MessageFormat.format(decl, args); if (JavaMetamodel.getManager().isElementGuarded(element)) { status = RefactoringElement.GUARDED; } else { status = RefactoringElement.NORMAL; } } /** * Returns the text describing the change provided by element. * * @return the text describing element functionality */ public String getText() { return text; } /** * Returns text containing the description of the element * (i.e. 'Change declaration' and the current declaration of the method. * * @return description text with method declaration change text */ public String getDisplayText() { return displayText; } /** * If the refactoring of this element is enabled, it returns true. * * @return true, if the refactoring of this element is enabled */ public boolean isEnabled() { return enabled; } /** * Sets the enabled flag. It allows to stop refactoring on the * element, when you call setEnabled(false); * * @param enabled tell if the element can be refactored */ public void setEnabled(boolean enabled) { this.enabled = enabled; } /** * Performs the change on header. Change the parameter if the modifier * was change and also do the parameters changes in header. */ public void performChange() { List parameters = element.getParameters(); Parameter[] origParameters = (Parameter[]) parameters.toArray(new Parameter[0]); // set new access modifier int oldMod = element.getModifiers(); if ((oldMod & modifier) == 0) { int newMod = oldMod & ~(Modifier.PUBLIC | Modifier.PROTECTED | Modifier.PRIVATE) | modifier; element.setModifiers(newMod); } parameters.clear(); JavaModelPackage modelPackage = (JavaModelPackage) getJavaElement().refImmediatePackage(); for (int i = 0; i < paramTable.length; i++) { ParameterInfo parInfo = paramTable[i]; int origIndex = parInfo.getOrigIndex(); Parameter parameter = null; // for the new parameter if (origIndex == -1) { parameter = modelPackage.getParameter().createParameter(); parameter.setName(parInfo.getName()); parameter.setType(parInfo.getType()); } else { parameter = origParameters[origIndex]; Object val; if ((val = parInfo.getName()) != null) parameter.setName((String) val); if ((val = parInfo.getType()) != null) parameter.setType((Type) val); } parameters.add(parameter); } } /** * Do undo on the element. */ public void undoChange() { throw new UnsupportedOperationException(); } /** * Returns refactored java element. (method or constructor) * * @return java element, which is refactored. It is always * CallableFeature, i.e. method or constructor . */ public Element getJavaElement() { return element; } /** * Returns bounds of the declarion, e.g. testMethod(int a, int b). * * @return bounds of the method declaration */ public PositionBounds getPosition() { if (bounds == null) { bounds = getPositionBounds(element); } return bounds; } public int getStatus() { return status; } } //////////////////////////////////////////////////////////////////////////// // PRIVATE MEMBERS //////////////////////////////////////////////////////////////////////////// private Problem checkParameterAttributes() { // check, if the default values for all new added parameters are present Problem p = null; for (int i = 0; i < paramTable.length; i++) { int origIndex = paramTable[i].getOrigIndex(); // check parameter name String s; s = paramTable[i].getName(); if (origIndex == -1 && (s == null || s.length() < 1)) p = createProblem(p, true, newParMessage("ERR_parname")); // NOI18N // check parameter type Type t = paramTable[i].getType(); if (origIndex == -1 && t == null) p = createProblem(p, true, newParMessage("ERR_partype")); // NOI18N // check the default value s = paramTable[i].getDefaultVal(); if (origIndex == -1 && (s == null || s.length() < 1)) p = createProblem(p, true, newParMessage("ERR_pardefv")); // NOI18N } return p; } private Method methodClashes(ParameterInfo[] parInfo) { // check, if there is any existing method with the same signature if (!(method instanceof Method)) return null; List paramTypes = new ArrayList(parInfo.length); List parameters = method.getParameters(); for (int i = 0; i < parInfo.length; i++) { Type t = parInfo[i].getType(); if (t == null) t = ((Parameter) parameters.get(parInfo[i].getOrigIndex())).getType(); paramTypes.add(t); } return CheckUtils.getMethod(method.getDeclaringClass(), method.getName(), paramTypes); } private Constructor constructorClashes(ParameterInfo[] parInfo) { // check, if there is any existing constructor with the same signature if (!(method instanceof Constructor)) return null; List paramTypes = new ArrayList(parInfo.length); List parameters = method.getParameters(); for (int i = 0; i < parInfo.length; i++) { Type t = parInfo[i].getType(); if (t == null) t = ((Parameter) parameters.get(parInfo[i].getOrigIndex())).getType(); paramTypes.add(t); } return CheckUtils.getConstructor(method.getDeclaringClass(), paramTypes); } private static String newParMessage(String par) { return new MessageFormat( getString("ERR_newpar")).format(new Object[] { getString(par) } // NOI18N ); } private static String getString(String key) { return NbBundle.getMessage(ChangeParameters.class, key); } private String getDefClassName(ClassDefinition dc) { if (dc instanceof JavaClass) { return ((JavaClass) dc).getName(); } else { return ""; } } public void start(org.netbeans.modules.javacore.internalapi.ProgressEvent event) { fireProgressListenerStart(event.getOperationType(), event.getCount()); } public void step(org.netbeans.modules.javacore.internalapi.ProgressEvent event) { fireProgressListenerStep(); } public void stop(org.netbeans.modules.javacore.internalapi.ProgressEvent event) { fireProgressListenerStop(); } // end private members } |
... this post is sponsored by my books ... | |
#1 New Release! |
FP Best Seller |
Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.
A percentage of advertising revenue from
pages under the /java/jwarehouse
URI on this website is
paid back to open source projects.