/** * Removes overloaded methods from the contents list and adds them to the overloadedMethods list * of the first overloaded method encountered. * * @param contents * @param doExtractedMethods */ private static void cullOverloadedMethods( List<ClassContentsEntry> contents, boolean doExtractedMethods) { List<ClassContentsEntry> copy = new ArrayList<ClassContentsEntry>(contents); for (ClassContentsEntry o : copy) { if (o instanceof RelatableEntry) { MethodEntry me = (MethodEntry) o; if ((me.isRelatedMethod() == doExtractedMethods) && !me.isOverloadedMethod) { String meName = me.myEnd.toString(); // search contents list for methods with identical name, and attach them as overloaded // methods. ListIterator<ClassContentsEntry> contentIterator = contents.listIterator(); while (contentIterator.hasNext()) { Object o1 = contentIterator.next(); if (o1 instanceof RelatableEntry) { MethodEntry me2 = (MethodEntry) o1; if (me2 == me) { continue; } String me2Name = me2.myEnd.toString(); if (meName.equals(me2Name)) { contentIterator.remove(); me.myOverloadedMethods.add(me2); me2.isOverloadedMethod = true; // set flag so array copy will skip this entry. } } } } } } }
/** * Called after top level methods (and other items) have been moved from entries to * ruleInstanceList. Task now is to scan ruleInstanceList looking for MethodEntry objects with * dependent (related, extracted) methods. Move these from the entries list to the correct place * in the ruleInstanceList list. * * @param entries Remaining (unarranged) items, including all dependent (extracted) methods. * @param ruleInstance Rule containing parents (callers) of potentially related methods */ public static void rearrangeRelatedItems( List<ClassContentsEntry> entries, RuleInstance ruleInstance, RelatedMethodsSettings rms) { List<RangeEntry> parentEntries = new ArrayList<RangeEntry>(ruleInstance.getMatches()); for (RangeEntry o : parentEntries) { if (o instanceof RelatableEntry) { MethodEntry me = (MethodEntry) o; if (me.isGetter()) { if (me.myKeptWithProperty) { if (me.getMatchedRule() != null && me.getMatchedRule().getMatches() != null) { // prevent the getter from appearing under a rule it matches; it will be placed under // the property me.getMatchedRule().getMatches().remove(me); } } for (MethodEntry theSetter : me.myCorrespondingGetterSetters) { final RuleInstance theRule = theSetter.getMatchedRule(); LOG.debug( "rearrangeRelatedItems: for getter method " + me + ", corresponding setter is " + theSetter); if (theRule != null && theRule.getMatches() != null) { LOG.debug( "remove entry " + theSetter.myEnd + " from matched rule" + theRule + "; matches = " + ((java.util.List) theRule.getMatches()) == null ? "null" : ""); theRule.getMatches().remove(theSetter); } } } if (me.myCalledMethods.size() > 0) { List<MethodEntry> parents = new LinkedList<MethodEntry>(); parents.add(me); moveRelatedItems( entries, parents, rms, ((PsiMethod) me.myEnd).getName(), ((PsiMethod) me.myEnd).getName() + "()", 1); if (LOG.isDebugEnabled()) { // dump sorted children recursively me.dumpChild(0); } me.assignComments(rms); } } } }
private static void test(MethodEntry current, List<MethodEntry> set) { Iterator<MethodEntry> it = current.myCalledMethods.iterator(); while (it.hasNext()) { MethodEntry callee = it.next(); if (set.contains(callee)) { callee.myCalledByMethods.remove(current); if (callee.myCalledByMethods.size() == 0) { callee.myRelatedMethod = false; } it.remove(); } else { set.add(callee); test(callee, set); } } }
public void emit(Emitter emitter) { StringBuilder sb = emitter.getTextBuffer(); if (getCustomizedPrecedingComment().length() > 0) { sb.append("\n"); sb.append(getCustomizedPrecedingComment()); } emitAllElements(sb, emitter.getDocument()); if (getCustomizedTrailingComment().length() > 0) { sb.append("\n"); sb.append(getCustomizedTrailingComment()); } /** emit corresponding setter, overloaded methods, and related methods, if any. */ ListIterator li; if (isGetter()) { for (MethodEntry entry : myCorrespondingGetterSetters) { entry.emit(emitter); } } for (MethodEntry me : myOverloadedMethods) { me.emit(emitter); } for (MethodEntry me : sortedMethods) { me.emit(emitter); } }
/** * Called when getters and setters are to be kept together in pairs. Searches the list of entries * for any getter/setter methods that matches this getter method. Normally there is only one * setter per getter, but one could have a "getXXX", "isXXX" and "setXXX" trio which need to be * related. In this case, the first getter encountered finds the other getter and setter and the * three will be emitted in that order. * * @param possibleMethods entry list, possibly containing the corresponding setter for this * getter. * @param settings */ public void determineSetter( final List<ClassContentsEntry> possibleMethods, RearrangerSettings settings) { if (!isGetter()) // wasted assertion, already checked before calling { return; } final PsiMethod thisMethod = (PsiMethod) myEnd; String thisProperty = MethodUtil.getPropertyName(thisMethod); if (isGetter() && !myKeptWithProperty) { if (settings.isKeepGettersSettersWithProperty()) { hookGetterToProperty(possibleMethods); } } for (ClassContentsEntry o : possibleMethods) { if (o instanceof RelatableEntry) { MethodEntry me = (MethodEntry) o; // don't use a setter twice (could be two methods which both look like getters; assign the // setter // to only one of them.) Also, associate all getter/setter methods for the same property // with the // first one encountered. if ((me.isSetter() || me.isGetter()) && me.myCorrespondingGetterSetters.size() == 0 && me != this) { PsiMethod m = (PsiMethod) me.myEnd; String otherProperty = MethodUtil.getPropertyName(m); if (thisProperty.equals(otherProperty)) { LOG.debug( "method " + thisMethod.toString() + " is getter; its setter is " + m.toString()); // place getters ahead of setters if (me.isGetter()) { myCorrespondingGetterSetters.add(0, me); // clear the getter flag and set the setter flag; this causes the method to be emitted // under the first getter encountered. me.setGetter(false); me.setSetter(true); } else { myCorrespondingGetterSetters.add(me); } me.myCorrespondingGetterSetters.add(this); } } } } }
public DefaultMutableTreeNode addToPopupTree( DefaultMutableTreeNode parent, RearrangerSettings settings) { DefaultMutableTreeNode node = new RearrangerTreeNode(this, myName); parent.add(node); ListIterator li; for (MethodEntry methodEntry : sortedMethods) { if (methodEntry.isSetter() && methodEntry.myCalledByMethods.size() > 0) { // setters are arranged with getters when "keep getters/setters together" option is checked. // but setters are not really called by getters. So attach them to the upper level. methodEntry.addToPopupTree(parent, settings); } else { methodEntry.addToPopupTree(node, settings); } } for (MethodEntry methodEntry : myOverloadedMethods) { methodEntry.addToPopupTree(node, settings); } return node; }
/** * Set preceding and/or trailing comments on the appropriate methods, based on the comment type. * * @param rms * @param allMethodNames contains a list of methods invoked to reach this point. Top level is * empty string. * @param level current nesting level; top level is 1. */ private void assignComments( RelatedMethodsSettings rms, List<MethodEntry> allCallingMethods, String allMethodNames, int level) { final String currentMethodName = ((PsiMethod) myEnd).getName(); String[] callingNames = new String[allCallingMethods.size()]; for (int i = 0; i < allCallingMethods.size(); i++) { MethodEntry me = allCallingMethods.get(i); callingNames[i] = ((PsiMethod) me.myEnd).getName(); } MethodEntry topLevel = this; while (topLevel.myCalledByMethods.size() > 0) { topLevel = (topLevel.myCalledByMethods.get(0)); } switch (rms.getCommentType()) { case RelatedMethodsSettings.COMMENT_TYPE_TOP_LEVEL: customizedPrecedingComment = expandComment( rms.getPrecedingComment(), currentMethodName, allMethodNames, currentMethodName, level); MethodEntry last = this; while (last.sortedMethods.size() > 0) { last = (last.sortedMethods.get(last.sortedMethods.size() - 1)); } last.customizedTrailingComment = expandComment( rms.getTrailingComment(), currentMethodName, allMethodNames, currentMethodName, level); break; case RelatedMethodsSettings.COMMENT_TYPE_EACH_METHOD: customizedPrecedingComment = expandComment( rms.getPrecedingComment(), currentMethodName, allMethodNames, ((PsiMethod) topLevel.myEnd).getName(), level); customizedTrailingComment = expandComment( rms.getTrailingComment(), currentMethodName, allMethodNames, ((PsiMethod) topLevel.myEnd).getName(), level); // recursively assign comments. for (MethodEntry methodEntry : sortedMethods) { String newAllMethodNames = appendMN(allMethodNames, callingNames, allCallingMethods, this, rms); methodEntry.assignComments(rms, sortedMethods, newAllMethodNames, level + 1); } break; case RelatedMethodsSettings.COMMENT_TYPE_NEW_FAMILY: { /** * Specialized routine to assign comments to "families" of methods. Insert a preceding * comment before every sibling except: when it is the first, and when the prior sibling * had no children and this sibling has no children. */ MethodEntry firstEntry = null; MethodEntry previousEntry = null; for (MethodEntry methodEntry : allCallingMethods) { if (firstEntry == null) { firstEntry = methodEntry; } else if (methodEntry.sortedMethods.size() != 0 || previousEntry.sortedMethods.size() != 0) { methodEntry.customizedPrecedingComment = expandComment( rms.getPrecedingComment(), currentMethodName, allMethodNames, ((PsiMethod) topLevel.myEnd).getName(), level); } previousEntry = methodEntry; if (methodEntry.sortedMethods.size() != 0) { String newAllMethodNames = appendMN(allMethodNames, callingNames, allCallingMethods, methodEntry, rms); methodEntry.assignComments( rms, methodEntry.sortedMethods, newAllMethodNames, level + 1); } } } break; case RelatedMethodsSettings.COMMENT_TYPE_EACH_LEVEL: { MethodEntry beginLevel = allCallingMethods.get(0); MethodEntry endLevel = allCallingMethods.get(allCallingMethods.size() - 1); beginLevel.customizedPrecedingComment = expandComment( rms.getPrecedingComment(), currentMethodName, allMethodNames, ((PsiMethod) topLevel.myEnd).getName(), level); // recursively assign comments for each level. for (MethodEntry methodEntry : allCallingMethods) { if (methodEntry.sortedMethods.size() > 0) { String newAllMethodNames = appendMN(allMethodNames, callingNames, allCallingMethods, methodEntry, rms); methodEntry.assignComments( rms, methodEntry.sortedMethods, newAllMethodNames, level + 1); } } while (endLevel.sortedMethods.size() > 0) { endLevel = endLevel.sortedMethods.get(endLevel.sortedMethods.size() - 1); } if (endLevel.customizedTrailingComment.length() > 0) { endLevel.customizedTrailingComment += "\n"; } endLevel.customizedTrailingComment += expandComment( rms.getTrailingComment(), currentMethodName, allMethodNames, ((PsiMethod) topLevel.myEnd).getName(), level); } break; } }
/** * If setting indicates, move all overloaded methods (of the same name) adjacent with the first * encountered. Sort in the configured order (original order, or by number of parameters). */ public static void handleOverloadedMethods( List<ClassContentsEntry> contents, RearrangerSettings settings) { if (!settings.isKeepOverloadedMethodsTogether()) return; /** * first, detect overloaded methods and move them to the first method's list of overloaded * methods. Make two passes, first for extracted methods, then for non-extracted methods. This * will organize any overloaded methods for extracted methods with them (as opposed to whichever * was seen first.) */ cullOverloadedMethods(contents, true); LOG.debug("entered handleOverloadedMethods(): move overloaded methods together"); cullOverloadedMethods(contents, false); List<ClassContentsEntry> copy = new ArrayList<ClassContentsEntry>(contents); for (ClassContentsEntry rangeEntry : copy) { if (rangeEntry instanceof RelatableEntry) { MethodEntry current = (MethodEntry) rangeEntry; if (current.myOverloadedMethods.size() > 0) { List<MethodEntry> newList = new ArrayList<MethodEntry>(current.myOverloadedMethods.size() + 1); newList.add(current); /** * we are looking at the head of a list of overloaded methods. We need to sort the list if * necessary and, if the head of the list has changed, replace it in the contents array. */ switch (settings.getOverloadedOrder()) { case RearrangerSettings.OVERLOADED_ORDER_RETAIN_ORIGINAL: // list is already in original order, except perhaps that the top-most extracted // method // comes first (if there is one). newList.addAll(current.myOverloadedMethods); break; case RearrangerSettings.OVERLOADED_ORDER_ASCENDING_PARAMETERS: case RearrangerSettings.OVERLOADED_ORDER_DESCENDING_PARAMETERS: for (MethodEntry entry : current.myOverloadedMethods) { boolean inserted = false; for (int index = 0; index < newList.size(); index++) { MethodEntry me = newList.get(index); if (settings.getOverloadedOrder() == RearrangerSettings.OVERLOADED_ORDER_ASCENDING_PARAMETERS ? me.getnParameters() > entry.getnParameters() : me.getnParameters() < entry.getnParameters()) { newList.add(index, entry); inserted = true; break; } } if (!inserted) { newList.add(entry); } } break; } current.myOverloadedMethods.clear(); /** * if the head of the arraylist is not the same as "current", then the sort operation * moved another method to the head of the list. Replace that in the contents array. Then * assign a new ordered list. */ int index = contents.indexOf(current); if (newList.get(0) != current) { contents.set(index, ((MethodEntry) newList.get(0))); } /** insert the remaining overloaded methods after the current entry. */ newList.remove(0); for (int j = 0; j < newList.size(); j++) { contents.add(index + j + 1, ((MethodEntry) newList.get(j))); } } } } }
void dumpChild(int level) { LOG.debug(level + ": " + ((PsiMethod) myEnd).getName()); for (MethodEntry methodEntry : sortedMethods) { methodEntry.dumpChild(level + 1); } }
/** * Move methods related to this one underneath it (beginning at the index specified.) Do so in * depth-first or breadth-first order, as configured. At each level, do alphabetical, original * order, or call order sorting. * * @param entries list of global (all rule) unmatched items including dependent methods * @param parents contains a list of items to be handled; order is depth or breadth-first. */ private static void moveRelatedItems( List<ClassContentsEntry> entries, List<MethodEntry> parents, RelatedMethodsSettings rms, String topLevelMethodName, String allMethodNames, int level) { allMethodNames = buildAllMethodNamesString(allMethodNames, parents); /** First, identify all dependent methods. Move them to parents in the specified order. */ List<MethodEntry> children = ((MethodEntry) parents.get(parents.size() - 1)).sortedMethods; // if (rms.isDepthFirstOrdering()) // { switch (rms.getOrdering()) { case RelatedMethodsSettings.RETAIN_ORIGINAL_ORDER: { /** * iterate through entries looking for those that are called by method(s) in the set of * parent methods. Add these to the list of children in order seen in entries. */ ListIterator li = entries.listIterator(); while (li.hasNext()) { Object o = li.next(); if (o instanceof RelatableEntry) { MethodEntry me = (MethodEntry) o; for (MethodEntry entry : parents) { if (me.myCalledByMethods.contains(entry)) { children.add(me); li.remove(); break; } } } } } break; case RelatedMethodsSettings.ALPHABETICAL_ORDER: { /** * iterate through entries looking for those that are called by method(s) in the set of * parent methods. Add these to the list of children alphabetically. */ ListIterator li = entries.listIterator(); while (li.hasNext()) { Object o = li.next(); if (o instanceof RelatableEntry) { MethodEntry me = (MethodEntry) o; for (MethodEntry entry : parents) { if (me.myCalledByMethods.contains(entry)) { me.insertAlphabetically(children); li.remove(); break; } } } } } break; case RelatedMethodsSettings.INVOCATION_ORDER: /** * iterate through calling methods, looking for remaining unmatched methods (in 'entries' * list). Add these to the list of children in order of invocation. */ for (MethodEntry me : parents) { for (MethodEntry child : me.myCalledMethods) { if (entries.contains(child)) { children.add(child); entries.remove(child); } } } break; } /** * now children contains all the children of the parents for this level, and all these children * have been removed from "entries". Move these to the rearranged list. Then: If depth-first, * recurse setting the parent list to each of the children in turn. If breadth first, recurse * setting the parent list to all of the children. */ if (children.size() > 0) { if (rms.isDepthFirstOrdering()) { for (MethodEntry entry : children) { if (entry.myCalledMethods.size() == 0) { continue; } List<MethodEntry> parent = new LinkedList<MethodEntry>(); parent.add(entry); moveRelatedItems( entries, parent, rms, topLevelMethodName, allMethodNames + "." + ((PsiMethod) entry.myEnd).getName() + "()", level + 1); } } else { moveRelatedItems( entries, children, rms, topLevelMethodName, allMethodNames + ".Depth " + level, level + 1); } } }