/**
   * Removes the specified namespaces recursively from all children.
   *
   * @param element the element from which the specified namespaces should be removed recursively.
   * @param list the namespaces to remoev
   */
  public static void remove_namespaces(Element element, List<Namespace> list) {
    // Depth first
    for (Object obj : element.getContent()) {
      if ((obj instanceof Element) == false) {
        continue;
      }
      remove_namespaces((Element) obj, list);
    } // for

    // Remove all specified namespaces. Done in a manner which is
    // independent of the referential equivalence. Also, this is independent
    // of the Namespace objects equivalence, which accounts only for URI.

    Namespace pns = element.getNamespace();
    if (pns != null) {
      if (contains_same_ns(list, pns)) {
        element.removeNamespaceDeclaration(pns);
      }
    } // if

    List<Namespace> del = new LinkedList<Namespace>();
    for (Object obj : element.getAdditionalNamespaces()) {
      Namespace ns = (Namespace) obj;
      if (contains_same_ns(list, ns)) {
        // Schedule for deletion; cannot delete immedately,
        // because it would be concurrent modification
        del.add(ns);
      }
    } // for

    // Execute deletion
    for (Namespace ns : del) {
      // from jdom's javadoc: "If the declaration is not present,
      // this method does nothing."
      element.removeNamespaceDeclaration(ns);
    } // for
  } // remove_namespaces()
  /**
   * Purging unused declarations is less optimal, performance-wise, than never adding them in the
   * first place. So, we should still ask the ROME guys to fix their code (not adding dozens of
   * unnecessary module declarations). Having said that: purging them here, before XML generation,
   * is more efficient than parsing and re-molding the XML after ROME generates it.
   *
   * <p>Note that the calling app could still add declarations/modules to the Feed tree after this.
   * Which is fine. But those modules are then responsible for crawling to the root of the tree, at
   * generate() time, to make sure their namespace declarations are present.
   */
  protected static void purgeUnusedNamespaceDeclarations(Element root) {
    java.util.Set usedPrefixes = new java.util.HashSet();
    collectUsedPrefixes(root, usedPrefixes);

    List list = root.getAdditionalNamespaces();
    List additionalNamespaces = new java.util.ArrayList();
    additionalNamespaces.addAll(
        list); // the duplication will prevent a ConcurrentModificationException below

    for (int i = 0; i < additionalNamespaces.size(); i++) {
      Namespace ns = (Namespace) additionalNamespaces.get(i);
      String prefix = ns.getPrefix();
      if (prefix != null && prefix.length() > 0 && !usedPrefixes.contains(prefix)) {
        root.removeNamespaceDeclaration(ns);
      }
    }
  }
  /**
   * Bubbles all namespaces present in the element and in its children as close to the element as
   * possible. The process attempts to bubble all namespaces as up as possible. If at some level the
   * direct children define conflicting namespace prefixes or multiple prefixes for a single
   * namespace URI, those namespaces are left there.
   *
   * <p>
   *
   * @param element bubbles namespaces in the children recursively upwards.
   * @return The namespaces which can be bubbled from the specified element upstream and which
   *     namespaces caused conflicts in the prefixes.
   */
  public static List<Namespace> bubble_namespaces_greedy(Element element) {
    // Depth first
    Map<Element, List<Namespace>> map = new LinkedHashMap<Element, List<Namespace>>();

    for (Object obj : element.getContent()) {
      if ((obj instanceof Element) == false) {
        continue;
      }

      // Depth-first recursion
      Element c = (Element) obj;
      List<Namespace> rval = null;
      rval = bubble_namespaces_greedy(c);
      map.put(c, rval);
    } // for

    // Create a set containing all different namespaces in the children
    List<Namespace> set = new LinkedList<Namespace>();

    for (Map.Entry<Element, List<Namespace>> entry : map.entrySet()) {
      for (Namespace a : entry.getValue()) {
        // Pick these into local variables for convenience
        // and to avoid frequently calling the member methods..
        String uri = a.getURI();
        String prefix = a.getPrefix();

        boolean already = false;

        for (Namespace b : set) {
          boolean uri_equal = uri.equals(b.getURI());
          boolean prefix_equal = prefix.equals(b.getPrefix());

          if (uri_equal & prefix_equal) {
            // already included
            already = true;
            break;
          }
        } // for: all total

        if (already == false) {
          set.add(a);
        } // if: not already
      } // for each ns
    } // for

    // The list "set" contains now all namespaces present in all
    // children. Next the conflicting ones need to be singled out.

    // Return namespaces for this element
    List<Namespace> pset = new LinkedList<Namespace>();
    Namespace pns = element.getNamespace();
    if (pns != null) {
      pset.add(pns);
    }

    for (Object obj : element.getAdditionalNamespaces()) {
      pset.add((Namespace) obj);
    } // for

    // The list "pset" contains now all namespaces present
    // in the parent element itself.

    List<Namespace> set2 = new LinkedList<Namespace>();

    for (Namespace ns1 : set) {
      String uri = ns1.getURI();
      String prefix = ns1.getPrefix();

      boolean conflicting = false;
      for (Namespace ns2 : set) {
        if (ns1 == ns2) {
          continue;
        }
        boolean uri_eq = uri.equals(ns2.getURI());
        boolean p_eq = prefix.equals(ns2.getPrefix());

        if (p_eq != uri_eq) {
          // Conflict. Drop both ns1 and ns2.
          set2.add(ns1);
          conflicting = true;
          // The ns2 will come..
          break;
        } // if
      } // for

      if (conflicting) {
        continue;
      }

      // Make sure that ns1 does not conflict with the parent either
      for (Namespace ns2 : pset) {
        if (ns1 == ns2) {
          continue;
        }
        boolean uri_eq = uri.equals(ns2.getURI());
        boolean p_eq = prefix.equals(ns2.getPrefix());

        if (p_eq != uri_eq) {
          // Conflict. Drop both ns1 only; it cannot be
          // propagated more upwards.
          set2.add(ns1);
          conflicting = true;
          break;
        } // if
      }
    } // for

    // the list "set2" is now a list of all conflicting nodes
    // in the children
    set.removeAll(set2);
    // At this point:
    //      set:  a list of all namespaces which can be propagated
    //            upwards without conflicts.
    //      set2: a list of all namespaces which are conflicting

    for (Map.Entry<Element, List<Namespace>> entry : map.entrySet()) {
      // see which namespaces can be propagated to this..
      List<Namespace> list = entry.getValue();
      Element child = entry.getKey();

      for (Namespace ns : list) {
        // ------- Find if ns belongs in set
        boolean contains = false;
        String uri = ns.getURI();
        String p = ns.getPrefix();
        for (Namespace x : set) {
          boolean uri_eq = uri.equals(x.getURI());
          boolean p_eq = p.equals(x.getPrefix());
          if (p_eq && uri_eq) {
            contains = true;
            break;
          }
        } // for
        // if "ns" is contained in "set",
        // it can be removed from the child

        if (contains) {
          // Namespace "ns" can be propagated
          child.removeNamespaceDeclaration(ns);
        } // if: contains
      } // for
    } // for

    // Add all not in pset to the parent
    List<Namespace> rval = new LinkedList<Namespace>();

    for (Namespace ns : set) {
      String uri = ns.getURI();
      String p = ns.getPrefix();
      boolean contains = false;
      for (Namespace x : pset) {
        boolean uri_eq = uri.equals(x.getURI());
        boolean p_eq = p.equals(x.getPrefix());
        if (p_eq && uri_eq) {
          contains = true;
          break;
        }
      } // for

      if (!contains) {
        element.addNamespaceDeclaration(ns);
        rval.add(ns);
      }
    } // for

    rval.addAll(pset);

    return rval;
  } // bubble_namespaces_greedy()