package org.apache.logging.log4j.core.config.xml;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.Source;
import javax.xml.transform.stream.StreamSource;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;
import javax.xml.validation.Validator;
import org.apache.logging.log4j.core.config.AbstractConfiguration;
import org.apache.logging.log4j.core.config.Configuration;
import org.apache.logging.log4j.core.config.ConfigurationSource;
import org.apache.logging.log4j.core.config.FileConfigurationMonitor;
import org.apache.logging.log4j.core.config.Node;
import org.apache.logging.log4j.core.config.Reconfigurable;
import org.apache.logging.log4j.core.config.plugins.util.PluginManager;
import org.apache.logging.log4j.core.config.plugins.util.PluginType;
import org.apache.logging.log4j.core.config.plugins.util.ResolverUtil;
import org.apache.logging.log4j.core.config.status.StatusConfiguration;
import org.apache.logging.log4j.core.util.Loader;
import org.apache.logging.log4j.core.util.Patterns;
import org.w3c.dom.Attr;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.NodeList;
import org.w3c.dom.Text;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

public class XmlConfiguration extends AbstractConfiguration implements Reconfigurable {
   private static final String XINCLUDE_FIXUP_LANGUAGE = "http://apache.org/xml/features/xinclude/fixup-language";
   private static final String XINCLUDE_FIXUP_BASE_URIS = "http://apache.org/xml/features/xinclude/fixup-base-uris";
   private static final String[] VERBOSE_CLASSES = new String[]{ResolverUtil.class.getName()};
   private static final String LOG4J_XSD = "Log4j-config.xsd";
   private final List<XmlConfiguration.Status> status = new ArrayList();
   private Element rootElement;
   private boolean strict;
   private String schema;

   static DocumentBuilder newDocumentBuilder() throws ParserConfigurationException {
      DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
      factory.setNamespaceAware(true);
      enableXInclude(factory);
      return factory.newDocumentBuilder();
   }

   private static void enableXInclude(DocumentBuilderFactory factory) {
      try {
         factory.setXIncludeAware(true);
      } catch (UnsupportedOperationException var6) {
         LOGGER.warn("The DocumentBuilderFactory does not support XInclude: {}", factory, var6);
      } catch (AbstractMethodError var7) {
         LOGGER.warn("The DocumentBuilderFactory is out of date and does not support XInclude: {}", factory, var7);
      }

      try {
         factory.setFeature("http://apache.org/xml/features/xinclude/fixup-base-uris", true);
      } catch (ParserConfigurationException var4) {
         LOGGER.warn("The DocumentBuilderFactory [{}] does not support the feature [{}].", factory, "http://apache.org/xml/features/xinclude/fixup-base-uris", var4);
      } catch (AbstractMethodError var5) {
         LOGGER.warn("The DocumentBuilderFactory is out of date and does not support setFeature: {}", factory, var5);
      }

      try {
         factory.setFeature("http://apache.org/xml/features/xinclude/fixup-language", true);
      } catch (ParserConfigurationException var2) {
         LOGGER.warn("The DocumentBuilderFactory [{}] does not support the feature [{}].", factory, "http://apache.org/xml/features/xinclude/fixup-language", var2);
      } catch (AbstractMethodError var3) {
         LOGGER.warn("The DocumentBuilderFactory is out of date and does not support setFeature: {}", factory, var3);
      }

   }

   public XmlConfiguration(ConfigurationSource configSource) {
      super(configSource);
      File configFile = configSource.getFile();
      byte[] buffer = null;

      InputStream is;
      try {
         is = configSource.getInputStream();

         try {
            buffer = toByteArray(is);
         } finally {
            is.close();
         }

         InputSource source = new InputSource(new ByteArrayInputStream(buffer));
         source.setSystemId(configSource.getLocation());
         Document document = newDocumentBuilder().parse(source);
         this.rootElement = document.getDocumentElement();
         Map attrs = this.processAttributes(this.rootNode, this.rootElement);
         StatusConfiguration statusConfig = (new StatusConfiguration()).withVerboseClasses(VERBOSE_CLASSES).withStatus(this.getDefaultStatus());
         Iterator i$ = attrs.entrySet().iterator();

         while(true) {
            while(i$.hasNext()) {
               Entry<String, String> entry = (Entry)i$.next();
               String key = (String)entry.getKey();
               String value = this.getStrSubstitutor().replace((String)entry.getValue());
               if ("status".equalsIgnoreCase(key)) {
                  statusConfig.withStatus(value);
               } else if ("dest".equalsIgnoreCase(key)) {
                  statusConfig.withDestination(value);
               } else if ("shutdownHook".equalsIgnoreCase(key)) {
                  this.isShutdownHookEnabled = !"disable".equalsIgnoreCase(value);
               } else if ("verbose".equalsIgnoreCase(key)) {
                  statusConfig.withVerbosity(value);
               } else if ("packages".equalsIgnoreCase(key)) {
                  String[] packages = value.split(Patterns.COMMA_SEPARATOR);
                  String[] arr$ = packages;
                  int len$ = packages.length;

                  for(int i$ = 0; i$ < len$; ++i$) {
                     String p = arr$[i$];
                     PluginManager.addPackage(p);
                  }
               } else if ("name".equalsIgnoreCase(key)) {
                  this.setName(value);
               } else if ("strict".equalsIgnoreCase(key)) {
                  this.strict = Boolean.parseBoolean(value);
               } else if ("schema".equalsIgnoreCase(key)) {
                  this.schema = value;
               } else if ("monitorInterval".equalsIgnoreCase(key)) {
                  int interval = Integer.parseInt(value);
                  if (interval > 0 && configFile != null) {
                     this.monitor = new FileConfigurationMonitor(this, configFile, this.listeners, interval);
                  }
               } else if ("advertiser".equalsIgnoreCase(key)) {
                  this.createAdvertiser(value, configSource, buffer, "text/xml");
               }
            }

            statusConfig.initialize();
            break;
         }
      } catch (SAXException var31) {
         LOGGER.error((String)("Error parsing " + configSource.getLocation()), (Throwable)var31);
      } catch (IOException var32) {
         LOGGER.error((String)("Error parsing " + configSource.getLocation()), (Throwable)var32);
      } catch (ParserConfigurationException var33) {
         LOGGER.error((String)("Error parsing " + configSource.getLocation()), (Throwable)var33);
      }

      if (this.strict && this.schema != null && buffer != null) {
         is = null;

         try {
            is = Loader.getResourceAsStream(this.schema, XmlConfiguration.class.getClassLoader());
         } catch (Exception var29) {
            LOGGER.error("Unable to access schema {}", this.schema, var29);
         }

         if (is != null) {
            Source src = new StreamSource(is, "Log4j-config.xsd");
            SchemaFactory factory = SchemaFactory.newInstance("http://www.w3.org/2001/XMLSchema");
            Schema schema = null;

            try {
               schema = factory.newSchema(src);
            } catch (SAXException var28) {
               LOGGER.error((String)"Error parsing Log4j schema", (Throwable)var28);
            }

            if (schema != null) {
               Validator validator = schema.newValidator();

               try {
                  validator.validate(new StreamSource(new ByteArrayInputStream(buffer)));
               } catch (IOException var26) {
                  LOGGER.error((String)"Error reading configuration for validation", (Throwable)var26);
               } catch (SAXException var27) {
                  LOGGER.error((String)"Error validating configuration", (Throwable)var27);
               }
            }
         }
      }

      if (this.getName() == null) {
         this.setName(configSource.getLocation());
      }

   }

