gt:             Node child = childNodes.item(i);
context: /*
  // $Id: //open/mondrian-release/3.1/testsrc/main/mondrian/test/DiffRepository.java#2 $
  // This software is subject to the terms of the Eclipse Public License v1.0
  // Agreement, available at the following URL:
  // http://www.eclipse.org/legal/epl-v10.html.
  // Copyright (C) 2006-2009 Julian Hyde
  // All Rights Reserved.
  // You must accept the terms of that agreement to use this software.
  */
  package mondrian.test;
  
  import junit.framework.Assert;
  import junit.framework.AssertionFailedError;
  import junit.framework.ComparisonFailure;
  import org.eigenbase.xom.XMLOutput;
  import org.w3c.dom.*;
  import org.xml.sax.SAXException;
  
  import javax.xml.parsers.DocumentBuilder;
  import javax.xml.parsers.DocumentBuilderFactory;
  import javax.xml.parsers.ParserConfigurationException;
  import java.io.*;
  import java.util.*;
  import java.net.URL;
  
  import mondrian.olap.Util;
  
  /**
   * A collection of resources used by tests.
   *
   * <p>Loads files containing test input and output into memory.
   * If there are differences, writes out a log file containing the actual
   * output.
   *
   * <p>Typical usage is as follows:<ol>
   * <li>A testcase class defines a method<blockquote><code><pre>
   *
   * package com.acme.test;
   *
   * public class MyTest extends TestCase {
   *     public DiffRepository getDiffRepos() {
   *         return DiffRepository.lookup(MyTest.class);
   *     }
   *
   *     public void testToUpper() {
   *          getDiffRepos().assertEquals("${result}", "${string}");
   *     }
  
   *     public void testToLower() {
   *          getDiffRepos().assertEquals("Multi-line\nstring", "${string}");
   *     }
   * }</pre></code></blockquote>
   *
   * There is an accompanying reference file named after the class,
   * <code>com/acme/test/MyTest.ref.xml</code>:
   * <blockquote><code><pre>
   * &lt;Root&gt;
   *     &lt;TestCase name="testToUpper"&gt;
   *         &lt;Resource name="string"&gt;
   *             &lt;![CDATA[String to be converted to upper case]]&gt;
   *         &lt;/Resource&gt;
   *         &lt;Resource name="result"&gt;
   *             &lt;![CDATA[STRING TO BE CONVERTED TO UPPER CASE]]&gt;
   *         &lt;/Resource&gt;
   *     &lt;/TestCase&gt;
   *     &lt;TestCase name="testToLower"&gt;
   *         &lt;Resource name="result"&gt;
   *             &lt;![CDATA[multi-line
   * string]]&gt;
   *         &lt;/Resource&gt;
   *     &lt;/TestCase&gt;
   * &lt;/Root&gt;
   * </pre></code></blockquote>
   *
   * <p>If any of the testcases fails, a log file is generated, called
   * <code>com/acme/test/MyTest.log.xml</code> containing the actual output.
   * The log file is otherwise identical to the reference log, so once the
   * log file has been verified, it can simply be copied over to become the new
   * reference log.</p>
   *
   * <p>If a resource or testcase does not exist, <code>DiffRepository</code>
   * creates them in the log file. Because DiffRepository is so forgiving, it is
   * very easy to create new tests and testcases.</p>
   *
   * <p>The {@link #lookup} method ensures that all test cases share the same
   * instance of the repository. This is important more than one one test case
   * fails. The shared instance ensures that the generated <code>.log.xml</code>
   * file contains the actual for <em>both</em> test cases.
   *
   * @author jhyde
   * @version $Id: //open/mondrian-release/3.1/testsrc/main/mondrian/test/DiffRepository.java#2 $
   */
  public class DiffRepository
  {
      private final DiffRepository baseRepos;
      private final DocumentBuilder docBuilder;
      private Document doc;
      private final Element root;
      private final File refFile;
      private final File logFile;
  
      /*
      Example XML document:
  
      <Root>
          <TestCase name="testFoo">
              <Resource name="sql">
                  <![CDATA[select * from emps]]>
              </Resource>
              <Resource name="plan">
                  <![CDATA[MockTableImplRel.FENNEL_EXEC(table=[SALES, EMP])]]>
              </Resource>
          </TestCase>
          <TestCase name="testBar">
              <Resource name="sql">
                  <![CDATA[select * from depts where deptno = 10]]>
              </Resource>
              <Resource name="output">
                  <![CDATA[10, 'Sales']]>
              </Resource>
          </TestCase>
      </Root>
      */
      private static final String RootTag = "Root";
      private static final String TestCaseTag = "TestCase";
      private static final String TestCaseNameAttr = "name";
      private static final String ResourceTag = "Resource";
      private static final String ResourceNameAttr = "name";
      private static final String ResourceSqlDialectAttr = "dialect";
  
      private static final ThreadLocal<String> CurrentTestCaseName =
          new ThreadLocal<String>();
  
      /**
       * Holds one diff-repository per class. It is necessary for all testcases
       * in the same class to share the same diff-repository: if the
       * repos gets loaded once per testcase, then only one diff is recorded.
       */
      private static final Map<Class, DiffRepository> mapClassToRepos =
          new HashMap<Class, DiffRepository>();
  
      /**
       * Default prefix directories.
       */
      private static final String[] DefaultPrefixes = {"testsrc", "main"};
  
      private static File findFile(
          Class clazz, String[] prefixes, final String suffix)
      {
          // The reference file for class "com.foo.Bar" is "com/foo/Bar.ref.xml"
          String rest =
              clazz.getName().replace('.', File.separatorChar) + suffix;
          File fileBase = getFileBase(clazz, prefixes);
          return new File(fileBase, rest);
      }
  
      /**
       * Returns the base directory relative to which test logs are stored.
       *
       * <p>Deduces the directory based upon the current directory.
       * If the current directory is "/home/jhyde/open/mondrian/intellij",
       * returns "/home/jhyde/open/mondrian/testsrc".
       */
      private static File getFileBase(Class clazz, String[] prefixes)
      {
          String javaFileName =
              clazz.getName().replace('.', File.separatorChar) + ".java";
          File file = new File(System.getProperty("user.dir"));
          while (true) {
              File file2 = file;
              for (String prefix : prefixes) {
                  file2 = new File(file2, prefix);
              }
              if (file2.isDirectory()
                  && new File(file2, javaFileName).exists())
              {
                  return file2;
              }
  
              file = file.getParentFile();
              if (file == null) {
                  throw new RuntimeException("cannot find base dir");
              }
          }
      }
  
      /**
       * Creates a DiffRepository from a pair of files.
       *
       * @param refFile File containing reference results
       * @param logFile File to contain the actual output of the test run
       * @param baseRepos Base repository to inherit from, or null
       * @param prefixes List of directories to search in, or null
       */
      public DiffRepository(
          File refFile, File logFile, DiffRepository baseRepos,
          String[] prefixes)
      {
          if (prefixes == null) {
              prefixes = DefaultPrefixes;
          }
          this.baseRepos = baseRepos;
          if (refFile == null) {
              throw new IllegalArgumentException("url must not be null");
          }
          this.refFile = refFile;
          Util.discard(this.refFile);
          this.logFile = logFile;
  
          // Load the document.
          DocumentBuilderFactory fac = DocumentBuilderFactory.newInstance();
          try {
              this.docBuilder = fac.newDocumentBuilder();
              if (refFile.exists()) {
                  // Parse the reference file.
                  this.doc = docBuilder.parse(new FileInputStream(refFile));
                  // Don't write a log file yet -- as far as we know, it's still
                  // identical.
              } else {
                  // There's no reference file. Create and write a log file.
                  this.doc = docBuilder.newDocument();
                  this.doc.appendChild(
                      doc.createElement(RootTag));
                  flushDoc();
              }
              this.root = doc.getDocumentElement();
              if (!root.getNodeName().equals(RootTag)) {
                  throw new RuntimeException(
                      "expected root element of type '" + RootTag
                      + "', but found '" + root.getNodeName() + "'");
              }
          } catch (ParserConfigurationException e) {
              throw Util.newInternal(e, "error while creating xml parser");
          } catch (IOException e) {
              throw Util.newInternal(e, "error while creating xml parser");
          } catch (SAXException e) {
              throw Util.newInternal(e, "error while creating xml parser");
          }
      }
  
      /**
       * Creates a read-only repository reading from a URL.
       *
       * @param refUrl URL pointing to reference file
       */
      public DiffRepository(URL refUrl)
      {
          this.refFile = null;
          this.logFile = null;
          this.baseRepos = null;
  
          // Load the document.
          DocumentBuilderFactory fac = DocumentBuilderFactory.newInstance();
          try {
              this.docBuilder = fac.newDocumentBuilder();
              // Parse the reference file.
              this.doc = docBuilder.parse(refUrl.openStream());
              this.root = doc.getDocumentElement();
              if (!root.getNodeName().equals(RootTag)) {
                  throw new RuntimeException(
                      "expected root element of type '" + RootTag
                      + "', but found '" + root.getNodeName() + "'");
              }
          } catch (ParserConfigurationException e) {
              throw Util.newInternal(e, "error while creating xml parser");
          } catch (IOException e) {
              throw Util.newInternal(e, "error while creating xml parser");
          } catch (SAXException e) {
              throw Util.newInternal(e, "error while creating xml parser");
          }
      }
  
      /**
       * Expands a string containing one or more variables.
       * (Currently only works if there is one variable.)
       */
      public String expand(String tag, String text)
      {
          if (text == null) {
              return null;
          } else if (text.startsWith("${")
              && text.endsWith("}"))
          {
              final String testCaseName = getCurrentTestCaseName(true);
              final String token = text.substring(2, text.length() - 1);
              if (tag == null) {
                  tag = token;
              }
              assert token.startsWith(tag)
                  : "token '" + token
                  + "' does not match tag '" + tag + "'";
              final String expanded = get(testCaseName, token);
              if (expanded == null) {
                  // Token is not specified. Return the original text: this will
                  // cause a diff, and the actual value will be written to the
                  // log file.
                  return text;
              }
              return expanded;
          } else {
              // Make sure what appears in the resource file is consistent with
              // what is in the Java. It helps to have a redundant copy in the
              // resource file.
              final String testCaseName = getCurrentTestCaseName(true);
              if (baseRepos != null
                  && baseRepos.get(testCaseName, tag) != null)
              {
                  // set in base repos; don't override
              } else {
                  set(tag, text);
              }
              return text;
          }
      }
  
      /**
       * Sets the value of a given resource of the current testcase.
       *
       * @param resourceName Name of the resource, e.g. "sql"
       * @param value Value of the resource
       */
      public synchronized void set(String resourceName, String value)
      {
          assert resourceName != null;
          final String testCaseName = getCurrentTestCaseName(true);
          update(testCaseName, resourceName, value);
      }
  
      public void amend(String expected, String actual)
      {
          if (expected.startsWith("${")
              && expected.endsWith("}"))
          {
              String token = expected.substring(2, expected.length() - 1);
              set(token, actual);
          } else {
              // do nothing
          }
      }
  
      public synchronized String get(
          final String testCaseName,
          String resourceName)
      {
          return get(testCaseName, resourceName, null);
      }
  
      /**
       * Returns a given resource from a given testcase.
       *
       * @param testCaseName Name of test case, e.g. "testFoo"
       * @param resourceName Name of resource, e.g. "sql", "plan"
       * @param dialectName Name of sql dialect, e.g. "MYSQL", "LUCIDDB"
       * @return The value of the resource, or null if not found
       */
      public synchronized String get(
          final String testCaseName,
          String resourceName,
          String dialectName)
      {
          Element testCaseElement =
              getTestCaseElement(root, testCaseName);
          if (testCaseElement == null) {
              if (baseRepos != null) {
                  return baseRepos.get(testCaseName, resourceName, dialectName);
              } else {
                  return null;
              }
          }
          final Element resourceElement =
              getResourceElement(testCaseElement, resourceName, dialectName);
          if (resourceElement != null) {
              return getText(resourceElement);
          }
          return null;
      }
  
      /**
       * Returns the text under an element.
       */
      private static String getText(Element element)
      {
          // If there is a <![CDATA[ ... ]]> child, return its text and ignore
          // all other child elements.
          final NodeList childNodes = element.getChildNodes();
          for (int i = 0; i < childNodes.getLength(); i++) {
              Node node = childNodes.item(i);
              if (node instanceof CDATASection) {
                  return node.getNodeValue();
              }
          }
          // Otherwise return all the text under this element (including
          // whitespace).
          StringBuilder buf = new StringBuilder();
          for (int i = 0; i < childNodes.getLength(); i++) {
              Node node = childNodes.item(i);
              if (node instanceof Text) {
                  buf.append(((Text) node).getData());
              }
          }
          return buf.toString();
      }
  
      /**
       * Returns the &lt;TestCase&gt; element corresponding to the current
       * test case.
       *
       * @param root Root element of the document
       * @param testCaseName Name of test case
       * @return TestCase element, or null if not found
       */
      private static Element getTestCaseElement(
          final Element root,
          final String testCaseName)
      {
          final NodeList childNodes = root.getChildNodes();
          for (int i = 0; i < childNodes.getLength(); i++) {
  