// Path: src/main/java/com/google/cloud/spanner/pgadapter/metadata/DynamicCommandMetadata.java
// public class DynamicCommandMetadata {
// 
//   private static final String COMMANDS_KEY = "commands";
//   private static final String INPUT_KEY = "input_pattern";
//   private static final String OUTPUT_KEY = "output_pattern";
//   private static final String MATCHER_KEY = "matcher_array";
// 
//   private String inputPattern;
//   private String outputPattern;
//   private List<String> matcherOrder;
// 
//   private DynamicCommandMetadata(JSONObject commandJSON) {
//     this.inputPattern = getJSONString(commandJSON, INPUT_KEY);
//     this.outputPattern = getJSONString(commandJSON, OUTPUT_KEY);
//     this.matcherOrder = new ArrayList<>();
//     for (Object arrayObject : getJSONArray(commandJSON, MATCHER_KEY)) {
//       String argumentNumber = (String) arrayObject;
//       this.matcherOrder.add(argumentNumber);
//     }
//   }
// 
//   /**
//    * Takes a JSON object and returns a list of metadata objects holding the desired information.
//    *
//    * @param jsonObject Input JSON object in the format {"commands": [{"input_pattern": "",
//    * "output_pattern": "", "matcher_array": [number1, ...]}, ...]}
//    * @return A list of constructed metadata objects in the format understood by DynamicCommands
//    */
//   public static List<DynamicCommandMetadata> fromJSON(JSONObject jsonObject) {
//     List<DynamicCommandMetadata> resultList = new ArrayList<>();
//     for (Object currentJSONObject : getJSONArray(jsonObject, COMMANDS_KEY)) {
//       resultList.add(new DynamicCommandMetadata((JSONObject) currentJSONObject));
//     }
//     return resultList;
//   }
// 
//   /**
//    * Simple getter which transforms the result into the desired type.
//    *
//    * @param jsonObject The input object.
//    * @param key The reference key.
//    * @return The JSON value referenced by the key in String type.
//    */
//   private static String getJSONString(JSONObject jsonObject, String key) {
//     return getJSONObject(jsonObject, key).toString();
//   }
// 
//   /**
//    * Simple getter which transforms the result into the desired type.
//    *
//    * @param jsonObject The input object.
//    * @param key The reference key.
//    * @return The JSON value referenced by the key in JSONArray type.
//    */
//   private static JSONArray getJSONArray(JSONObject jsonObject, String key) {
//     return (JSONArray) getJSONObject(jsonObject, key);
//   }
// 
//   /**
//    * Simple getter which transforms the result into the desired type. If the key does not exist,
//    * throw an exception.
//    *
//    * @param jsonObject The input object.
//    * @param key The reference key.
//    * @return The JSON value referenced by the key in JSONObject type.
//    * @throws IllegalArgumentException If the key doesn't exist.
//    */
//   private static Object getJSONObject(JSONObject jsonObject, String key) {
//     Object result = jsonObject.get(key);
//     if (result == null) {
//       throw new IllegalArgumentException(
//           String.format("A '%s' key must be specified in the JSON definition.", key)
//       );
//     }
//     return result;
//   }
// 
//   public String getInputPattern() {
//     return this.inputPattern;
//   }
// 
//   public String getOutputPattern() {
//     return this.outputPattern;
//   }
// 
//   public List<String> getMatcherOrder() {
//     return this.matcherOrder;
//   }
// }


import com.google.cloud.spanner.pgadapter.metadata.DynamicCommandMetadata;
import java.sql.Connection;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.json.simple.JSONObject;
// Copyright 2020 Google LLC
//
// Licensed 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 com.google.cloud.spanner.pgadapter.commands;


/**
 * This abstract class concerns itself with representing a method of matching a specific statement
 * and translating it to a form that Spanner understands. The user should call is() to determine
 * whether it matches before trying to translate.
 */
abstract public class Command {

  protected Matcher matcher;
  protected String sql;

  public Command(String sql) {
    this.sql = sql;
    this.matcher = getPattern().matcher(sql);
  }

  /**
   * This constructor should only be called if you do wish to do your own matching (i.e.: set up
   * this.matcher). Useful if the pattern is not static, or not pattern related (in which case you
   * may set it to null).
   */
  protected Command(Matcher matcher) {
    this.matcher = matcher;
  }

