package com.sun.identity.shared.xml;

import com.sun.identity.shared.configuration.SystemPropertiesManager;
import com.sun.identity.shared.debug.Debug;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.ErrorHandler;
import org.xml.sax.SAXParseException;

public class XMLUtils {
   private static boolean validating;

   public static Document toDOMDocument(String xmlString, Debug debug) {
      if (xmlString != null && !xmlString.isEmpty()) {
         ByteArrayInputStream is = new ByteArrayInputStream(xmlString.getBytes(StandardCharsets.UTF_8));
         return toDOMDocument(is, debug);
      } else {
         return null;
      }
   }

   public static Document toDOMDocument(InputStream is, Debug debug) {
      DocumentBuilderFactory dbFactory;
      try {
         dbFactory = DocumentBuilderFactory.newInstance();
         dbFactory.setValidating(validating);
         dbFactory.setNamespaceAware(true);
         dbFactory.setExpandEntityReferences(false);
         dbFactory.setXIncludeAware(false);
         dbFactory.setFeature("http://xml.org/sax/features/validation", validating);
         dbFactory.setFeature("http://xml.org/sax/features/external-general-entities", false);
         dbFactory.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
         dbFactory.setFeature("http://apache.org/xml/features/nonvalidating/load-dtd-grammar", false);
         dbFactory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
      } catch (Exception e) {
         if (debug != null) {
            debug.error("XMLUtils.DocumentBuilder init failed", e);
         }

         return null;
      }

      try {
         DocumentBuilder documentBuilder = dbFactory.newDocumentBuilder();
         if (documentBuilder == null) {
            if (debug != null) {
               debug.error("XMLUtils.toDOM : null builder instance");
            }

            return null;
         } else {
            documentBuilder.setEntityResolver(new XMLHandler());
            if (debug != null && debug.warningEnabled()) {
               documentBuilder.setErrorHandler(new XMLUtils.ValidationErrorHandler(debug));
            }

            return documentBuilder.parse(is);
         }
      } catch (Exception e) {
         if (debug != null && debug.warningEnabled()) {
            debug.warning("Can't parse the XML document", e);
         }

         return null;
      }
   }

   public static Map<String, Set<String>> parseAttributeValuePairTags(Node parentNode) {
      NodeList avList = parentNode.getChildNodes();
      Map<String, Set<String>> map = null;
      int numAVPairs = avList.getLength();
      if (numAVPairs <= 0) {
         return Collections.emptyMap();
      }

      for (int l = 0; l < numAVPairs; l++) {
         Node avPair = avList.item(l);
         if (avPair.getNodeType() == 1 && avPair.getNodeName().equals("AttributeValuePair")) {
            NodeList leafNodeList = avPair.getChildNodes();
            long numLeafNodes = leafNodeList.getLength();
            if (numLeafNodes >= 2L) {
               Set<String> values = null;
               Node attributeNode = null;

               for (int i = 0; i < numLeafNodes; i++) {
                  attributeNode = leafNodeList.item(i);
                  if (attributeNode.getNodeType() == 1 && attributeNode.getNodeName().equals("Attribute")) {
                     i = (int)numLeafNodes;
                  }
               }

               String key = ((Element)attributeNode).getAttribute("name");

               for (int m = 0; m < numLeafNodes; m++) {
                  Node valueNode = leafNodeList.item(m);
                  if (valueNode.getNodeType() == 1 && valueNode.getNodeName().equals("Value")) {
                     if (values == null) {
                        values = new HashSet<>();
                     }

                     Node fchild = valueNode.getFirstChild();
                     if (fchild != null) {
                        String value = fchild.getNodeValue();
                        if (value != null) {
                           values.add(value.trim());
                        }
                     }
                  }
               }

               if (values != null) {
                  if (map == null) {
                     map = new HashMap<>();
                  }

                  Set<String> oldValues = map.get(key);
                  if (oldValues != null) {
                     values.addAll(oldValues);
                  }

                  map.put(key, values);
               }
            }
         }
      }

      return map == null ? Collections.emptyMap() : map;
   }

   public static Node getRootNode(Document doc, String nodeName) {
      NodeList nodes = doc.getElementsByTagName(nodeName);
      return nodes != null && nodes.getLength() != 0 ? nodes.item(0) : null;
   }

   public static String getElementValue(Element element) {
      if (element == null) {
         return null;
      }

      StringBuilder sb = new StringBuilder(1000);
      NodeList nl = element.getChildNodes();
      int length = nl.getLength();

      for (int i = 0; i < length; i++) {
         Node child = nl.item(i);
         if (child.getNodeType() == 3) {
            sb.append(child.getNodeValue());
         }
      }

      return sb.toString().trim();
   }

   public static String getChildrenValue(Element element) {
      if (element == null) {
         return null;
      }

      StringBuilder sb = new StringBuilder(1000);
      NodeList nl = element.getChildNodes();
      int length = nl.getLength();

      for (int i = 0; i < length; i++) {
         sb.append(print(nl.item(i)));
      }

      return sb.toString().trim();
   }

   public static String getElementString(Element element) {
      if (element == null) {
         return null;
      }

      StringBuilder sb = new StringBuilder(1000);
      NodeList nl = element.getChildNodes();
      int i = 0;

      for (int length = nl.getLength(); i < length; i++) {
         Node child = nl.item(i);
         if (child.getNodeType() != 3) {
            return null;
         }

         sb.append(child.getNodeValue());
      }

      return sb.toString().trim();
   }