   public void setup() {
      if (this.rootElement == null) {
         LOGGER.error("No logging configuration");
      } else {
         this.constructHierarchy(this.rootNode, this.rootElement);
         if (this.status.size() <= 0) {
            this.rootElement = null;
         } else {
            Iterator i$ = this.status.iterator();

            while(i$.hasNext()) {
               XmlConfiguration.Status s = (XmlConfiguration.Status)i$.next();
               LOGGER.error("Error processing element " + s.name + ": " + s.errorType);
            }

         }
      }
   }

   public Configuration reconfigure() {
      try {
         ConfigurationSource source = this.getConfigurationSource().resetInputStream();
         if (source == null) {
            return null;
         } else {
            XmlConfiguration config = new XmlConfiguration(source);
            return config.rootElement == null ? null : config;
         }
      } catch (IOException var3) {
         LOGGER.error((String)("Cannot locate file " + this.getConfigurationSource()), (Throwable)var3);
         return null;
      }
   }

   private void constructHierarchy(Node node, Element element) {
      this.processAttributes(node, element);
      StringBuilder buffer = new StringBuilder();
      NodeList list = element.getChildNodes();
      List<Node> children = node.getChildren();

      for(int i = 0; i < list.getLength(); ++i) {
         org.w3c.dom.Node w3cNode = list.item(i);
         if (w3cNode instanceof Element) {
            Element child = (Element)w3cNode;
            String name = this.getType(child);
            PluginType<?> type = this.pluginManager.getPluginType(name);
            Node childNode = new Node(node, name, type);
            this.constructHierarchy(childNode, child);
            if (type == null) {
               String value = childNode.getValue();
               if (!childNode.hasChildren() && value != null) {
                  node.getAttributes().put(name, value);
               } else {
                  this.status.add(new XmlConfiguration.Status(name, element, XmlConfiguration.ErrorType.CLASS_NOT_FOUND));
               }
            } else {
               children.add(childNode);
            }
         } else if (w3cNode instanceof Text) {
            Text data = (Text)w3cNode;
            buffer.append(data.getData());
         }
      }

      String text = buffer.toString().trim();
      if (text.length() > 0 || !node.hasChildren() && !node.isRoot()) {
         node.setValue(text);
      }

   }

   private String getType(Element element) {
      if (this.strict) {
         NamedNodeMap attrs = element.getAttributes();

         for(int i = 0; i < attrs.getLength(); ++i) {
            org.w3c.dom.Node w3cNode = attrs.item(i);
            if (w3cNode instanceof Attr) {
               Attr attr = (Attr)w3cNode;
               if (attr.getName().equalsIgnoreCase("type")) {
                  String type = attr.getValue();
                  attrs.removeNamedItem(attr.getName());
                  return type;
               }
            }
         }
      }

      return element.getTagName();
   }

   private Map<String, String> processAttributes(Node node, Element element) {
      NamedNodeMap attrs = element.getAttributes();
      Map<String, String> attributes = node.getAttributes();

      for(int i = 0; i < attrs.getLength(); ++i) {
         org.w3c.dom.Node w3cNode = attrs.item(i);
         if (w3cNode instanceof Attr) {
            Attr attr = (Attr)w3cNode;
            if (!attr.getName().equals("xml:base")) {
               attributes.put(attr.getName(), attr.getValue());
            }
         }
      }

      return attributes;
   }

   public String toString() {
      return this.getClass().getSimpleName() + "[location=" + this.getConfigurationSource() + "]";
   }

   private static class Status {
      private final Element element;
      private final String name;
      private final XmlConfiguration.ErrorType errorType;

      public Status(String name, Element element, XmlConfiguration.ErrorType errorType) {
         this.name = name;
         this.element = element;
         this.errorType = errorType;
      }
   }

   private static enum ErrorType {
      CLASS_NOT_FOUND;
   }
}
