gt:         List<String> result = m_diagnostics;
context: /* This file is part of VoltDB.
   * Copyright (C) 2008-2018 VoltDB Inc.
   *
   * This program is free software: you can redistribute it and/or modify
   * it under the terms of the GNU Affero General Public License as
   * published by the Free Software Foundation, either version 3 of the
   * License, or (at your option) any later version.
   *
   * This program is distributed in the hope that it will be useful,
   * but WITHOUT ANY WARRANTY; without even the implied warranty of
   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   * GNU Affero General Public License for more details.
   *
   * You should have received a copy of the GNU Affero General Public License
   * along with VoltDB.  If not, see <http://www.gnu.org/licenses/>.
   */
  
  package org.voltdb.compiler;
  
  import java.io.File;
  import java.io.FileWriter;
  import java.io.IOException;
  import java.io.PrintStream;
  import java.io.UnsupportedEncodingException;
  import java.net.URL;
  import java.net.URLDecoder;
  import java.net.URLEncoder;
  import java.util.ArrayList;
  import java.util.HashSet;
  import java.util.LinkedHashSet;
  import java.util.List;
  
  import org.voltdb.utils.MiscUtils;
  import org.voltdb.utils.NotImplementedException;
  
  /**
   * Alternate (programmatic) interface to VoltCompiler. Give the class all of
   * the information a user would put in a VoltDB project file and it will go
   * and build the project file and run the compiler on it.
   *
   * It will also create a deployment.xml file and apply its changes to the catalog.
   */
  public class CatalogBuilder {
  
      final LinkedHashSet<String> m_schemas = new LinkedHashSet<>();
      private StringBuffer transformer = new StringBuffer();
  
      public static final class ProcedureInfo {
          private final String roles[];
          private final Class<?> cls;
          private final String name;
          private final String sql;
          private final String partitionInfo;
          private final String joinOrder;
  
          public ProcedureInfo(final String roles[], final Class<?> cls) {
              this.roles = roles;
              this.cls = cls;
              this.name = cls.getSimpleName();
              this.sql = null;
              this.joinOrder = null;
              this.partitionInfo = null;
              assert(this.name != null);
          }
  
          public ProcedureInfo(
                  final String roles[],
                  final String name,
                  final String sql,
                  final String partitionInfo) {
              this(roles, name, sql, partitionInfo, null);
          }
  
          public ProcedureInfo(
                  final String roles[],
                  final String name,
                  final String sql,
                  final String partitionInfo,
                  final String joinOrder) {
              assert(name != null);
              this.roles = roles;
              this.cls = null;
              this.name = name;
              if(sql.endsWith(";")) {
                  this.sql = sql;
              }
              else {
                  this.sql = sql + ";";
              }
              this.partitionInfo = partitionInfo;
              this.joinOrder = joinOrder;
              assert(this.name != null);
          }
  
          @Override
          public int hashCode() {
              return name.hashCode();
          }
  
          @Override
          public boolean equals(final Object o) {
              if (o instanceof ProcedureInfo) {
                  final ProcedureInfo oInfo = (ProcedureInfo)o;
                  return name.equals(oInfo.name);
              }
              return false;
          }
      }
  
      final LinkedHashSet<Class<?>> m_supplementals = new LinkedHashSet<>();
  
      List<String> m_elAuthGroups;      // authorized groups
  
      PrintStream m_compilerDebugPrintStream = null;
  
      private List<String> m_diagnostics;
  
      /**
       * Produce all catalogs this project builder knows how to produce.
       * Written to allow BenchmarkController to cause compilation of multiple
       * catalogs for benchmarks that need to update running appplications and
       * consequently need multiple benchmark controlled catalog jars.
       * @param sitesPerHost
       * @param length
       * @param kFactor
       * @param voltRoot  where to put the compiled catalogs
       * @return a list of jar filenames that were compiled. The benchmark will
       * be started using the filename at index 0.
       */
      public String[] compileAllCatalogs(
              int sitesPerHost, int length,
              int kFactor, String voltRoot)
      {
          throw new NotImplementedException("This project builder does not support compileAllCatalogs");
      }
  
      public void addAllDefaults() {
          // does nothing in the base class
      }
  
      public void addSchema(final URL schemaURL) {
          assert(schemaURL != null);
          addSchema(schemaURL.getPath());
      }
  
      /**
       * This is test code written by Ryan, even though it was
       * committed by John.
       */
      public void addLiteralSchema(String ddlText) throws IOException {
          File temp = File.createTempFile("literalschema", "sql");
          temp.deleteOnExit();
          FileWriter out = new FileWriter(temp);
          out.write(ddlText);
          out.close();
          addSchema(URLEncoder.encode(temp.getAbsolutePath(), "UTF-8"));
      }
  
      /**
       * Add a schema based on a URL.
       * @param schemaURL Schema file URL
       */
      public void addSchema(String schemaURL) {
          try {
              schemaURL = URLDecoder.decode(schemaURL, "UTF-8");
          } catch (final UnsupportedEncodingException e) {
              e.printStackTrace();
              System.exit(-1);
          }
          assert(m_schemas.contains(schemaURL) == false);
          final File schemaFile = new File(schemaURL);
          assert(schemaFile != null);
          assert(schemaFile.isDirectory() == false);
          // this check below fails in some valid cases (like when the file is in a jar)
          //assert schemaFile.canRead()
          //    : "can't read file: " + schemaPath;
  
          m_schemas.add(schemaURL);
      }
  
      public void addStmtProcedure(String name, String sql) {
          addStmtProcedure(name, sql, null, null);
      }
  
      public void addStmtProcedure(String name, String sql, String partitionInfo) {
          addStmtProcedure( name, sql, partitionInfo, null);
      }
  
      public void addStmtProcedure(String name, String sql, String partitionInfo, String joinOrder) {
          addProcedures(new ProcedureInfo(new String[0], name, sql, partitionInfo, joinOrder));
      }
  
      public void addProcedures(final Class<?>... procedures) {
          final ArrayList<ProcedureInfo> procArray = new ArrayList<>();
          for (final Class<?> procedure : procedures)
              procArray.add(new ProcedureInfo(new String[0], procedure));
          addProcedures(procArray);
      }
  
      /*
       * List of roles permitted to invoke the procedure
       */
      public void addProcedures(final ProcedureInfo... procedures) {
          final ArrayList<ProcedureInfo> procArray = new ArrayList<>();
          for (final ProcedureInfo procedure : procedures)
              procArray.add(procedure);
          addProcedures(procArray);
      }
  
      public void addProcedures(final Iterable<ProcedureInfo> procedures) {
          // check for duplicates and existings
          final HashSet<ProcedureInfo> newProcs = new HashSet<>();
          for (final ProcedureInfo procedure : procedures) {
              assert(newProcs.contains(procedure) == false);
              newProcs.add(procedure);
          }
  
          // add the procs
          for (final ProcedureInfo procedure : procedures) {
  
              // ALLOW clause in CREATE PROCEDURE stmt
              StringBuffer roleInfo = new StringBuffer();
              if(procedure.roles.length != 0) {
                  roleInfo.append(" ALLOW ");
                  for(int i = 0; i < procedure.roles.length; i++) {
                      roleInfo.append(procedure.roles[i] + ",");
                  }
                  int length = roleInfo.length();
                  roleInfo.replace(length - 1, length, " ");
              }
  
              if(procedure.cls != null) {
                  transformer.append("CREATE PROCEDURE " + roleInfo.toString() + " FROM CLASS " + procedure.cls.getName() + ";");
              }
              else if(procedure.sql != null) {
                  transformer.append("CREATE PROCEDURE " + procedure.name + roleInfo.toString() + " AS " + procedure.sql);
              }
  
              if(procedure.partitionInfo != null) {
                  String[] parameter = procedure.partitionInfo.split(":");
                  String[] token = parameter[0].split("\\.");
                  String position = "";
                  if(Integer.parseInt(parameter[1].trim()) > 0) {
                      position = " PARAMETER " + parameter[1];
                  }
                  transformer.append("PARTITION PROCEDURE " + procedure.name + " ON TABLE " + token[0] + " COLUMN " + token[1] + position + ";");
              }
          }
      }
  
      public void addSupplementalClasses(final Class<?>... supplementals) {
          final ArrayList<Class<?>> suppArray = new ArrayList<>();
          for (final Class<?> supplemental : supplementals)
              suppArray.add(supplemental);
          addSupplementalClasses(suppArray);
      }
  
      public void addSupplementalClasses(final Iterable<Class<?>> supplementals) {
          // check for duplicates and existings
          final HashSet<Class<?>> newSupps = new HashSet<>();
          for (final Class<?> supplemental : supplementals) {
              assert(newSupps.contains(supplemental) == false);
              assert(m_supplementals.contains(supplemental) == false);
              newSupps.add(supplemental);
          }
  
          // add the supplemental classes
          for (final Class<?> supplemental : supplementals)
              m_supplementals.add(supplemental);
      }
  
      public void addPartitionInfo(final String tableName, final String partitionColumnName) {
          transformer.append("PARTITION TABLE " + tableName + " ON COLUMN " + partitionColumnName + ";");
      }
  
      public void addExport(List<String> groups) {
          m_elAuthGroups = groups;
      }
  
      public void setCompilerDebugPrintStream(final PrintStream out) {
          m_compilerDebugPrintStream = out;
      }
  
      public byte[] compileToBytes() {
          try {
              File jarFile = File.createTempFile("catalogasbytes", ".jar");
  
              if (!compile(new VoltCompiler(false), jarFile.getAbsolutePath())) {
                  return null;
              }
              return MiscUtils.fileToBytes(jarFile);
          } catch (Exception e) {
              e.printStackTrace();
              return null;
          }
      }
  
      public boolean compile(final String jarPath) {
          return compile(new VoltCompiler(false), jarPath);
      }
  
      public boolean compile(final VoltCompiler compiler,
                             final String jarPath)
      {
          assert(jarPath != null);
  
          // Add the DDL in the transformer to the schema files before compilation
          try {
              addLiteralSchema(transformer.toString());
              transformer = new StringBuffer();
          } catch (IOException e) {
              e.printStackTrace();
              return false;
          }
  
          String[] schemaPath = m_schemas.toArray(new String[0]);
  
          if (m_diagnostics != null) {
              compiler.enableDetailedCapture();
          }
  
          boolean success = compiler.compileFromDDL(jarPath, schemaPath);
  
          m_diagnostics = compiler.harvestCapturedDetail();
          if (m_compilerDebugPrintStream != null) {
              if (success) {
                  compiler.summarizeSuccess(m_compilerDebugPrintStream, m_compilerDebugPrintStream, jarPath);
              } else {
                  compiler.summarizeErrors(m_compilerDebugPrintStream, m_compilerDebugPrintStream);
              }
          }
          return success;
      }
  
      /**
       * Utility method to take a string and put it in a file. This is used by
       * this class to write the project file to a temp file, but it also used
       * by some other tests.
       *
       * @param content The content of the file to create.
       * @return A reference to the file created or null on failure.
       */
      public static File writeStringToTempFile(final String content) {
          File tempFile;
          try {
              tempFile = File.createTempFile("myApp", ".tmp");
              // tempFile.deleteOnExit();
  
              final FileWriter writer = new FileWriter(tempFile);
              writer.write(content);
              writer.flush();
              writer.close();
  
              return tempFile;
  
          } catch (final IOException e) {
              e.printStackTrace();
              return null;
          }
      }
  
      /** Provide a feedback path to monitor the VoltCompiler's plan output via harvestDiagnostics */
      public void enableDiagnostics() {
          // This empty dummy value enables the feature and provides a default fallback return value,
          // but gets replaced in the normal code path.
          m_diagnostics = new ArrayList<>();
      }
  
      /** Access the VoltCompiler's recent plan output, for diagnostic purposes */
      public List<String> harvestDiagnostics() {
  