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 com.cloud.api.doc;
  
  import java.io.BufferedWriter;
  import java.io.EOFException;
  import java.io.FileReader;
  import java.io.FileWriter;
  import java.io.IOException;
  import java.io.ObjectInputStream;
  import java.util.Map;
  import java.util.LinkedHashMap;
  import java.util.ArrayList;
  import java.util.HashMap;
  import java.util.Iterator;
  import java.util.List;
  import java.util.Arrays;
  
  import com.google.gson.Gson;
  
  import com.thoughtworks.xstream.XStream;
  import com.thoughtworks.xstream.io.xml.DomDriver;
  
  public class ApiXmlDocReader {
      public static void main(String[] args) {
          String newFile = null;
          String oldFile = null;
          String dirName = "";
  
          LinkedHashMap<String, Command> commands = new LinkedHashMap<String, Command>();
          LinkedHashMap<String, Command> oldCommands = new LinkedHashMap<String, Command>();
          ArrayList<Command> addedCommands = new ArrayList<Command>();
          ArrayList<Command> removedCommands = new ArrayList<Command>();
          HashMap<String, Command> stableCommands = new HashMap<String, Command>();
          HashMap<String, Object> jsonOut = new HashMap<String, Object>();
          Gson gson = new Gson();
  
          XStream xs = new XStream(new DomDriver());
          xs.alias("command", Command.class);
          xs.alias("arg", Argument.class);
  
          List<String> argsList = Arrays.asList(args);
          Iterator<String> iter = argsList.iterator();
          while (iter.hasNext()) {
              String arg = iter.next();
              // populate the file names
              if (arg.equals("-new")) {
                  newFile = iter.next();
              }
              if (arg.equals("-old")) {
                  oldFile = iter.next();
              }
              if (arg.equals("-d")) {
                  dirName = iter.next();
              }
          }
  
          try {
              try (ObjectInputStream inOld = xs.createObjectInputStream(new FileReader(oldFile));){
                  while (true) {
                      Command c1 = (Command)inOld.readObject();
                      oldCommands.put(c1.getName(), c1);
                  }
              } catch (EOFException ex) {
                  // EOF exception shows that there is no more objects in ObjectInputStream, so do nothing here
              }
  
              try (ObjectInputStream inNew = xs.createObjectInputStream(new FileReader(newFile));){
                  while (true) {
                      Command c = (Command)inNew.readObject();
                      commands.put(c.getName(), c);
                  }
              } catch (EOFException ex) {
                  // EOF exception shows that there is no more objects in ObjectInputStream, so do nothing here
              }
          } catch (Exception ex) {
              ex.printStackTrace();
          }
  
          // Check if any commands got added in new version
          for (Map.Entry<String,Command>entry : commands.entrySet()) {
              if (!oldCommands.containsKey(entry.getKey())) {
                  addedCommands.add(entry.getValue());
              } else {
                  stableCommands.put(entry.getValue().getName(), entry.getValue());
              }
          }
  
          // Check if any commands were removed in new version
          for (Map.Entry<String,Command>entry : oldCommands.entrySet()) {
              if (!commands.containsKey(entry.getKey())) {
                  removedCommands.add(entry.getValue());
                  if (stableCommands.get(entry.getKey()) != null) {
                      stableCommands.remove(entry.getKey());
                  }
              }
          }
  
          try (FileWriter fstream = new FileWriter(dirName + "/diff.txt");
               BufferedWriter out = new BufferedWriter(fstream);
               FileWriter jfstream = new FileWriter(dirName + "/diff.json");
               BufferedWriter json = new BufferedWriter(jfstream);){
              // Print added commands
              out.write("Added commands:\n");
              ArrayList<HashMap<String, Object>> addedCmds = new ArrayList<HashMap<String, Object>>();
              for (Command c : addedCommands) {
                  HashMap<String, Object> addedCmd = new HashMap<String, Object>();
                  if (c.getDescription() != null && !c.getDescription().isEmpty()) {
                      out.write("\n    " + c.getName() + " (" + c.getDescription() + ")\n");
                      addedCmd.put("description", c.getDescription());
                  } else {
                      out.write("\n    " + c.getName() + "\n");
                  }
                  addedCmd.put("name", c.getName());
                  addedCmds.add(addedCmd);
              }
              jsonOut.put("commands_added", addedCmds);
  
              // Print removed commands
              out.write("\nRemoved commands:\n");
              ArrayList<HashMap<String, Object>> removedCmds = new ArrayList<HashMap<String, Object>>();
              for (Command c : removedCommands) {
                  HashMap<String, Object> removedCmd = new HashMap<String, Object>();
                  if (c.getDescription() != null && !c.getDescription().isEmpty()) {
                      out.write("\n\t" + c.getName() + " (" + c.getDescription() + ")\n");
                      removedCmd.put("description", c.getDescription());
                  } else {
                      out.write("\n\t" + c.getName() + "\n");
                  }
                  removedCmd.put("name", c.getName());
                  removedCmds.add(removedCmd);
              }
              jsonOut.put("commands_removed", removedCmds);
  
              out.write("\nChanges in command type (sync versus async)\n");
              ArrayList<HashMap<String, Object>> syncChangeCmds = new ArrayList<HashMap<String, Object>>();
              // Verify if the command was sync and became async and vice versa
              for (Map.Entry<String,Command>entry : stableCommands.entrySet()) {
                  if (commands.get(entry.getKey()).isAsync() != oldCommands.get(entry.getKey()).isAsync()) {
                      HashMap<String, Object> syncChangeCmd = new HashMap<String, Object>();
                      String type = "Sync";
                      if (commands.get(entry.getKey()).isAsync()) {
                          type = "Async";
                      }
                      syncChangeCmd.put("name", entry.getValue().getName());
                      syncChangeCmd.put("sync_type", type);
                      syncChangeCmds.add(syncChangeCmd);
                      out.write("\n\t" + entry.getValue().getName() + " became " + type);
                  }
              }
              jsonOut.put("commands_sync_changed", syncChangeCmds);
  
              // Print differences between commands arguments
              out.write("\n\nChanges in commands arguments:\n");
              ArrayList<HashMap<String, Object>> argsChangeCmds = new ArrayList<HashMap<String, Object>>();
              for (String key : stableCommands.keySet()) {
                  ArrayList<Argument> newReqArgs = new ArrayList<Argument>();
                  ArrayList<Argument> removedReqArgs = new ArrayList<Argument>();
                  HashMap<String, Argument> stableReqArgs = new HashMap<String, Argument>();
                  ArrayList<Argument> newRespArgs = new ArrayList<Argument>();
                  ArrayList<Argument> removedRespArgs = new ArrayList<Argument>();
                  HashMap<String, Object> argsChangeCmd = new HashMap<String, Object>();
  
                  Command newCommand = commands.get(key);
                  Command oldCommand = oldCommands.get(key);
  
                  // Check if any request arguments were added in new version
                  for (Argument arg : newCommand.getRequest()) {
                      if (oldCommand.getReqArgByName(arg.getName()) == null) {
                          if (!(arg.getName().equals("page") || arg.getName().equals("pagesize") || arg.getName().equals("keyword"))) {
                              newReqArgs.add(arg);
                          }
                      } else {
                          stableReqArgs.put(arg.getName(), arg);
                      }
                  }
  
                  // Check if any request arguments were removed in new version
                  for (Argument arg : oldCommand.getRequest()) {
                      if (newCommand.getReqArgByName(arg.getName()) == null) {
                          removedReqArgs.add(arg);
                          if (stableReqArgs.get(arg.getName()) != null) {
                              stableReqArgs.remove(arg.getName());
                          }
                      }
                  }
  
                  // Compare stable request arguments of old and new version
                  for (Iterator<String> i = stableReqArgs.keySet().iterator(); i.hasNext();) {
                      String argName = i.next();
                      if ((oldCommand.getReqArgByName(argName) != null) && (newCommand.getReqArgByName(argName) != null))
                      {
                          if (oldCommand.getReqArgByName(argName).isRequired().equals(newCommand.getReqArgByName(argName).isRequired())) {
                              i.remove();
                          }
                      }
                  }
  
                  // Check if any response arguments were added in new version
                  if (newCommand.getResponse() != null && oldCommand.getResponse() != null) {
                      for (Argument arg : newCommand.getResponse()) {
                          if (oldCommand.getResArgByName(arg.getName()) == null) {
                              newRespArgs.add(arg);
                          }
                      }
  
                      // Check if any response arguments were removed in new version
                      for (Argument arg : oldCommand.getResponse()) {
                          if (newCommand.getResArgByName(arg.getName()) == null) {
                              removedRespArgs.add(arg);
                          }
                      }
                  }
  
                  if (newReqArgs.size() != 0 || newRespArgs.size() != 0 || removedReqArgs.size() != 0 || removedRespArgs.size() != 0 || stableReqArgs.size() != 0) {
                      StringBuffer commandInfo = new StringBuffer();
                      commandInfo.append("\n\t" + key);
                      out.write(commandInfo.toString());
                      out.write("\n");
                      argsChangeCmd.put("name", key);
  
                      // Request
                      if (newReqArgs.size() != 0 || removedReqArgs.size() != 0 || stableReqArgs.size() != 0) {
                          HashMap<String, Object> requestChanges = new HashMap<String, Object>();
                          StringBuffer request = new StringBuffer();
                          request.append("\n\t\tRequest:\n");
                          out.write(request.toString());
                          if (newReqArgs.size() != 0) {
                              StringBuffer newParameters = new StringBuffer();
                              newParameters.append("\n\t\t\tNew parameters: ");
                              ArrayList<HashMap<String, Object>> newRequestParams = new ArrayList<HashMap<String, Object>>();
                              for (Argument newArg : newReqArgs) {
                                  HashMap<String, Object> newRequestParam = new HashMap<String, Object>();
                                  String isRequiredParam = "optional";
                                  if (newArg.isRequired()) {
                                      isRequiredParam = "required";
                                  }
                                  newRequestParam.put("name", newArg.getName());
                                  newRequestParam.put("required", newArg.isRequired());
                                  newRequestParams.add(newRequestParam);
                                  newParameters.append(newArg.getName() + " (" + isRequiredParam + "), ");
                              }
                              requestChanges.put("params_new", newRequestParams);
                              newParameters.delete(newParameters.length() - 2, newParameters.length() - 1);
                              out.write(newParameters.toString());
                              out.write("\n");
                          }
                          if (removedReqArgs.size() != 0) {
                              StringBuffer removedParameters = new StringBuffer();
                              removedParameters.append("\n\t\t\tRemoved parameters: ");
                              ArrayList<HashMap<String, Object>> removedRequestParams = new ArrayList<HashMap<String, Object>>();
                              for (Argument removedArg : removedReqArgs) {
                                  HashMap<String, Object> removedRequestParam = new HashMap<String, Object>();
                                  removedRequestParam.put("name", removedArg.getName());
                                  removedRequestParams.add(removedRequestParam);
                                  removedParameters.append(removedArg.getName() + ", ");
                              }
                              requestChanges.put("params_removed", removedRequestParams);
                              removedParameters.delete(removedParameters.length() - 2, removedParameters.length() - 1);
                              out.write(removedParameters.toString());
                              out.write("\n");
                          }
  
                          if (stableReqArgs.size() != 0) {
                              StringBuffer changedParameters = new StringBuffer();
                              changedParameters.append("\n\t\t\tChanged parameters: ");
                              ArrayList<HashMap<String, Object>> changedRequestParams = new ArrayList<HashMap<String, Object>>();
                              for (Argument stableArg : stableReqArgs.values()) {
                                  HashMap<String, Object> changedRequestParam = new HashMap<String, Object>();
                                  String newRequired = "optional";
                                  String oldRequired = "optional";
                                  changedRequestParam.put("required_old", false);
                                  changedRequestParam.put("required_new", false);
                                  if ((oldCommand.getReqArgByName(stableArg.getName()) != null) && (oldCommand.getReqArgByName(stableArg.getName()).isRequired() == true)) {
                                      oldRequired = "required";
                                      changedRequestParam.put("required_old", true);
                                  }
                                  if ((newCommand.getReqArgByName(stableArg.getName()) != null) && (newCommand.getReqArgByName(stableArg.getName()).isRequired() == true)) {
                                      newRequired = "required";
                                      changedRequestParam.put("required_new", true);
                                  }
                                  changedRequestParam.put("name", stableArg.getName());
                                  changedRequestParams.add(changedRequestParam);
                                  changedParameters.append(stableArg.getName() + " (old version - " + oldRequired + ", new version - " + newRequired + "), ");
                              }
                              requestChanges.put("params_changed", changedRequestParams);
                              changedParameters.delete(changedParameters.length() - 2, changedParameters.length() - 1);
                              out.write(changedParameters.toString());
                              out.write("\n");
                          }
                          argsChangeCmd.put("request", requestChanges);
                      }
  
                      // Response
                      if (newRespArgs.size() != 0 || removedRespArgs.size() != 0) {
                          HashMap<String, Object> responseChanges = new HashMap<String, Object>();
                          StringBuffer changedResponseParams = new StringBuffer();
                          changedResponseParams.append("\n\t\tResponse:\n");
                          out.write(changedResponseParams.toString());
                          if (newRespArgs.size() != 0) {
                              ArrayList<HashMap<String, Object>> newResponseParams = new ArrayList<HashMap<String, Object>>();
                              StringBuffer newRespParams = new StringBuffer();
                              newRespParams.append("\n\t\t\tNew parameters: ");
                              for (Argument newArg : newRespArgs) {
                                  HashMap<String, Object> newResponseParam = new HashMap<String, Object>();
                                  newResponseParam.put("name", newArg.getName());
                                  newResponseParams.add(newResponseParam);
                                  newRespParams.append(newArg.getName() + ", ");
                              }
                              responseChanges.put("params_new", newResponseParams);
                              newRespParams.delete(newRespParams.length() - 2, newRespParams.length() - 1);
                              out.write(newRespParams.toString());
                              out.write("\n");
                          }
                          if (removedRespArgs.size() != 0) {
                              ArrayList<HashMap<String, Object>> removedResponseParams = new ArrayList<HashMap<String, Object>>();
                              StringBuffer removedRespParams = new StringBuffer();
                              removedRespParams.append("\n\t\t\tRemoved parameters: ");
                              for (Argument removedArg : removedRespArgs) {
                                  HashMap<String, Object> removedResponseParam = new HashMap<String, Object>();
                                  removedResponseParam.put("name", removedArg.getName());
                                  removedResponseParams.add(removedResponseParam);
                                  removedRespParams.append(removedArg.getName() + ", ");
                              }
                              responseChanges.put("params_removed", removedResponseParams);
                              removedRespParams.delete(removedRespParams.length() - 2, removedRespParams.length() - 1);
                              out.write(removedRespParams.toString());
                              out.write("\n");
                          }
                          argsChangeCmd.put("response", responseChanges);
                      }
                      argsChangeCmds.add(argsChangeCmd);
                  }
              }
              jsonOut.put("commands_args_changed", argsChangeCmds);
              json.write(gson.toJson(jsonOut));
          } catch (IOException e) {
              e.printStackTrace();
          }
      }
  }
  