content: /*
   * Licensed to the Apache Software Foundation (ASF) under one or more
   * contributor license agreements.  See the NOTICE file distributed with
   * this work for additional information regarding copyright ownership.
   * The ASF licenses this file to you under the Apache License, Version 2.0
   * (the "License"); you may not use this file except in compliance with
   * the License.  You may obtain a copy of the License at
   *
   * http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  package org.apache.calcite.rel.externalize;
  
  import org.apache.calcite.rel.RelNode;
  import org.apache.calcite.sql.SqlExplainLevel;
  import org.apache.calcite.util.Pair;
  import org.apache.calcite.util.XmlOutput;
  
  import org.checkerframework.checker.nullness.qual.Nullable;
  
  import java.io.PrintWriter;
  import java.util.ArrayList;
  import java.util.List;
  import java.util.Objects;
  
  /**
   * Callback for a relational expression to dump in XML format.
   */
  public class RelXmlWriter extends RelWriterImpl {
    //~ Instance fields --------------------------------------------------------
  
    private final XmlOutput xmlOutput;
    final boolean generic = true;
  
    //~ Constructors -----------------------------------------------------------
  
    // TODO jvs 23-Dec-2005:  honor detail level.  The current inheritance
    // structure makes this difficult without duplication; need to factor
    // out the filtering of attributes before rendering.
  
    public RelXmlWriter(PrintWriter pw, SqlExplainLevel detailLevel) {
      super(pw, detailLevel, true);
      xmlOutput = new XmlOutput(pw);
      xmlOutput.setGlob(true);
      xmlOutput.setCompact(false);
    }
  
    //~ Methods ----------------------------------------------------------------
  
    @Override protected void explain_(
        RelNode rel,
        List<Pair<String, @Nullable Object>> values) {
      if (generic) {
        explainGeneric(rel, values);
      } else {
        explainSpecific(rel, values);
      }
    }
  
    /**
     * Generates generic XML (sometimes called 'element-oriented XML'). Like
     * this:
     *
     * <blockquote>
     * <code>
     * &lt;RelNode id="1" type="Join"&gt;<br>
     * &nbsp;&nbsp;&lt;Property name="condition"&gt;EMP.DEPTNO =
     * DEPT.DEPTNO&lt;/Property&gt;<br>
     * &nbsp;&nbsp;&lt;Inputs&gt;<br>
     * &nbsp;&nbsp;&nbsp;&nbsp;&lt;RelNode id="2" type="Project"&gt;<br>
     * &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;Property name="expr1"&gt;x +
     * y&lt;/Property&gt;<br>
     * &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;Property
     * name="expr2"&gt;45&lt;/Property&gt;<br>
     * &nbsp;&nbsp;&nbsp;&nbsp;&lt;/RelNode&gt;<br>
     * &nbsp;&nbsp;&nbsp;&nbsp;&lt;RelNode id="3" type="TableAccess"&gt;<br>
     * &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;Property
     * name="table"&gt;SALES.EMP&lt;/Property&gt;<br>
     * &nbsp;&nbsp;&nbsp;&nbsp;&lt;/RelNode&gt;<br>
     * &nbsp;&nbsp;&lt;/Inputs&gt;<br>
     * &lt;/RelNode&gt;</code>
     * </blockquote>
     *
     * @param rel    Relational expression
     * @param values List of term-value pairs
     */
    private void explainGeneric(
        RelNode rel,
        List<Pair<String, @Nullable Object>> values) {
      String relType = rel.getRelTypeName();
      xmlOutput.beginBeginTag("RelNode");
      xmlOutput.attribute("type", relType);
  
      xmlOutput.endBeginTag("RelNode");
  
      final List<RelNode> inputs = new ArrayList<>();
      for (Pair<String, @Nullable Object> pair : values) {
        if (pair.right instanceof RelNode) {
          inputs.add((RelNode) pair.right);
          continue;
        }
        if (pair.right == null) {
          continue;
        }
        xmlOutput.beginBeginTag("Property");
        xmlOutput.attribute("name", pair.left);
        xmlOutput.endBeginTag("Property");
        xmlOutput.cdata(pair.right.toString());
        xmlOutput.endTag("Property");
      }
      xmlOutput.beginTag("Inputs", null);
      spacer.add(2);
      for (RelNode input : inputs) {
        input.explain(this);
      }
      spacer.subtract(2);
      xmlOutput.endTag("Inputs");
      xmlOutput.endTag("RelNode");
    }
  
    /**
     * Generates specific XML (sometimes called 'attribute-oriented XML'). Like
     * this:
     *
     * <blockquote><pre>
     * &lt;Join condition="EMP.DEPTNO = DEPT.DEPTNO"&gt;
     *   &lt;Project expr1="x + y" expr2="42"&gt;
     *   &lt;TableAccess table="SALES.EMPS"&gt;
     * &lt;/Join&gt;
     * </pre></blockquote>
     *
     * @param rel    Relational expression
     * @param values List of term-value pairs
     */
    private void explainSpecific(
        RelNode rel,
        List<Pair<String, @Nullable Object>> values) {
      String tagName = rel.getRelTypeName();
      xmlOutput.beginBeginTag(tagName);
      xmlOutput.attribute("id", rel.getId() + "");
  
      for (Pair<String, @Nullable Object> value : values) {
        if (value.right instanceof RelNode) {
          continue;
        }
        xmlOutput.attribute(
            value.left,
            Objects.toString(value.right));
      }
      xmlOutput.endBeginTag(tagName);
      spacer.add(2);
      for (RelNode input : rel.getInputs()) {
        input.explain(this);
      }
      spacer.subtract(2);
    }
  }
  