  /**
   * The list of all subcommands to this command. When subclassing this, make sure to add the new
   * command to the below list, if applicable (i.e.: if you want it to be run). Commands may also be
   * specified via Dynamic Commands, which are defined from an input JSON file. These commands
   * precede the hard-coded ones in execution.
   *
   * @param sql The SQL command to be translated.
   * @param connection The connection currently in use.
   * @return A list of all Command subclasses.
   */
  public static List<Command> getCommands(String sql, Connection connection,
      JSONObject commandMetadataJSON) {
    List<Command> commandList = new ArrayList<>();

// Path: src/main/java/com/google/cloud/spanner/pgadapter/metadata/DynamicCommandMetadata.java
// public class DynamicCommandMetadata {
// 
//   private static final String COMMANDS_KEY = "commands";
//   private static final String INPUT_KEY = "input_pattern";
//   private static final String OUTPUT_KEY = "output_pattern";
//   private static final String MATCHER_KEY = "matcher_array";
// 
//   private String inputPattern;
//   private String outputPattern;
//   private List<String> matcherOrder;
// 
//   private DynamicCommandMetadata(JSONObject commandJSON) {
//     this.inputPattern = getJSONString(commandJSON, INPUT_KEY);
//     this.outputPattern = getJSONString(commandJSON, OUTPUT_KEY);
//     this.matcherOrder = new ArrayList<>();
//     for (Object arrayObject : getJSONArray(commandJSON, MATCHER_KEY)) {
//       String argumentNumber = (String) arrayObject;
//       this.matcherOrder.add(argumentNumber);
//     }
//   }
// 
//   /**
//    * Takes a JSON object and returns a list of metadata objects holding the desired information.
//    *
//    * @param jsonObject Input JSON object in the format {"commands": [{"input_pattern": "",
//    * "output_pattern": "", "matcher_array": [number1, ...]}, ...]}
//    * @return A list of constructed metadata objects in the format understood by DynamicCommands
//    */
//   public static List<DynamicCommandMetadata> fromJSON(JSONObject jsonObject) {
//     List<DynamicCommandMetadata> resultList = new ArrayList<>();
//     for (Object currentJSONObject : getJSONArray(jsonObject, COMMANDS_KEY)) {
//       resultList.add(new DynamicCommandMetadata((JSONObject) currentJSONObject));
//     }
//     return resultList;
//   }
// 
//   /**
//    * Simple getter which transforms the result into the desired type.
//    *
//    * @param jsonObject The input object.
//    * @param key The reference key.
//    * @return The JSON value referenced by the key in String type.
//    */
//   private static String getJSONString(JSONObject jsonObject, String key) {
//     return getJSONObject(jsonObject, key).toString();
//   }
// 
//   /**
//    * Simple getter which transforms the result into the desired type.
//    *
//    * @param jsonObject The input object.
//    * @param key The reference key.
//    * @return The JSON value referenced by the key in JSONArray type.
//    */
//   private static JSONArray getJSONArray(JSONObject jsonObject, String key) {
//     return (JSONArray) getJSONObject(jsonObject, key);
//   }
// 
//   /**
//    * Simple getter which transforms the result into the desired type. If the key does not exist,
//    * throw an exception.
//    *
//    * @param jsonObject The input object.
//    * @param key The reference key.
//    * @return The JSON value referenced by the key in JSONObject type.
//    * @throws IllegalArgumentException If the key doesn't exist.
//    */
//   private static Object getJSONObject(JSONObject jsonObject, String key) {
//     Object result = jsonObject.get(key);
//     if (result == null) {
//       throw new IllegalArgumentException(
//           String.format("A '%s' key must be specified in the JSON definition.", key)
//       );
//     }
//     return result;
//   }
// 
//   public String getInputPattern() {
//     return this.inputPattern;
//   }
// 
//   public String getOutputPattern() {
//     return this.outputPattern;
//   }
// 
//   public List<String> getMatcherOrder() {
//     return this.matcherOrder;
//   }
// }


// Path: src/main/java/com/google/cloud/spanner/pgadapter/commands/Command.java
import com.google.cloud.spanner.pgadapter.metadata.DynamicCommandMetadata;
import java.sql.Connection;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.json.simple.JSONObject;

// Copyright 2020 Google LLC
//
// Licensed 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 com.google.cloud.spanner.pgadapter.commands;


/**
 * This abstract class concerns itself with representing a method of matching a specific statement
 * and translating it to a form that Spanner understands. The user should call is() to determine
 * whether it matches before trying to translate.
 */
abstract public class Command {

  protected Matcher matcher;
  protected String sql;

  public Command(String sql) {
    this.sql = sql;
    this.matcher = getPattern().matcher(sql);
  }

  /**
   * This constructor should only be called if you do wish to do your own matching (i.e.: set up
   * this.matcher). Useful if the pattern is not static, or not pattern related (in which case you
   * may set it to null).
   */
  protected Command(Matcher matcher) {
    this.matcher = matcher;
  }

  /**
   * The list of all subcommands to this command. When subclassing this, make sure to add the new
   * command to the below list, if applicable (i.e.: if you want it to be run). Commands may also be
   * specified via Dynamic Commands, which are defined from an input JSON file. These commands
   * precede the hard-coded ones in execution.
   *
   * @param sql The SQL command to be translated.
   * @param connection The connection currently in use.
   * @return A list of all Command subclasses.
   */
  public static List<Command> getCommands(String sql, Connection connection,
      JSONObject commandMetadataJSON) {
    List<Command> commandList = new ArrayList<>();

    for (DynamicCommandMetadata currentMetadata : DynamicCommandMetadata