// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

package org.chromium.sdk.tests.system.runner;

import java.util.AbstractList;

import org.w3c.dom.Attr;
import org.w3c.dom.CDATASection;
import org.w3c.dom.Comment;
import org.w3c.dom.Document;
import org.w3c.dom.DocumentFragment;
import org.w3c.dom.DocumentType;
import org.w3c.dom.Element;
import org.w3c.dom.Entity;
import org.w3c.dom.EntityReference;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.dom.Notation;
import org.w3c.dom.ProcessingInstruction;
import org.w3c.dom.Text;

/**
 * Utility class that adapts org.w3c.dom package to Java.
 */
public class DomUtils {

  static String getTextParameter(Element baseElement, final String name) {
    for (Node node : iterableNodes(baseElement.getChildNodes())) {
      String value = visitNode(node, new DefaultVisitor<String>() {
        @Override public String visitElement(Element element) {
          if (!element.getTagName().equals(name)) {
            return null;
          }
          return readTextParameter(element);
        }
        @Override protected String visitDefault(Node node1) {
          throw new RuntimeException();
        }
      });
      if (value != null) {
        return value;
      }
    }
    return null;
  }

  static String readTextParameter(Element element) {
    NodeList list = element.getChildNodes();
    if (list.getLength() != 1) {
      throw new RuntimeException();
    }
    return visitNode(list.item(0), GET_TEXT_CONTENT_VISITOR);
  }

  private static final DefaultVisitor<String> GET_TEXT_CONTENT_VISITOR =
        new DefaultVisitor<String>() {
        @Override public String visitText(Text node) {
          return node.getTextContent();
        }
        @Override protected String visitDefault(Node node) {
          throw new RuntimeException();
        }
      };

  interface NodeVisitor<R> {
    R visitElement(Element node);
    R visitAttr(Attr node);
    R visitText(Text node);
    R visitCDATASection(CDATASection node);
    R visitEntityReference(EntityReference node);
    R visitEntity(Entity node);
    R visitProcessingInstruction(ProcessingInstruction node);
    R visitComment(Comment node);
    R visitDocument(Document node);
    R visitDocumentType(DocumentType node);
    R visitDocumentFragment(DocumentFragment node);
    R visitNotation(Notation node);
  }

  static abstract class DefaultVisitor<R> implements NodeVisitor<R> {
    @Override public R visitElement(Element node) {
      return visitDefault(node);
    }
    @Override public R visitAttr(Attr node) {
      return visitDefault(node);
    }
    @Override public R visitText(Text node) {
      return visitDefault(node);
    }
    @Override public R visitCDATASection(CDATASection node) {
      return visitDefault(node);
    }
    @Override public R visitEntityReference(EntityReference node) {
      return visitDefault(node);
    }
    @Override public R visitEntity(Entity node) {
      return visitDefault(node);
    }
    @Override public R visitProcessingInstruction(ProcessingInstruction node) {
      return visitDefault(node);
    }
    @Override public R visitComment(Comment node) {
      return visitDefault(node);
    }
    @Override public R visitDocument(Document node) {
      return visitDefault(node);
    }
    @Override public R visitDocumentType(DocumentType node) {
      return visitDefault(node);
    }
    @Override public R visitDocumentFragment(DocumentFragment node) {
      return visitDefault(node);
    }
    @Override public R visitNotation(Notation node) {
      return visitDefault(node);
    }
    protected abstract R visitDefault(Node node);
  }

  static <R> R visitNode(Node node, NodeVisitor<R> visitor) {
    switch (node.getNodeType()) {
      case Node.ELEMENT_NODE: return visitor.visitElement((Element) node);
      case Node.ATTRIBUTE_NODE: return visitor.visitAttr((Attr) node);
      case Node.TEXT_NODE: return visitor.visitText((Text) node);
      case Node.CDATA_SECTION_NODE: return visitor.visitCDATASection((CDATASection) node);
      case Node.ENTITY_REFERENCE_NODE: return visitor.visitEntityReference((EntityReference) node);
      case Node.ENTITY_NODE: return visitor.visitEntity((Entity) node);
      case Node.PROCESSING_INSTRUCTION_NODE:
          return visitor.visitProcessingInstruction((ProcessingInstruction) node);
      case Node.COMMENT_NODE: return visitor.visitComment((Comment) node);
      case Node.DOCUMENT_NODE: return visitor.visitDocument((Document) node);
      case Node.DOCUMENT_TYPE_NODE: return visitor.visitDocumentType((DocumentType) node);
      case Node.DOCUMENT_FRAGMENT_NODE:
          return visitor.visitDocumentFragment((DocumentFragment) node);
      case Node.NOTATION_NODE: return visitor.visitNotation((Notation) node);
      default: throw new RuntimeException();
    }
  }

  static Iterable<Node> iterableNodes(final NodeList nodeList) {
    return new AbstractList<Node>() {
      @Override public Node get(int index) {
        return nodeList.item(index);
      }
      @Override public int size() {
        return nodeList.getLength();
      }
    };
  }
}
