1 /*2 * The contents of this file are subject to the terms of the Common Development3 * and Distribution License (the License). You may not use this file except in4 * compliance with the License.5 *6 * You can obtain a copy of the License at http://www.netbeans.org/cddl.html7 * or http://www.netbeans.org/cddl.txt.8 * 9 * When distributing Covered Code, include this CDDL Header Notice in each file10 * and include the License file at http://www.netbeans.org/cddl.txt.11 * If applicable, add the following below the CDDL Header, with the fields12 * enclosed by brackets [] replaced by your own identifying information:13 * "Portions Copyrighted [year] [name of copyright owner]"14 *15 * The Original Software is NetBeans. The Initial Developer of the Original16 * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun17 * Microsystems, Inc. All Rights Reserved.18 */19 package org.netbeans.modules.refactoring.java.plugins;20 21 import org.netbeans.modules.refactoring.api.Problem;22 import org.netbeans.modules.refactoring.java.api.PullUpRefactoring;23 import org.netbeans.modules.refactoring.java.plugins.JavaRefactoringPlugin;24 import org.netbeans.modules.refactoring.spi.RefactoringElementsBag;25 26 27 /** Plugin that implements the core functionality of Pull Up refactoring.28 *29 * @author Martin Matula30 */31 public class PullUpRefactoringPlugin extends JavaRefactoringPlugin {32 /** Reference to the parent refactoring instance */33 private final PullUpRefactoring refactoring;34 35 /** Creates a new instance of PullUpRefactoringPlugin36 * @param refactoring Parent refactoring instance.37 */38 PullUpRefactoringPlugin(PullUpRefactoring refactoring) {39 this.refactoring = refactoring;40 }41 42 public Problem preCheck() {43 throw new UnsupportedOperationException ("Not supported yet.");44 }45 46 public Problem checkParameters() {47 throw new UnsupportedOperationException ("Not supported yet.");48 }49 50 public Problem fastCheckParameters() {51 throw new UnsupportedOperationException ("Not supported yet.");52 }53 54 public Problem prepare(RefactoringElementsBag refactoringElements) {55 throw new UnsupportedOperationException ("Not supported yet.");56 }57 58 // /** Checks pre-conditions of the refactoring.59 // * @return Problems found or <code>null</code>.60 // */61 // public Problem preCheck() {62 // // fire operation start on the registered progress listeners (3 steps)63 // fireProgressListenerStart(AbstractRefactoring.PRE_CHECK, 4);64 // try {65 // JavaClass sourceType = refactoring.getSourceType();66 // 67 // // check whether the element is valid68 // Problem result = isElementAvail(sourceType);69 // if (result != null) {70 // // fatal error -> don't continue with further checks71 // return result;72 // }73 // if (!CheckUtils.isElementInOpenProject(sourceType)) {74 // return new Problem(true, NbBundle.getMessage(JavaRefactoringPlugin.class, "ERR_ProjectNotOpened"));75 // }76 // 77 // // check whether the element is an unresolved class78 // if (sourceType instanceof UnresolvedClass) {79 // // fatal error -> return80 // return new Problem(true, NbBundle.getMessage(JavaRefactoringPlugin.class, "ERR_ElementNotAvailable")); // NOI18N81 // }82 // 83 // // increase progress (step 1)84 // fireProgressListenerStep();85 // 86 // // #1 - check if the class has any supertypes from the opened project87 // JavaClass supertypes[] = refactoring.collectSupertypes();88 // if (supertypes.length == 0) {89 // // fatal error -> return90 // return new Problem(true, NbBundle.getMessage(PullUpRefactoringPlugin.class, "ERR_PullUp_NoSuperTypes")); // NOI18N91 // }92 // 93 // // increase progress (step 2)94 // fireProgressListenerStep();95 // 96 // // #2 - check if there are any members to pull up97 // if (!hasMembers(sourceType, supertypes)) {98 // // fatal error -> return99 // return new Problem(true, NbBundle.getMessage(PullUpRefactoringPlugin.class, "ERR_PullUp_NoMembers")); // NOI18N100 // }101 // 102 // // increase progress (step 3)103 // fireProgressListenerStep();104 // 105 // // all checks passed -> return null106 // return null;107 // } finally {108 // // fire operation end on the registered progress listeners109 // fireProgressListenerStop();110 // }111 // }112 // 113 // public Problem fastCheckParameters() {114 // MemberInfo[] info = refactoring.getMembers();115 // // #1 - check whether there are any members to pull up116 // if (info.length == 0) {117 // return new Problem(true, NbBundle.getMessage(PullUpRefactoringPlugin.class, "ERR_PullUp_NoMembersSelected")); // NOI18N118 // }119 // 120 // if (info.length > 1) {121 // for (int i=0; i<info.length - 1; i++) {122 // for (int j = i + 1; j < info.length; j++) {123 // if (CheckUtils.membersEqual(info[i].member, info[j].member)) {124 // return new Problem(true, NbBundle.getMessage(PullUpRefactoringPlugin.class, "ERR_CannotPullupDuplicateMembers"));125 // }126 // }127 // }128 // }129 // 130 // // #2 - check if the targed type is not null131 // if (refactoring.getTargetType() == null) {132 // return new Problem(true, NbBundle.getMessage(PullUpRefactoringPlugin.class, "ERR_PullUp_NoTargetType")); // NOI18N133 // }134 // 135 // return null;136 // }137 // 138 // public Problem checkParameters() {139 // HashSet supers = new HashSet(Arrays.asList(refactoring.collectSupertypes()));140 // JavaClass targetType = refactoring.getTargetType();141 // PullUpRefactoring.MemberInfo[] members = refactoring.getMembers();142 //143 // fireProgressListenerStart(AbstractRefactoring.PARAMETERS_CHECK, members.length + 1);144 // try {145 // // #1 - check whether the target type is a legal super type146 // if (!supers.contains(targetType)) {147 // return new Problem(true, NbBundle.getMessage(PullUpRefactoringPlugin.class, "ERR_PullUp_IllegalTargetType")); // NOI18N148 // }149 // 150 // fireProgressListenerStep();151 //152 // // #2 - check whether all the members are legal members that can be pulled up153 // HashSet visitedSources = new HashSet();154 //// HashSet allMembers = new HashSet(Arrays.asList(members));155 // Problem problems = null;156 // visitedSources.add(refactoring.getSourceType());157 // for (int i = 0; i < members.length; i++) {158 // ClassDefinition cls;159 // NamedElement member = members[i].member;160 // if (member instanceof Feature) {161 // // member is a feature (inner class, field or method)162 // cls = ((Feature) member).getDeclaringClass();163 // } else {164 // // member is an interface from implements clause165 // MultipartId ifcName = (MultipartId) member;166 // // get parent of the element (should be class if this is really167 // // a name from implements clause168 // Object parent = ifcName.refImmediateComposite();169 // // if parent is not a class, member is invalid170 // if (!(parent instanceof JavaClass)) {171 // cls = null;172 // } else {173 // // check if the parent class contains this MultipartId174 // // in interfaceNames175 // if (!((JavaClass) parent).getInterfaceNames().contains(ifcName)) {176 // cls = null;177 // } else {178 // cls = (ClassDefinition) parent;179 // }180 // }181 // }182 // // if the declaring class has not been visited yet, perform checks on it183 // if (visitedSources.add(cls)) {184 // // if the declaring class of a feature is not a JavaClass, 185 // // or if it is not from the set of source type's supertypes186 // // or if the declaring class is not a subtype of target class187 // // then this member is illegal188 // if (!(cls instanceof JavaClass) || !supers.contains(cls) || cls.equals(targetType) || !cls.isSubTypeOf(targetType)) {189 // return createProblem(problems, true, NbBundle.getMessage(PullUpRefactoringPlugin.class, "ERR_PullUp_IllegalMember", member.getName())); // NOI18N190 // }191 // }192 // // #3 - check if the member already exists in the target class193 // boolean exists;194 // if (member instanceof Field) {195 // exists = (targetType.getField(member.getName(), false) != null);196 // } else if (member instanceof Method) {197 // exists = (targetType.getMethod(member.getName(), Utilities.getFeatureParamTypes((CallableFeature) member), false) != null);198 // } else if (member instanceof JavaClass) {199 // exists = (targetType.getInnerClass(member.getName(), false) != null);200 // } else {201 // exists = targetType.getInterfaces().contains(((MultipartId) member).getElement());202 // }203 // if (exists) {204 // return createProblem(problems, true, NbBundle.getMessage(PullUpRefactoringPlugin.class, "ERR_PullUp_MemberAlreadyExists", member.getName())); // NOI18N205 // }206 //207 // // #4 - check if the field does not use something that is not going to be pulled up208 //// Resource sourceResource = refactoring.getSourceType().getResource();209 //// Resource targetResource = targetType.getResource();210 //// if (!sourceResource.equals(targetResource)) {211 //// problems = checkUsedByElement(member, allMembers, problems, 212 //// !sourceResource.equals(targetResource), 213 //// !sourceResource.getPackageName().equals(targetResource.getPackageName()));214 //// }215 // 216 // fireProgressListenerStep();217 // }218 //219 // // TODO: implement non-fatal checks220 //221 // return null;222 // } finally {223 // fireProgressListenerStop();224 // }225 // }226 //227 // public Problem prepare(RefactoringElementsBag refactoringElements) {228 // PullUpRefactoring.MemberInfo[] members = refactoring.getMembers();229 // 230 // boolean makeTargetTypeAbstract = false;231 // 232 // for (int i = 0; i < members.length; i++) {233 // int modifiers = (members[i].member instanceof MultipartId) ? 0 : ((Feature) members[i].member).getModifiers();234 // int newmodifiers = 0;235 // if (refactoring.getTargetType().isInterface()) {236 // newmodifiers = (modifiers | Modifier.PUBLIC) & ~Modifier.PRIVATE & ~Modifier.PROTECTED;237 // } else {238 // if (!(Modifier.isPublic(modifiers) || Modifier.isProtected(modifiers))) {239 // newmodifiers = (modifiers | Modifier.PROTECTED) & ~Modifier.PRIVATE;240 // }241 // }242 // if (members[i].makeAbstract) {243 // refactoringElements.add(refactoring, new AddAbstractMethodElement((Method) members[i].member, refactoring.getTargetType(), newmodifiers));244 // makeTargetTypeAbstract = true;245 // if (newmodifiers != 0) {246 // refactoringElements.add(refactoring, new ChangeModElement((Feature) members[i].member, newmodifiers));247 // }248 // } else {249 // refactoringElements.add(refactoring, new MoveMemberElement(members[i].member, refactoring.getTargetType(), newmodifiers));250 // if (Modifier.isAbstract(modifiers)) {251 // makeTargetTypeAbstract = true;252 // }253 // }254 // }255 // 256 // if (makeTargetTypeAbstract && !Modifier.isAbstract(refactoring.getTargetType().getModifiers()) && !refactoring.getTargetType().isInterface()) {257 // refactoringElements.add(refactoring, new ChangeModElement(refactoring.getTargetType(), refactoring.getTargetType().getModifiers() | Modifier.ABSTRACT));258 // }259 // 260 // UndoWatcher.watch(((JMManager) JMManager.getManager()).getDataObject(refactoring.getTargetType().getResource()));261 //262 // 263 // // TODO: add refactoring element for changing modifiers of the target class264 // // if necessary265 // 266 // return null;267 // }268 // 269 // // --- REFACTORING ELEMENTS ------------------------------------------------270 // 271 // /** Refactoring element that takes care of adding an abstract method declaration272 // * to the target class.273 // */274 // private static class AddAbstractMethodElement extends SimpleRefactoringElementImpl {275 // private final Method methodToAdd;276 // private final JavaClass target;277 // private final int newModifiers;278 // private final String text;279 // 280 // /** Creates a new instance of this refactoring element.281 // * @methodToAdd Method in the source class that should be declared as abstract282 // * in the target class.283 // * @target Target class.284 // * @newModifiers New modifiers of the method or 0 if the modifiers should be285 // * the same as for the original method (+ <code>abstract</code> modifier).286 // */287 // AddAbstractMethodElement(Method methodToAdd, JavaClass target, int newModifiers) {288 // this.methodToAdd = methodToAdd;289 // this.target = target;290 // this.newModifiers = newModifiers;291 // this.text = NbBundle.getMessage(PullUpRefactoringPlugin.class, "TXT_PullUp_AddMethod", UIUtilities.getDisplayText(methodToAdd)); // NOI18N292 // }293 //294 // public void performChange() {295 // // get extent of the target method296 // JavaModelPackage extent = (JavaModelPackage) target.refImmediatePackage();297 // // create the abstract method in this extent (duplicating the header298 // // of the existing method299 // Method newMethod = extent.getMethod().createMethod(300 // methodToAdd.getName(),301 // Utilities.duplicateList(methodToAdd.getAnnotations(), extent),302 // (newModifiers == 0 ? methodToAdd.getModifiers() : newModifiers) | Modifier.ABSTRACT,303 // methodToAdd.getJavadocText(),304 // null,305 // null,306 // null, 307 // Utilities.duplicateList(methodToAdd.getTypeParameters(), extent),308 // Utilities.duplicateList(methodToAdd.getParameters(), extent),309 // Utilities.duplicateList(methodToAdd.getExceptionNames(), extent),310 // (TypeReference) ((MetadataElement) methodToAdd.getTypeName()).duplicate(extent),311 // methodToAdd.getDimCount()312 // );313 // // add the new method to the target class314 // if (target.isInterface()) {315 // newMethod.setModifiers(newMethod.getModifiers() & ~Modifier.ABSTRACT & ~Modifier.PUBLIC & ~Modifier.PROTECTED & ~Modifier.PRIVATE);316 // }317 // target.getContents().add(newMethod);318 // }319 //320 // public String getText() {321 // return text;322 // }323 //324 // public String getDisplayText() {325 // return text;326 // }327 //328 // public FileObject getParentFile() {329 // return JavaMetamodel.getManager().getFileObject(target.getResource());330 // }331 //332 // public Element getJavaElement() {333 // return target;334 // }335 //336 // public PositionBounds getPosition() {337 // return null;338 // }339 // }340 // 341 // /** Refactoring element that takes care of moving an element to the target type.342 // */343 // private static class MoveMemberElement extends SimpleRefactoringElementImpl {344 // private final NamedElement elementToMove;345 // private final JavaClass target;346 // private final int newModifiers;347 // private final String text;348 // 349 // /** Creates a new instance of this refactoring element.350 // * @elementToMove Element to be moved to the target type.351 // * @target The target type the element should be moved to.352 // * @newModifiers New modifiers of the element or 0 if the modifiers should353 // * remain unchanged.354 // */355 // MoveMemberElement(NamedElement elementToMove, JavaClass target, int newModifiers) {356 // this.elementToMove = elementToMove;357 // this.target = target;358 // this.newModifiers = newModifiers;359 // this.text = NbBundle.getMessage(PullUpRefactoringPlugin.class, "TXT_PullUp_Member", UIUtilities.getDisplayText(elementToMove)); // NOI18N360 // }361 //362 // public void performChange() {363 // JavaModelPackage targetExtent = (JavaModelPackage) target.refImmediatePackage();364 // JavaClass elementParent;365 // Element newElement;366 // boolean deleteElementToMove = false;367 // // processing is different for Feature (field, inner class, method)368 // // and MultipartId (interface in the implements clause)369 // if (elementToMove instanceof Feature) {370 // // get the declaring class of the element371 // elementParent = (JavaClass) ((Feature) elementToMove).getDeclaringClass();372 // // check if the declaring type is in the same extent as the target type373 // if (targetExtent.equals(elementParent.refImmediatePackage())) {374 // // if so, a simple move is possible375 // elementParent.getFeatures().remove(elementToMove);376 // newElement = elementToMove;377 // } else {378 // // otherwise we need to create a copy of the element in the target extent379 // newElement = ((MetadataElement) elementToMove).duplicate(targetExtent);380 // // and delete the original element in the source extent381 // deleteElementToMove = true;382 // }383 // // add the element to the target class384 // target.getContents().add(newElement);385 // // change modifiers if necessary386 // if (newModifiers != 0) {387 // ((Feature) newElement).setModifiers(newModifiers);388 // }389 // } else {390 // // get parent type of the element391 // elementParent = (JavaClass) elementToMove.refImmediateComposite();392 // // check if the target extent is the same as the source extent393 // if (targetExtent.equals(elementParent.refImmediatePackage())) {394 // // yes -> simple move395 // elementParent.getInterfaceNames().remove(elementToMove);396 // newElement = elementToMove;397 // } else {398 // // no -> a new copy in the target extent needs to be created399 // newElement = ((MetadataElement) elementToMove).duplicate(targetExtent);400 // deleteElementToMove = true;401 // }402 // // add the new element to the implements clause of the target type403 // target.getInterfaceNames().add(newElement);404 // }405 // ((MetadataElement) newElement).fixImports(target, elementToMove);406 // if (deleteElementToMove) {407 // elementToMove.refDelete();408 // }409 // }410 //411 // public String getText() {412 // return text;413 // }414 //415 // public String getDisplayText() {416 // return text;417 // }418 //419 // public FileObject getParentFile() {420 // return JavaMetamodel.getManager().getFileObject(elementToMove.getResource());421 // }422 //423 // public Element getJavaElement() {424 // return JavaModelUtil.getDeclaringFeature(elementToMove);425 // }426 //427 // public PositionBounds getPosition() {428 // return JavaMetamodel.getManager().getElementPosition(elementToMove);429 // }430 // }431 // 432 // // --- HELPER METHODS ------------------------------------------------------433 // 434 // // checks if the source type or any of its supertypes has any members that could435 // // be pulled up436 // private static boolean hasMembers(JavaClass sourceType, JavaClass[] supertypes) {437 // boolean result = Utilities.hasMembers(sourceType);438 // 439 // for (int i = 0; i < (supertypes.length - 1) && !result; i++) {440 // result = Utilities.hasMembers(supertypes[i]);441 // }442 // 443 // return result;444 // }445 // 446 // // checks if the element is used by other element which will not be pulled up447 // private Problem checkUsedByElement(Element element, Set allMembers, Problem problems, boolean resourceChange, boolean packageChange) {448 // if (element instanceof MultipartId) {449 // // TODO: check with import management tool, whether an import should be added450 // } else if (element instanceof MethodInvocation451 // || element instanceof NewClassExpression452 // || element instanceof VariableAccess) {453 // NamedElement referencedElement = ((ElementReference) element).getElement();454 // if (referencedElement instanceof Feature) {455 // Feature referencedFeature = (Feature) referencedElement;456 // int modifiers = referencedFeature.getModifiers();457 // ClassDefinition declClass = referencedFeature.getDeclaringClass();458 // PrimaryExpression parentClass;459 // if (element instanceof VariableAccess) {460 // parentClass = ((VariableAccess) element).getParentClass();461 // } else if (element instanceof MethodInvocation) {462 // parentClass = ((MethodInvocation) element).getParentClass();463 // } else {464 // if ((declClass instanceof JavaClass) && !Modifier.isStatic(((JavaClass) declClass).getModifiers())) {465 // parentClass = ((NewClassExpression) element).getEnclosingClass();466 // } else {467 // parentClass = null;468 // }469 // }470 // boolean isSameInstance = (parentClass == null) || (parentClass instanceof ThisExpression);471 // if (Modifier.isPrivate(modifiers)) {472 // if (resourceChange || isSameInstance) {473 // problems = createProblem(problems, false, "will not be accessible"); // NOI18N474 // }475 // } else if (packageChange && !Modifier.isPublic(modifiers)) {476 // if (Modifier.isProtected(modifiers) && (referencedElement instanceof Method)) {477 // // TODO: check if the method is not defined also in target or one of its supers ->478 // // in that case do not generate a problem479 // }480 // problems = createProblem(problems, false, "will not be accessible - is protected/package private"); // NOI18N481 // }482 // }483 // }484 // 485 // for (Iterator it = element.getChildren().iterator(); it.hasNext();) {486 // problems = checkUsedByElement((Element) it.next(), allMembers, problems, resourceChange, packageChange);487 // }488 // 489 // return problems;490 // }491 }492