   public static String getNodeAttributeValueNS(Node node, String namespaceURI, String attrName) {
      NamedNodeMap attrs = node.getAttributes();
      if (attrs == null) {
         return null;
      }

      Node value = attrs.getNamedItemNS(namespaceURI, attrName);
      return value == null ? null : value.getNodeValue();
   }

   public static String getValueOfValueNode(Node n) {
      return getValueOfValueNode(n, true);
   }

   private static String getValueOfValueNode(Node n, boolean unescape) {
      return getValueOfValueNodeNoTrim(n, unescape).trim();
   }

   private static String getValueOfValueNodeNoTrim(Node n, boolean unescape) {
      NodeList textNodes = n.getChildNodes();
      StringBuilder value = new StringBuilder();

      for (int j = 0; j < textNodes.getLength(); j++) {
         Node textNode = textNodes.item(j);
         String text = null;
         if (textNode.getNodeType() == 3) {
            text = textNode.getNodeValue();
         } else if (textNode.getNodeType() == 1) {
            text = print(textNode);
         }

         if (text != null && unescape) {
            value.append(unescapeSpecialCharacters(text));
         } else {
            value.append(text);
         }
      }

      return value.toString();
   }

   public static String print(Node node) {
      return print(node, "UTF-8");
   }

   public static String print(Node node, String encoding) {
      if (node == null) {
         return null;
      }

      try {
         TransformerFactory tFactory = TransformerFactory.newInstance();
         tFactory.setFeature("http://javax.xml.XMLConstants/feature/secure-processing", true);

         try {
            tFactory.setAttribute("http://javax.xml.XMLConstants/property/accessExternalDTD", "");
            tFactory.setAttribute("http://javax.xml.XMLConstants/property/accessExternalStylesheet", "");
         } catch (IllegalArgumentException var7) {
         }

         Transformer transformer = tFactory.newTransformer();
         transformer.setOutputProperty("omit-xml-declaration", "yes");
         transformer.setOutputProperty("encoding", encoding);
         DOMSource source = new DOMSource(node);
         ByteArrayOutputStream os = new ByteArrayOutputStream(2000);
         StreamResult result = new StreamResult(os);
         transformer.transform(source, result);
         return os.toString(encoding);
      } catch (Exception e) {
         return null;
      }
   }

   public static String unescapeSpecialCharacters(String text) {
      text = text.replace("&amp;", "&");
      text = text.replace("&lt;", "<");
      text = text.replace("&gt;", ">");
      text = text.replace("&quot;", "\"");
      text = text.replace("&apos;", "'");
      text = text.replace("&#xA;", "\n");
      return text.replace("&#xD;", "\r");
   }

   public static String escapeSpecialCharacters(String text) {
      String escapedText = text;
      int len = 0;
      if (text != null) {
         len = text.length();
      }

      int i = 0;
      boolean specialCharacterFound = false;

      while (i < len) {
         char c = text.charAt(i);
         if (c == '&' || c == '<' || c == '>' || c == '\'' || c == '"' || c == '\n' || c == '\r') {
            specialCharacterFound = true;
            break;
         }

         i++;
      }

      if (specialCharacterFound) {
         StringBuilder sb = new StringBuilder();
         sb.append(text, 0, i);

         for (; i < len; i++) {
            char c = text.charAt(i);
            switch (c) {
               case '\n':
                  sb.append("&#xA;");
                  break;
               case '\r':
                  sb.append("&#xD;");
                  break;
               case '"':
                  sb.append("&quot;");
                  break;
               case '&':
                  sb.append("&amp;");
                  break;
               case '\'':
                  sb.append("&apos;");
                  break;
               case '<':
                  sb.append("&lt;");
                  break;
               case '>':
                  sb.append("&gt;");
                  break;
               default:
                  sb.append(c);
            }
         }

         escapedText = sb.toString();
      }

      return escapedText;
   }

   static {
      try {
         String xmlVal = SystemPropertiesManager.get("com.iplanet.am.util.xml.validating", "off");
         String debugLevel = SystemPropertiesManager.get("com.iplanet.services.debug.level", "error");
         if (xmlVal.trim().equalsIgnoreCase("on") && (debugLevel.trim().equalsIgnoreCase("warning") || debugLevel.trim().equalsIgnoreCase("message"))) {
            validating = true;
         }
      } catch (Exception var2) {
      }
   }

   private static final class ValidationErrorHandler implements ErrorHandler {
      private final Debug debug;

      ValidationErrorHandler(Debug tmpDebug) {
         this.debug = tmpDebug;
      }

      @Override
      public void fatalError(SAXParseException spe) {
         if (this.debug != null) {
            this.debug.error("XMLUtils.fatalError", spe);
         }
      }

      @Override
      public void error(SAXParseException spe) {
         if (this.debug != null) {
            this.debug.warning("XMLUtils.error", spe);
         }
      }

      @Override
      public void warning(SAXParseException spe) {
         if (this.debug != null && this.debug.warningEnabled()) {
            this.debug.warning("XMLUtils.warning", spe);
         }
      }
   }
}
