code: /*
   * Copyright 2018 Mitsunori Komatsu (komamitsu)
   *
   * 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 org.komamitsu.fluency;
  
  import com.fasterxml.jackson.core.JsonGenerator;
  import com.fasterxml.jackson.databind.SerializerProvider;
  import com.fasterxml.jackson.databind.annotation.JsonSerialize;
  import com.fasterxml.jackson.databind.ser.std.StdSerializer;
  import org.msgpack.jackson.dataformat.MessagePackExtensionType;
  import org.msgpack.jackson.dataformat.MessagePackGenerator;
  
  import java.io.IOException;
  import java.nio.ByteBuffer;
  
  @JsonSerialize(using = EventTime.Serializer.class)
  public class EventTime
  {
      private final long seconds;
      private final long nanoseconds;
  
      /**
       * Constructs an <code>EventTime</code>.
       *
       * @param seconds the epoch seconds. This should be a 32-bit value.
       * @param nanoseconds the nanoseconds. This should be a 32-bit value.
       */
      public EventTime(long seconds, long nanoseconds)
      {
          if (seconds >> 32 != 0) {
              throw new IllegalArgumentException("`seconds` should be a 32-bit value");
          }
  
          if (nanoseconds >> 32 != 0) {
              throw new IllegalArgumentException("`nanoseconds` should be a 32-bit value");
          }
  
          this.seconds = seconds;
          this.nanoseconds = nanoseconds;
      }
  
      /**
       * Constructs an <code>EventTime</code>.
       *
       * @param epochSeconds the epoch seconds. This should be a 32-bit value.
       */
      public static EventTime fromEpoch(long epochSeconds)
      {
          return new EventTime(epochSeconds, 0);
      }
  
      /**
       * Constructs an <code>EventTime</code>.
       *
       * @param epochSeconds the epoch seconds. This should be a 32-bit value.
       * @param nanoseconds the nanoseconds. This should be a 32-bit value.
       */
      public static EventTime fromEpoch(long epochSeconds, long nanoseconds)
      {
          return new EventTime(epochSeconds, nanoseconds);
      }
  
      /**
       * Constructs an <code>EventTime</code>.
       *
       * @param epochMillisecond the epoch milli seconds.
       * This should be a 32-bit value.
       */
      public static EventTime fromEpochMilli(long epochMillisecond)
      {
          return new EventTime(epochMillisecond / 1000, (epochMillisecond % 1000) * 1000000);
      }
  
      public long getSeconds()
      {
          return seconds;
      }
  
      public long getNanoseconds()
      {
          return nanoseconds;
      }
  
      /**
       * @deprecated As of release 1.9, replaced by {@link #getNanoseconds()}
       */
      @Deprecated
      public long getNanoSeconds()
      {
          return nanoseconds;
      }
  
      @Override
      public boolean equals(Object o)
      {
          if (this == o) {
              return true;
          }
          if (!(o instanceof EventTime)) {
              return false;
          }
  
          EventTime eventTime = (EventTime) o;
  
          if (seconds != eventTime.seconds) {
              return false;
          }
          return nanoseconds == eventTime.nanoseconds;
      }
  
      @Override
      public int hashCode()
      {
          int result = (int) (seconds ^ (seconds >>> 32));
          result = 31 * result + (int) (nanoseconds ^ (nanoseconds >>> 32));
          return result;
      }
  
      @Override
      public String toString()
      {
          return "EventTime{" +
                  "seconds=" + seconds +
                  ", nanoseconds=" + nanoseconds +
                  '}';
      }
  
      public static class Serializer
              extends StdSerializer<EventTime>
      {
          public Serializer()
          {
              super(EventTime.class);
          }
  
          protected Serializer(Class<EventTime> t)
          {
              super(t);
          }
  
          @Override
          public void serialize(EventTime value, JsonGenerator gen, SerializerProvider provider)
                  throws IOException
          {
              if (!(gen instanceof MessagePackGenerator)) {
                  throw new IllegalStateException("" +
                          "This class should be serialized by MessagePackGenerator, but `gen` is " + gen.getClass());
              }
  
              MessagePackGenerator messagePackGenerator = (MessagePackGenerator) gen;
  
              // https://github.com/fluent/fluentd/wiki/Forward-Protocol-Specification-v1#eventtime-ext-format
              //
              // +-------+----+----+----+----+----+----+----+----+----+
              // |     1 |  2 |  3 |  4 |  5 |  6 |  7 |  8 |  9 | 10 |
              // +-------+----+----+----+----+----+----+----+----+----+
              // |    D7 | 00 | seconds from epoch|     nanosecond    |
              // +-------+----+----+----+----+----+----+----+----+----+
              // |fixext8|type| 32bits integer BE | 32bits integer BE |
              // +-------+----+----+----+----+----+----+----+----+----+
              ByteBuffer buffer = ByteBuffer.allocate(8);
              buffer.putInt((int) value.seconds).putInt((int) value.nanoseconds);
              messagePackGenerator.writeExtensionType(new MessagePackExtensionType((byte) 0x0, buffer.array()));
          }
      }
  }
  