content: /*
   * Copyright (c) 2015, 2020, Oracle and/or its affiliates.
   *
   * This program is free software; you can redistribute it and/or modify it under
   * the terms of the GNU General Public License, version 2.0, as published by the
   * Free Software Foundation.
   *
   * This program is also distributed with certain software (including but not
   * limited to OpenSSL) that is licensed under separate terms, as designated in a
   * particular file or component or in included license documentation. The
   * authors of MySQL hereby grant you an additional permission to link the
   * program and your derivative works with the separately licensed software that
   * they have included with MySQL.
   *
   * Without limiting anything contained in the foregoing, this file, which is
   * part of MySQL Connector/J, is also subject to the Universal FOSS Exception,
   * version 1.0, a copy of which can be found at
   * http://oss.oracle.com/licenses/universal-foss-exception.
   *
   * 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 General Public License, version 2.0,
   * for more details.
   *
   * You should have received a copy of the GNU General Public License along with
   * this program; if not, write to the Free Software Foundation, Inc.,
   * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA
   */
  
  package com.mysql.cj.protocol.x;
  
  import java.util.Collections;
  import java.util.HashMap;
  import java.util.Map;
  
  import com.google.protobuf.GeneratedMessageV3;
  import com.google.protobuf.MessageLite;
  import com.google.protobuf.Parser;
  import com.mysql.cj.exceptions.AssertionFailedException;
  import com.mysql.cj.exceptions.WrongArgumentException;
  import com.mysql.cj.x.protobuf.Mysqlx.ClientMessages;
  import com.mysql.cj.x.protobuf.Mysqlx.Error;
  import com.mysql.cj.x.protobuf.Mysqlx.Ok;
  import com.mysql.cj.x.protobuf.Mysqlx.ServerMessages;
  import com.mysql.cj.x.protobuf.MysqlxConnection.Capabilities;
  import com.mysql.cj.x.protobuf.MysqlxConnection.CapabilitiesGet;
  import com.mysql.cj.x.protobuf.MysqlxConnection.CapabilitiesSet;
  import com.mysql.cj.x.protobuf.MysqlxConnection.Compression;
  import com.mysql.cj.x.protobuf.MysqlxCrud.CreateView;
  import com.mysql.cj.x.protobuf.MysqlxCrud.Delete;
  import com.mysql.cj.x.protobuf.MysqlxCrud.DropView;
  import com.mysql.cj.x.protobuf.MysqlxCrud.Find;
  import com.mysql.cj.x.protobuf.MysqlxCrud.Insert;
  import com.mysql.cj.x.protobuf.MysqlxCrud.ModifyView;
  import com.mysql.cj.x.protobuf.MysqlxCrud.Update;
  import com.mysql.cj.x.protobuf.MysqlxExpect.Open;
  import com.mysql.cj.x.protobuf.MysqlxNotice.Frame;
  import com.mysql.cj.x.protobuf.MysqlxNotice.SessionStateChanged;
  import com.mysql.cj.x.protobuf.MysqlxNotice.SessionVariableChanged;
  import com.mysql.cj.x.protobuf.MysqlxNotice.Warning;
  import com.mysql.cj.x.protobuf.MysqlxPrepare.Deallocate;
  import com.mysql.cj.x.protobuf.MysqlxPrepare.Execute;
  import com.mysql.cj.x.protobuf.MysqlxPrepare.Prepare;
  import com.mysql.cj.x.protobuf.MysqlxResultset.ColumnMetaData;
  import com.mysql.cj.x.protobuf.MysqlxResultset.FetchDone;
  import com.mysql.cj.x.protobuf.MysqlxResultset.FetchDoneMoreResultsets;
  import com.mysql.cj.x.protobuf.MysqlxResultset.Row;
  import com.mysql.cj.x.protobuf.MysqlxSession.AuthenticateContinue;
  import com.mysql.cj.x.protobuf.MysqlxSession.AuthenticateOk;
  import com.mysql.cj.x.protobuf.MysqlxSession.AuthenticateStart;
  import com.mysql.cj.x.protobuf.MysqlxSession.Close;
  import com.mysql.cj.x.protobuf.MysqlxSession.Reset;
  import com.mysql.cj.x.protobuf.MysqlxSql.StmtExecute;
  import com.mysql.cj.x.protobuf.MysqlxSql.StmtExecuteOk;
  
  /**
   * Constants related to X Protocol messages.
   */
  public class MessageConstants {
      /**
       * Store a mapping of "ServerMessages" class to message parsers. This is used to get the de-serializer after reading the type tag.
       */
      public static final Map<Class<? extends GeneratedMessageV3>, Parser<? extends GeneratedMessageV3>> MESSAGE_CLASS_TO_PARSER;
  
      /**
       * Map of class to "ServerMessages" type tag for validation of parsed message class.
       */
      // TODO Find a clever way to generate both maps with a single set of input pairs.
      public static final Map<Class<? extends GeneratedMessageV3>, Integer> MESSAGE_CLASS_TO_TYPE;
  
      /**
       * Map of "ServerMessages" type tag to class.
       */
      public static final Map<Integer, Class<? extends GeneratedMessageV3>> MESSAGE_TYPE_TO_CLASS;
  
      /**
       * Store a mapping of message class to "ClientMessages" type tag. This is used to generate the header when sending a message.
       */
      public static final Map<Class<? extends MessageLite>, Integer> MESSAGE_CLASS_TO_CLIENT_MESSAGE_TYPE;
  
      static {
          /*
           * ServerMessages mappings (including embedded noticed messages with no entry in ServerMessages)
           */
          Map<Class<? extends GeneratedMessageV3>, Parser<? extends GeneratedMessageV3>> messageClassToParser = new HashMap<>();
          Map<Class<? extends GeneratedMessageV3>, Integer> messageClassToType = new HashMap<>();
          Map<Integer, Class<? extends GeneratedMessageV3>> messageTypeToClass = new HashMap<>();
          // To add support for new messages, add an entry to both maps
          messageClassToParser.put(Error.class, Error.getDefaultInstance().getParserForType());
          messageClassToParser.put(Ok.class, Ok.getDefaultInstance().getParserForType());
          messageClassToParser.put(AuthenticateContinue.class, AuthenticateContinue.getDefaultInstance().getParserForType());
          messageClassToParser.put(AuthenticateOk.class, AuthenticateOk.getDefaultInstance().getParserForType());
          messageClassToParser.put(Capabilities.class, Capabilities.getDefaultInstance().getParserForType());
          messageClassToParser.put(ColumnMetaData.class, ColumnMetaData.getDefaultInstance().getParserForType());
          messageClassToParser.put(FetchDone.class, FetchDone.getDefaultInstance().getParserForType());
          messageClassToParser.put(FetchDoneMoreResultsets.class, FetchDoneMoreResultsets.getDefaultInstance().getParserForType());
          messageClassToParser.put(Frame.class, Frame.getDefaultInstance().getParserForType());
          messageClassToParser.put(Row.class, Row.getDefaultInstance().getParserForType());
          messageClassToParser.put(StmtExecuteOk.class, StmtExecuteOk.getDefaultInstance().getParserForType());
          messageClassToParser.put(Compression.class, Compression.getDefaultInstance().getParserForType());
          // embedded notices (no type tags)
          messageClassToParser.put(SessionStateChanged.class, SessionStateChanged.getDefaultInstance().getParserForType());
          messageClassToParser.put(SessionVariableChanged.class, SessionVariableChanged.getDefaultInstance().getParserForType());
          messageClassToParser.put(Warning.class, Warning.getDefaultInstance().getParserForType());
  
          messageClassToType.put(Error.class, ServerMessages.Type.ERROR_VALUE);
          messageClassToType.put(Ok.class, ServerMessages.Type.OK_VALUE);
          messageClassToType.put(AuthenticateContinue.class, ServerMessages.Type.SESS_AUTHENTICATE_CONTINUE_VALUE);
          messageClassToType.put(AuthenticateOk.class, ServerMessages.Type.SESS_AUTHENTICATE_OK_VALUE);
          messageClassToType.put(Capabilities.class, ServerMessages.Type.CONN_CAPABILITIES_VALUE);
          messageClassToType.put(ColumnMetaData.class, ServerMessages.Type.RESULTSET_COLUMN_META_DATA_VALUE);
          messageClassToType.put(FetchDone.class, ServerMessages.Type.RESULTSET_FETCH_DONE_VALUE);
          messageClassToType.put(FetchDoneMoreResultsets.class, ServerMessages.Type.RESULTSET_FETCH_DONE_MORE_RESULTSETS_VALUE);
          messageClassToType.put(Frame.class, ServerMessages.Type.NOTICE_VALUE);
          messageClassToType.put(Row.class, ServerMessages.Type.RESULTSET_ROW_VALUE);
          messageClassToType.put(StmtExecuteOk.class, ServerMessages.Type.SQL_STMT_EXECUTE_OK_VALUE);
          messageClassToType.put(Compression.class, ServerMessages.Type.COMPRESSION_VALUE);
          for (Map.Entry<Class<? extends GeneratedMessageV3>, Integer> entry : messageClassToType.entrySet()) {
              messageTypeToClass.put(entry.getValue(), entry.getKey());
          }
          MESSAGE_CLASS_TO_PARSER = Collections.unmodifiableMap(messageClassToParser);
          MESSAGE_CLASS_TO_TYPE = Collections.unmodifiableMap(messageClassToType);
          MESSAGE_TYPE_TO_CLASS = Collections.unmodifiableMap(messageTypeToClass);
  
          /*
           * ClientMessages mappings
           */
          Map<Class<? extends MessageLite>, Integer> messageClassToClientMessageType = new HashMap<>();
          messageClassToClientMessageType.put(AuthenticateStart.class, ClientMessages.Type.SESS_AUTHENTICATE_START_VALUE);
          messageClassToClientMessageType.put(AuthenticateContinue.class, ClientMessages.Type.SESS_AUTHENTICATE_CONTINUE_VALUE);
          messageClassToClientMessageType.put(CapabilitiesGet.class, ClientMessages.Type.CON_CAPABILITIES_GET_VALUE);
          messageClassToClientMessageType.put(CapabilitiesSet.class, ClientMessages.Type.CON_CAPABILITIES_SET_VALUE);
          messageClassToClientMessageType.put(Close.class, ClientMessages.Type.SESS_CLOSE_VALUE);
          messageClassToClientMessageType.put(Delete.class, ClientMessages.Type.CRUD_DELETE_VALUE);
          messageClassToClientMessageType.put(Find.class, ClientMessages.Type.CRUD_FIND_VALUE);
          messageClassToClientMessageType.put(Insert.class, ClientMessages.Type.CRUD_INSERT_VALUE);
          messageClassToClientMessageType.put(Reset.class, ClientMessages.Type.SESS_RESET_VALUE);
          messageClassToClientMessageType.put(StmtExecute.class, ClientMessages.Type.SQL_STMT_EXECUTE_VALUE);
          messageClassToClientMessageType.put(Update.class, ClientMessages.Type.CRUD_UPDATE_VALUE);
          messageClassToClientMessageType.put(CreateView.class, ClientMessages.Type.CRUD_CREATE_VIEW_VALUE);
          messageClassToClientMessageType.put(ModifyView.class, ClientMessages.Type.CRUD_MODIFY_VIEW_VALUE);
          messageClassToClientMessageType.put(DropView.class, ClientMessages.Type.CRUD_DROP_VIEW_VALUE);
          messageClassToClientMessageType.put(Open.class, ClientMessages.Type.EXPECT_OPEN_VALUE);
          messageClassToClientMessageType.put(Prepare.class, ClientMessages.Type.PREPARE_PREPARE_VALUE);
          messageClassToClientMessageType.put(Execute.class, ClientMessages.Type.PREPARE_EXECUTE_VALUE);
          messageClassToClientMessageType.put(Deallocate.class, ClientMessages.Type.PREPARE_DEALLOCATE_VALUE);
          MESSAGE_CLASS_TO_CLIENT_MESSAGE_TYPE = Collections.unmodifiableMap(messageClassToClientMessageType);
      }
  
      /**
       * Lookup the "ClientMessages" type tag for a Protocol buffers message class.
       * 
       * @param msgClass
       *            message class extending {@link MessageLite}
       * @return type tag for this message class
       */
      public static int getTypeForMessageClass(Class<? extends MessageLite> msgClass) {
          Integer tag = MESSAGE_CLASS_TO_CLIENT_MESSAGE_TYPE.get(msgClass);
          if (tag == null) {
              throw new WrongArgumentException("No mapping to ClientMessages for message class " + msgClass.getSimpleName());
          }
          return tag;
      }
  
      public static Class<? extends GeneratedMessageV3> getMessageClassForType(int type) {
          Class<? extends GeneratedMessageV3> messageClass = MessageConstants.MESSAGE_TYPE_TO_CLASS.get(type);
          if (messageClass == null) {
              // check if there's a mapping that we don't explicitly handle
              ServerMessages.Type serverMessageMapping = ServerMessages.Type.forNumber(type);
              throw AssertionFailedException.shouldNotHappen("Unknown message type: " + type + " (server messages mapping: " + serverMessageMapping + ")");
          }
          return messageClass;
      }
  }
  