private void buildTree(Node node,
                         Document doc,
                         Element current,
                         boolean atRoot) {
    // Recurse through the tree
    switch (node.getNodeType()) {
      case Node.DOCUMENT_NODE:
        NodeList nodes = node.getChildNodes();
        for (int i = 0, size = nodes.getLength(); i < size; i++) {
          buildTree(nodes.item(i), doc, current, true);
        }
        break;

      case Node.ELEMENT_NODE:
        String nodeName = node.getNodeName();
        String prefix = NS_PREFIX_DEFAULT;
        String localName = nodeName;
        int colon = nodeName.indexOf(':');
        if (colon >= 0) {
          prefix = nodeName.substring(0, colon);
          localName = nodeName.substring(colon + 1);
        }

        // Get element's namespace
        Namespace ns;
        String uri = node.getNamespaceURI();
        if (uri == null) {
          ns = (current == null) ? Namespace.NO_NAMESPACE
                                 : current.getNamespace(prefix);
        }
        else {
          ns = Namespace.getNamespace(prefix, uri);
        }

        Element element = factory.element(localName, ns);

        if (atRoot) {
          // If at root, set as document root
          factory.setRoot(doc, element);
        }
        else {
          // else add to a parent element
          factory.addContent(current, element);
        }

        // Add namespaces
        NamedNodeMap attributeList = node.getAttributes();
        int attsize = attributeList.getLength();

        for (int i = 0; i < attsize; i++) {
          Attr att = (Attr)attributeList.item(i);

          String attname = att.getName();
          if (attname.startsWith(NS_PREFIX_XMLNS)) {
            String attPrefix = NS_PREFIX_DEFAULT;
            colon = attname.indexOf(':');
            if (colon >= 0) {
              attPrefix = attname.substring(colon + 1);
            }

            String attvalue = att.getValue();

            Namespace declaredNS =
              Namespace.getNamespace(attPrefix, attvalue);

            // Add as additional namespaces if it's different
            // to this element's namespace (perhaps we should
            // also have logic not to mark them as additional if
            // it's been done already, but it probably doesn't
            // matter)
            if (prefix.equals(attPrefix)) {
              // RL: note, it should also be true that uri.equals(attvalue)
              // if not, then the parser is boken.
              // further, declaredNS should be exactly the same as ns
              // so the following should, in fact do nothing.
              element.setNamespace(declaredNS);
            }
            else {
              factory.addNamespaceDeclaration(element, declaredNS);
            }
          }
        }

        // Add attributes
        for (int i = 0; i < attsize; i++) {
          Attr att = (Attr)attributeList.item(i);

          String attname = att.getName();

          if (!attname.startsWith(NS_PREFIX_XMLNS)) {
            String attPrefix = NS_PREFIX_DEFAULT;
            String attLocalName = attname;
            colon = attname.indexOf(':');
            if (colon >= 0) {
              attPrefix = attname.substring(0, colon);
              attLocalName = attname.substring(colon + 1);
            }

            String attvalue = att.getValue();

            // Get attribute's namespace
            Namespace attNS = null;
            String attURI = att.getNamespaceURI();
            if (attPrefix.isEmpty() && (attURI == null || NS_URI_DEFAULT.equals(attURI))) {
              attNS = Namespace.NO_NAMESPACE;
            }
            else {
              // various conditions can lead here.
              // the logical one is that we have a prefix for the
              // attribute, and also a namespace URI.
              // The alternative to that is in some conditions,
              // the parser could have a 'default' or 'fixed'
              // attribute that comes from an XSD used for
              // validation. In that case, there may not be a prefix
              // There's also the possibility the DOM contains
              // garbage.
              if (attPrefix.length() > 0) {
                // If the att has a prefix, we can assume that
                // the DOM is valid, and we can just use the prefix.
                // if this prefix conflicts with some other namespace
                // then we re-declare it. If redeclaring it screws up
                // other attributes in this Element, then the DOM
                // was broken to start with.
                if (attURI == null) {
                  // this can happen when the DOM is created
                  // without being namespace aware. we have a
                  // prefix, but the URI is not embedded in
                  // the Attribute itself. It must be declared
                  // on the element somewhere....
                  // https://github.com/hunterhacker/jdom/issues/138
                  attNS = element.getNamespace(attPrefix);
                }
                else {
                  attNS = Namespace.getNamespace(attPrefix, attURI);
                }
              }
              else {
                // OK, no prefix.
                // must be a defaulted value from an XSD.
                // perhaps we can find the namespace in our
                // element's ancestry, and use the prefix from that.
                HashMap<String, Namespace> tmpmap = new HashMap<>();
                for (Namespace nss : element.getNamespacesInScope()) {
                  if (nss.getPrefix().length() > 0 && nss.getURI().equals(attURI)) {
                    attNS = nss;
                    break;
                  }
                  tmpmap.put(nss.getPrefix(), nss);
                }
                if (attNS == null) {
                  // we cannot find a 'prevailing' namespace that has a prefix
                  // that is for this namespace.
                  // This basically means that there's an XMLSchema, for the
                  // DEFAULT namespace, and there's a defaulted/fixed
                  // attribute definition in the XMLSchema that's targeted
                  // for this namespace,... but, the user has either not
                  // declared a prefixed version of the namespace, or has
                  // re-declared the same prefix at a lower level with a
                  // different namespace.
                  // All of these things are possible.
                  // Create some sort of default prefix.
                  int cnt = 0;
                  String base = "attns";
                  String pfx = base + cnt;
                  while (tmpmap.containsKey(pfx)) {
                    cnt++;
                    pfx = base + cnt;
                  }
                  attNS = Namespace.getNamespace(pfx, attURI);
                }
              }
            }

            Attribute attribute =
              factory.attribute(attLocalName, attvalue, attNS);
            factory.setAttribute(element, attribute);
          }
        }

        // Recurse on child nodes
        // The list should never be null nor should it ever contain
        // null nodes, but some DOM impls are broken
        NodeList children = node.getChildNodes();
        int size = children.getLength();
        for (int i = 0; i < size; i++) {
          Node item = children.item(i);
          if (item != null) {
            buildTree(item, doc, element, false);
          }
        }
        break;

      case Node.TEXT_NODE:
        factory.addContent(current, build((Text)node));
        break;

      case Node.CDATA_SECTION_NODE:
        factory.addContent(current, build((CDATASection)node));
        break;


      case Node.PROCESSING_INSTRUCTION_NODE:

      case Node.COMMENT_NODE:
        break;

      case Node.ENTITY_REFERENCE_NODE:
        factory.addContent(current, build((EntityReference)node));
        break;

      case Node.ENTITY_NODE:
        // ??
        break;

      case Node.DOCUMENT_TYPE_NODE:

        factory.addContent(doc, build((DocumentType)node));
        break;
    }
  }
private void buildTree(Node node,
                         Document doc,
                         Element current,
                         boolean atRoot)