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.activemq.openwire.v1;
  
  import java.io.DataInput;
  import java.io.DataOutput;
  import java.io.IOException;
  import java.lang.reflect.Constructor;
  
  import org.apache.activemq.command.DataStructure;
  import org.apache.activemq.openwire.BooleanStream;
  import org.apache.activemq.openwire.DataStreamMarshaller;
  import org.apache.activemq.openwire.OpenWireFormat;
  import org.apache.activemq.openwire.OpenWireUtil;
  import org.apache.activemq.util.ByteSequence;
  
  public abstract class BaseDataStreamMarshaller implements DataStreamMarshaller {
  
      public static final Constructor STACK_TRACE_ELEMENT_CONSTRUCTOR;
      private static final int MAX_EXCEPTION_MESSAGE_SIZE = 1024;
  
      static {
          Constructor constructor = null;
          try {
              constructor = StackTraceElement.class.getConstructor(new Class[] {String.class, String.class,
                                                                                String.class, int.class});
          } catch (Throwable e) {
          }
          STACK_TRACE_ELEMENT_CONSTRUCTOR = constructor;
      }
  
      public abstract byte getDataStructureType();
  
      public abstract DataStructure createObject();
  
      public int tightMarshal1(OpenWireFormat wireFormat, Object o, BooleanStream bs) throws IOException {
          return 0;
      }
  
      public void tightMarshal2(OpenWireFormat wireFormat, Object o, DataOutput dataOut, BooleanStream bs)
          throws IOException {
      }
  
      public void tightUnmarshal(OpenWireFormat wireFormat, Object o, DataInput dataIn, BooleanStream bs)
          throws IOException {
      }
  
      public int tightMarshalLong1(OpenWireFormat wireFormat, long o, BooleanStream bs) throws IOException {
          if (o == 0) {
              bs.writeBoolean(false);
              bs.writeBoolean(false);
              return 0;
          } else if ((o & 0xFFFFFFFFFFFF0000L) == 0) {
              bs.writeBoolean(false);
              bs.writeBoolean(true);
              return 2;
          } else if ((o & 0xFFFFFFFF00000000L) == 0) {
              bs.writeBoolean(true);
              bs.writeBoolean(false);
              return 4;
          } else {
              bs.writeBoolean(true);
              bs.writeBoolean(true);
              return 8;
          }
      }
  
      public void tightMarshalLong2(OpenWireFormat wireFormat, long o, DataOutput dataOut, BooleanStream bs)
          throws IOException {
          if (bs.readBoolean()) {
              if (bs.readBoolean()) {
                  dataOut.writeLong(o);
              } else {
                  dataOut.writeInt((int)o);
              }
          } else {
              if (bs.readBoolean()) {
                  dataOut.writeShort((int)o);
              }
          }
      }
  
      public long tightUnmarshalLong(OpenWireFormat wireFormat, DataInput dataIn, BooleanStream bs)
          throws IOException {
          if (bs.readBoolean()) {
              if (bs.readBoolean()) {
                  return dataIn.readLong();
              } else {
                  return toLong(dataIn.readInt());
              }
          } else {
              if (bs.readBoolean()) {
                  return toLong(dataIn.readShort());
              } else {
                  return 0;
              }
          }
      }
  
      protected long toLong(short value) {
          // lets handle negative values
          long answer = value;
          return answer & 0xffffL;
      }
  
      protected long toLong(int value) {
          // lets handle negative values
          long answer = value;
          return answer & 0xffffffffL;
      }
  
      protected DataStructure tightUnmarsalNestedObject(OpenWireFormat wireFormat, DataInput dataIn,
                                                        BooleanStream bs) throws IOException {
          return wireFormat.tightUnmarshalNestedObject(dataIn, bs);
      }
  
      protected int tightMarshalNestedObject1(OpenWireFormat wireFormat, DataStructure o, BooleanStream bs)
          throws IOException {
          return wireFormat.tightMarshalNestedObject1(o, bs);
      }
  
      protected void tightMarshalNestedObject2(OpenWireFormat wireFormat, DataStructure o, DataOutput dataOut,
                                               BooleanStream bs) throws IOException {
          wireFormat.tightMarshalNestedObject2(o, dataOut, bs);
      }
  
      protected DataStructure tightUnmarsalCachedObject(OpenWireFormat wireFormat, DataInput dataIn,
                                                        BooleanStream bs) throws IOException {
          if (wireFormat.isCacheEnabled()) {
              if (bs.readBoolean()) {
                  short index = dataIn.readShort();
                  DataStructure object = wireFormat.tightUnmarshalNestedObject(dataIn, bs);
                  wireFormat.setInUnmarshallCache(index, object);
                  return object;
              } else {
                  short index = dataIn.readShort();
                  return wireFormat.getFromUnmarshallCache(index);
              }
          } else {
              return wireFormat.tightUnmarshalNestedObject(dataIn, bs);
          }
      }
  
      protected int tightMarshalCachedObject1(OpenWireFormat wireFormat, DataStructure o, BooleanStream bs)
          throws IOException {
          if (wireFormat.isCacheEnabled()) {
              Short index = wireFormat.getMarshallCacheIndex(o);
              bs.writeBoolean(index == null);
              if (index == null) {
                  int rc = wireFormat.tightMarshalNestedObject1(o, bs);
                  wireFormat.addToMarshallCache(o);
                  return 2 + rc;
              } else {
                  return 2;
              }
          } else {
              return wireFormat.tightMarshalNestedObject1(o, bs);
          }
      }
  
      protected void tightMarshalCachedObject2(OpenWireFormat wireFormat, DataStructure o, DataOutput dataOut,
                                               BooleanStream bs) throws IOException {
          if (wireFormat.isCacheEnabled()) {
              Short index = wireFormat.getMarshallCacheIndex(o);
              if (bs.readBoolean()) {
                  dataOut.writeShort(index);
                  wireFormat.tightMarshalNestedObject2(o, dataOut, bs);
              } else {
                  dataOut.writeShort(index);
              }
          } else {
              wireFormat.tightMarshalNestedObject2(o, dataOut, bs);
          }
      }
  
      protected Throwable tightUnmarsalThrowable(OpenWireFormat wireFormat, DataInput dataIn, BooleanStream bs)
          throws IOException {
          if (bs.readBoolean()) {
              String clazz = tightUnmarshalString(dataIn, bs);
              String message = tightUnmarshalString(dataIn, bs);
              Throwable o = createThrowable(clazz, message);
              if (wireFormat.isStackTraceEnabled()) {
                  if (STACK_TRACE_ELEMENT_CONSTRUCTOR != null) {
                      StackTraceElement ss[] = new StackTraceElement[dataIn.readShort()];
                      for (int i = 0; i < ss.length; i++) {
                          try {
                              ss[i] = (StackTraceElement)STACK_TRACE_ELEMENT_CONSTRUCTOR
                                  .newInstance(new Object[] {tightUnmarshalString(dataIn, bs),
                                                             tightUnmarshalString(dataIn, bs),
                                                             tightUnmarshalString(dataIn, bs),
                                                             dataIn.readInt()});
                          } catch (IOException e) {
                              throw e;
                          } catch (Throwable e) {
                          }
                      }
                      o.setStackTrace(ss);
                  } else {
                      short size = dataIn.readShort();
                      for (int i = 0; i < size; i++) {
                          tightUnmarshalString(dataIn, bs);
                          tightUnmarshalString(dataIn, bs);
                          tightUnmarshalString(dataIn, bs);
                          dataIn.readInt();
                      }
                  }
                  o.initCause(tightUnmarsalThrowable(wireFormat, dataIn, bs));
  
              }
              return o;
          } else {
              return null;
          }
      }
  
      private Throwable createThrowable(String className, String message) {
          try {
              Class clazz = Class.forName(className, false, BaseDataStreamMarshaller.class.getClassLoader());
              OpenWireUtil.validateIsThrowable(clazz);
              Constructor constructor = clazz.getConstructor(new Class[] {String.class});
              return (Throwable)constructor.newInstance(new Object[] {message});
          } catch (IllegalArgumentException e) {
              return e;
          } catch (Throwable e) {
              return new Throwable(className + ": " + message);
          }
      }
  
      protected int tightMarshalThrowable1(OpenWireFormat wireFormat, Throwable o, BooleanStream bs)
          throws IOException {
          if (o == null) {
              bs.writeBoolean(false);
              return 0;
          } else {
              int rc = 0;
              bs.writeBoolean(true);
              rc += tightMarshalString1(o.getClass().getName(), bs);
              rc += tightMarshalString1(cutMessageIfNeeded(o.getMessage()), bs);
              if (wireFormat.isStackTraceEnabled()) {
                  rc += 2;
                  StackTraceElement[] stackTrace = o.getStackTrace();
                  for (int i = 0; i < stackTrace.length; i++) {
                      StackTraceElement element = stackTrace[i];
                      rc += tightMarshalString1(element.getClassName(), bs);
                      rc += tightMarshalString1(element.getMethodName(), bs);
                      rc += tightMarshalString1(element.getFileName(), bs);
                      rc += 4;
                  }
                  rc += tightMarshalThrowable1(wireFormat, o.getCause(), bs);
              }
              return rc;
          }
      }
  
      protected void tightMarshalThrowable2(OpenWireFormat wireFormat, Throwable o, DataOutput dataOut,
                                            BooleanStream bs) throws IOException {
          if (bs.readBoolean()) {
              tightMarshalString2(o.getClass().getName(), dataOut, bs);
              tightMarshalString2(cutMessageIfNeeded(o.getMessage()), dataOut, bs);
              if (wireFormat.isStackTraceEnabled()) {
                  StackTraceElement[] stackTrace = o.getStackTrace();
                  dataOut.writeShort(stackTrace.length);
                  for (int i = 0; i < stackTrace.length; i++) {
                      StackTraceElement element = stackTrace[i];
                      tightMarshalString2(element.getClassName(), dataOut, bs);
                      tightMarshalString2(element.getMethodName(), dataOut, bs);
                      tightMarshalString2(element.getFileName(), dataOut, bs);
                      dataOut.writeInt(element.getLineNumber());
                  }
                  tightMarshalThrowable2(wireFormat, o.getCause(), dataOut, bs);
              }
          }
      }
  
      @SuppressWarnings("deprecation")
      protected String tightUnmarshalString(DataInput dataIn, BooleanStream bs) throws IOException {
          if (bs.readBoolean()) {
              if (bs.readBoolean()) {
                  int size = dataIn.readShort();
                  byte data[] = new byte[size];
                  dataIn.readFully(data);
                  // Yes deprecated, but we know what we are doing.
                  // This allows us to create a String from a ASCII byte array. (no UTF-8 decoding)
                  return new String(data, 0);
              } else {
                  return dataIn.readUTF();
              }
          } else {
              return null;
          }
      }
  
      protected int tightMarshalString1(String value, BooleanStream bs) throws IOException {
          bs.writeBoolean(value != null);
          if (value != null) {
  
              int strlen = value.length();
              int utflen = 0;
              char[] charr = new char[strlen];
              int c = 0;
              boolean isOnlyAscii = true;
  
              value.getChars(0, strlen, charr, 0);
  
              for (int i = 0; i < strlen; i++) {
                  c = charr[i];
                  if ((c >= 0x0001) && (c <= 0x007F)) {
                      utflen++;
                  } else if (c > 0x07FF) {
                      utflen += 3;
                      isOnlyAscii = false;
                  } else {
                      isOnlyAscii = false;
                      utflen += 2;
                  }
              }
  
              if (utflen >= Short.MAX_VALUE) {
                  throw new IOException("Encountered a String value that is too long to encode.");
              }
              bs.writeBoolean(isOnlyAscii);
              return utflen + 2;
  
          } else {
              return 0;
          }
      }
  
      protected void tightMarshalString2(String value, DataOutput dataOut, BooleanStream bs) throws IOException {
          if (bs.readBoolean()) {
              // If we verified it only holds ascii values
              if (bs.readBoolean()) {
                  dataOut.writeShort(value.length());
                  dataOut.writeBytes(value);
              } else {
                  dataOut.writeUTF(value);
              }
          }
      }
  
      protected int tightMarshalObjectArray1(OpenWireFormat wireFormat, DataStructure[] objects,
                                             BooleanStream bs) throws IOException {
          if (objects != null) {
              int rc = 0;
              bs.writeBoolean(true);
              rc += 2;
              for (int i = 0; i < objects.length; i++) {
                  rc += tightMarshalNestedObject1(wireFormat, objects[i], bs);
              }
              return rc;
          } else {
              bs.writeBoolean(false);
              return 0;
          }
      }
  
      protected void tightMarshalObjectArray2(OpenWireFormat wireFormat, DataStructure[] objects,
                                              DataOutput dataOut, BooleanStream bs) throws IOException {
          if (bs.readBoolean()) {
              dataOut.writeShort(objects.length);
              for (int i = 0; i < objects.length; i++) {
                  tightMarshalNestedObject2(wireFormat, objects[i], dataOut, bs);
              }
          }
      }
  
      protected int tightMarshalConstByteArray1(byte[] data, BooleanStream bs, int i) throws IOException {
          return i;
      }
  
      protected void tightMarshalConstByteArray2(byte[] data, DataOutput dataOut, BooleanStream bs, int i)
          throws IOException {
          dataOut.write(data, 0, i);
      }
  
      protected byte[] tightUnmarshalConstByteArray(DataInput dataIn, BooleanStream bs, int i)
          throws IOException {
          byte data[] = new byte[i];
          dataIn.readFully(data);
          return data;
      }
  
      protected int tightMarshalByteArray1(byte[] data, BooleanStream bs) throws IOException {
          bs.writeBoolean(data != null);
          if (data != null) {
              return data.length + 4;
          } else {
              return 0;
          }
      }
  
      protected void tightMarshalByteArray2(byte[] data, DataOutput dataOut, BooleanStream bs)
          throws IOException {
          if (bs.readBoolean()) {
              dataOut.writeInt(data.length);
              dataOut.write(data);
          }
      }
  
      protected byte[] tightUnmarshalByteArray(OpenWireFormat wireFormat, DataInput dataIn, BooleanStream bs) throws IOException {
          byte rc[] = null;
          if (bs.readBoolean()) {
              int size = dataIn.readInt();
              OpenWireUtil.validateBufferSize(wireFormat, size);
              rc = new byte[size];
              dataIn.readFully(rc);
          }
          return rc;
      }
  
      protected int tightMarshalByteSequence1(ByteSequence data, BooleanStream bs) throws IOException {
          bs.writeBoolean(data != null);
          if (data != null) {
              return data.getLength() + 4;
          } else {
              return 0;
          }
      }
  
      protected void tightMarshalByteSequence2(ByteSequence data, DataOutput dataOut, BooleanStream bs)
          throws IOException {
          if (bs.readBoolean()) {
              dataOut.writeInt(data.getLength());
              dataOut.write(data.getData(), data.getOffset(), data.getLength());
          }
      }
  
      protected ByteSequence tightUnmarshalByteSequence(OpenWireFormat wireFormat, DataInput dataIn, BooleanStream bs) throws IOException {
          ByteSequence rc = null;
          if (bs.readBoolean()) {
              int size = dataIn.readInt();
              OpenWireUtil.validateBufferSize(wireFormat, size);
              byte[] t = new byte[size];
              dataIn.readFully(t);
              return new ByteSequence(t, 0, size);
          }
          return rc;
      }
  
      //
      // The loose marshaling logic
      //
  
      public void looseMarshal(OpenWireFormat wireFormat, Object o, DataOutput dataOut) throws IOException {
      }
  
      public void looseUnmarshal(OpenWireFormat wireFormat, Object o, DataInput dataIn) throws IOException {
      }
  
      public void looseMarshalLong(OpenWireFormat wireFormat, long o, DataOutput dataOut) throws IOException {
          dataOut.writeLong(o);
      }
  
      public long looseUnmarshalLong(OpenWireFormat wireFormat, DataInput dataIn) throws IOException {
          return dataIn.readLong();
      }
  
      protected DataStructure looseUnmarsalNestedObject(OpenWireFormat wireFormat, DataInput dataIn)
          throws IOException {
          return wireFormat.looseUnmarshalNestedObject(dataIn);
      }
  
      protected void looseMarshalNestedObject(OpenWireFormat wireFormat, DataStructure o, DataOutput dataOut)
          throws IOException {
          wireFormat.looseMarshalNestedObject(o, dataOut);
      }
  
      protected DataStructure looseUnmarsalCachedObject(OpenWireFormat wireFormat, DataInput dataIn)
          throws IOException {
          if (wireFormat.isCacheEnabled()) {
              if (dataIn.readBoolean()) {
                  short index = dataIn.readShort();
                  DataStructure object = wireFormat.looseUnmarshalNestedObject(dataIn);
                  wireFormat.setInUnmarshallCache(index, object);
                  return object;
              } else {
                  short index = dataIn.readShort();
                  return wireFormat.getFromUnmarshallCache(index);
              }
          } else {
              return wireFormat.looseUnmarshalNestedObject(dataIn);
          }
      }
  
      protected void looseMarshalCachedObject(OpenWireFormat wireFormat, DataStructure o, DataOutput dataOut)
          throws IOException {
          if (wireFormat.isCacheEnabled()) {
              Short index = wireFormat.getMarshallCacheIndex(o);
              dataOut.writeBoolean(index == null);
              if (index == null) {
                  index = wireFormat.addToMarshallCache(o);
                  dataOut.writeShort(index);
                  wireFormat.looseMarshalNestedObject(o, dataOut);
              } else {
                  dataOut.writeShort(index);
              }
          } else {
              wireFormat.looseMarshalNestedObject(o, dataOut);
          }
      }
  
      protected Throwable looseUnmarsalThrowable(OpenWireFormat wireFormat, DataInput dataIn)
          throws IOException {
          if (dataIn.readBoolean()) {
              String clazz = looseUnmarshalString(dataIn);
              String message = looseUnmarshalString(dataIn);
              Throwable o = createThrowable(clazz, message);
              if (wireFormat.isStackTraceEnabled()) {
                  if (STACK_TRACE_ELEMENT_CONSTRUCTOR != null) {
                      StackTraceElement ss[] = new StackTraceElement[dataIn.readShort()];
                      for (int i = 0; i < ss.length; i++) {
                          try {
                              ss[i] = (StackTraceElement)STACK_TRACE_ELEMENT_CONSTRUCTOR
                                  .newInstance(new Object[] {looseUnmarshalString(dataIn),
                                                             looseUnmarshalString(dataIn),
                                                             looseUnmarshalString(dataIn),
                                                             dataIn.readInt()});
                          } catch (IOException e) {
                              throw e;
                          } catch (Throwable e) {
                          }
                      }
                      o.setStackTrace(ss);
                  } else {
                      short size = dataIn.readShort();
                      for (int i = 0; i < size; i++) {
                          looseUnmarshalString(dataIn);
                          looseUnmarshalString(dataIn);
                          looseUnmarshalString(dataIn);
                          dataIn.readInt();
                      }
                  }
                  o.initCause(looseUnmarsalThrowable(wireFormat, dataIn));
  
              }
              return o;
          } else {
              return null;
          }
      }
  
      protected void looseMarshalThrowable(OpenWireFormat wireFormat, Throwable o, DataOutput dataOut)
          throws IOException {
          dataOut.writeBoolean(o != null);
          if (o != null) {
              looseMarshalString(o.getClass().getName(), dataOut);
              looseMarshalString(cutMessageIfNeeded(o.getMessage()), dataOut);
              if (wireFormat.isStackTraceEnabled()) {
                  StackTraceElement[] stackTrace = o.getStackTrace();
                  dataOut.writeShort(stackTrace.length);
                  for (int i = 0; i < stackTrace.length; i++) {
                      StackTraceElement element = stackTrace[i];
                      looseMarshalString(element.getClassName(), dataOut);
                      looseMarshalString(element.getMethodName(), dataOut);
                      looseMarshalString(element.getFileName(), dataOut);
                      dataOut.writeInt(element.getLineNumber());
                  }
                  looseMarshalThrowable(wireFormat, o.getCause(), dataOut);
              }
          }
      }
  
      protected String looseUnmarshalString(DataInput dataIn) throws IOException {
          if (dataIn.readBoolean()) {
              return dataIn.readUTF();
          } else {
              return null;
          }
      }
  
      protected void looseMarshalString(String value, DataOutput dataOut) throws IOException {
          dataOut.writeBoolean(value != null);
          if (value != null) {
              dataOut.writeUTF(value);
          }
      }
  
      protected void looseMarshalObjectArray(OpenWireFormat wireFormat, DataStructure[] objects,
                                             DataOutput dataOut) throws IOException {
          dataOut.writeBoolean(objects != null);
          if (objects != null) {
              dataOut.writeShort(objects.length);
              for (int i = 0; i < objects.length; i++) {
                  looseMarshalNestedObject(wireFormat, objects[i], dataOut);
              }
          }
      }
  
      protected void looseMarshalConstByteArray(OpenWireFormat wireFormat, byte[] data, DataOutput dataOut,
                                                int i) throws IOException {
          dataOut.write(data, 0, i);
      }
  
      protected byte[] looseUnmarshalConstByteArray(DataInput dataIn, int i) throws IOException {
          byte data[] = new byte[i];
          dataIn.readFully(data);
          return data;
      }
  
      protected void looseMarshalByteArray(OpenWireFormat wireFormat, byte[] data, DataOutput dataOut)
          throws IOException {
          dataOut.writeBoolean(data != null);
          if (data != null) {
              dataOut.writeInt(data.length);
              dataOut.write(data);
          }
      }
  
      protected byte[] looseUnmarshalByteArray(OpenWireFormat wireFormat, DataInput dataIn) throws IOException {
          byte rc[] = null;
          if (dataIn.readBoolean()) {
              int size = dataIn.readInt();
              OpenWireUtil.validateBufferSize(wireFormat, size);
              rc = new byte[size];
              dataIn.readFully(rc);
          }
          return rc;
      }
  
      protected void looseMarshalByteSequence(OpenWireFormat wireFormat, ByteSequence data, DataOutput dataOut)
          throws IOException {
          dataOut.writeBoolean(data != null);
          if (data != null) {
              dataOut.writeInt(data.getLength());
              dataOut.write(data.getData(), data.getOffset(), data.getLength());
          }
      }
  
      protected ByteSequence looseUnmarshalByteSequence(OpenWireFormat wireFormat, DataInput dataIn) throws IOException {
          ByteSequence rc = null;
          if (dataIn.readBoolean()) {
              int size = dataIn.readInt();
              OpenWireUtil.validateBufferSize(wireFormat, size);
              byte[] t = new byte[size];
              dataIn.readFully(t);
              rc = new ByteSequence(t, 0, size);
          }
          return rc;
      }
  
      protected String cutMessageIfNeeded(final String message) {
          return (message.length() > MAX_EXCEPTION_MESSAGE_SIZE)?
              message.substring(0, MAX_EXCEPTION_MESSAGE_SIZE - 3) + "..." : message;
  
      }
  }
  