/**
   * Returns a new array without those classes, that are replaced by another class. The returned
   * array is a new instance, except there are no replacing classes. Replacing classes are annotated
   * with {@link Replace}. Replacing classes are reordered according to their nearest {@link Order}
   * annotation that is found up the type hierarchy.
   *
   * @param classes
   * @return
   * @since 3.8.2
   */
  @SuppressWarnings("unchecked")
  public static <T> Class<? extends T>[] removeReplacedClasses(Class<? extends T>[] classes) {
    Set<Class<? extends T>> replacingClasses = getReplacingLeafClasses(classes);
    if (replacingClasses.isEmpty()) {
      // there are no replacing classes -> return original array
      return classes;
    }

    // compute resulting list of ordered classes
    List<Class<? extends T>> list = new ArrayList<Class<? extends T>>();
    for (Class<? extends T> c : classes) {
      list.add(c);
    }

    for (Class<? extends T> replacingClass : replacingClasses) {
      boolean reorder = !replacingClass.isAnnotationPresent(Order.class);
      boolean reordered = false;

      // handle transitive replacements
      Class<?> classToBeReplaced = replacingClass.getSuperclass();
      while (classToBeReplaced.isAnnotationPresent(Replace.class)) {
        // reorder replacement if necessary
        if (reorder && !reordered && classToBeReplaced.isAnnotationPresent(Order.class)) {
          reordered = moveBefore(list, replacingClass, (Class<? extends T>) classToBeReplaced);
        }
        list.remove(classToBeReplaced);
        classToBeReplaced = classToBeReplaced.getSuperclass();
      }

      // reorder replacement if necessary
      if (reorder && !reordered) {
        moveBefore(list, replacingClass, (Class<? extends T>) classToBeReplaced);
      }
      list.remove(classToBeReplaced);
    }

    return list.toArray(new Class[list.size()]);
  }