/**
  * 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.
             }
           }
         }
       }
     }
   }
 }
 /**
  * Getters/Setters are supposed to be kept with their associated property. Search the list of
  * entries to find the property and attach the setter.
  *
  * @param entries list of all items (methods, fields) in the class.
  */
 private void hookGetterToProperty(List<ClassContentsEntry> entries) {
   ListIterator<ClassContentsEntry> li = entries.listIterator();
   String property = MethodUtil.getPropertyName((PsiMethod) myEnd);
   while (li.hasNext()) {
     Object o = li.next();
     if (o instanceof FieldEntry) {
       FieldEntry fe = (FieldEntry) o;
       StringBuffer sb = new StringBuffer(fe.getName());
       sb.setCharAt(0, Character.toUpperCase(sb.charAt(0)));
       if (fe.getGetterMethod() == null && property.equals(sb.toString())) {
         fe.setGetterMethod(this);
         myKeptWithProperty = true;
         break;
       }
     }
   }
 }
 void insertInList(final List<MethodEntry> list, Comparator<MethodEntry> comparator) {
   boolean inserted = false;
   ListIterator li = list.listIterator();
   while (li.hasNext() && !inserted) {
     MethodEntry entry = ((MethodEntry) li.next());
     if (comparator.compare(this, entry) < 0) {
       LOG.debug(
           "insertInList dependent method: add "
               + myEnd.toString()
               + " at index "
               + (li.nextIndex() - 1));
       list.add(li.nextIndex() - 1, this);
       inserted = true;
     }
   }
   if (!inserted) {
     list.add(this);
   }
 }
 /**
  * Decide if a method is an extracted method. It is an extracted method if: - it is called by at
  * least one other method, and - it is private, or passes the "never / 1 / >1" caller test.
  *
  * <p>(It is not considered an extracted method if it is a setter, there is a corresponding
  * getter, and the option to keep getters and setters together is set. This is a special case.)
  */
 public void determineExtractedMethod(RelatedMethodsSettings settings) {
   myRelatedMethod = false;
   if (!isGetter() && !isSetter()) {
     if (myCalledByMethods.size() > 0) {
       PsiMethod m = (PsiMethod) myEnd;
       if (!m.getModifierList().hasModifierProperty(PsiModifier.PRIVATE)) {
         switch (settings.getNonPrivateTreatment()) {
           case RelatedMethodsSettings.NON_PRIVATE_EXTRACTED_NEVER:
             break;
           case RelatedMethodsSettings.NON_PRIVATE_EXTRACTED_ONE_CALLER:
             if (myCalledByMethods.size() == 1) {
               myRelatedMethod = true;
             }
             break;
           case RelatedMethodsSettings.NON_PRIVATE_EXTRACTED_ANY_CALLERS:
             myRelatedMethod = true;
             break;
         }
       } else {
         myRelatedMethod = true;
       }
     }
   } else {
     if (isSetter() && myCalledByMethods.size() > 0 && myCorrespondingGetterSetters.size() == 0) {
       myRelatedMethod = true;
       LOG.debug(myEnd.toString() + " is setter but has no getter, treated as extracted method");
     } else {
       LOG.debug(myEnd.toString() + " is getter/setter, not treated as extracted method");
     }
   }
   LOG.debug("determined " + myEnd.toString() + " is extracted method? " + myRelatedMethod);
   /** If this is not an extracted method, remove it from any callers so that it won't be moved. */
   if (!myRelatedMethod) {
     ListIterator<MethodEntry> li = myCalledByMethods.listIterator();
     while (li.hasNext()) {
       MethodEntry entry = (li.next());
       entry.myCalledMethods.remove(this);
       li.remove();
     }
   }
   /**
    * Remaining entries are only extracted methods at this point. Using first/last rule, keep only
    * the call by the calling method that this child method will be grouped with, and discard the
    * rest.
    */
   if (myCalledByMethods.size() > 1) {
     ListIterator<MethodEntry> li;
     if (settings.isBelowFirstCaller()) {
       li = myCalledByMethods.listIterator(1);
       while (li.hasNext()) {
         MethodEntry entry = (li.next());
         entry.myCalledMethods.remove(this);
         li.remove();
       }
     } else {
       li = myCalledByMethods.listIterator(myCalledByMethods.size() - 1);
       while (li.hasPrevious()) {
         MethodEntry entry = (li.previous());
         entry.myCalledMethods.remove(this);
         li.remove();
       }
     }
   }
   if (myRelatedMethod) {
     LOG.debug(
         "extracted method "
             + toString()
             + " will be arranged under "
             + myCalledByMethods.get(0).toString());
   }
 }
 /**
  * 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);
     }
   }
 }