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.cassandra.db.marshal;
  
  import java.nio.ByteBuffer;
  import java.nio.charset.CharacterCodingException;
  import java.nio.charset.StandardCharsets;
  import java.util.ArrayList;
  import java.util.Collections;
  import java.util.HashMap;
  import java.util.List;
  import java.util.Map;
  import java.util.Objects;
  import java.util.concurrent.ConcurrentHashMap;
  
  import com.google.common.annotations.VisibleForTesting;
  import com.google.common.collect.ImmutableMap;
  import com.google.common.collect.Lists;
  import com.google.common.collect.Maps;
  import com.google.common.collect.Sets;
  import org.slf4j.Logger;
  import org.slf4j.LoggerFactory;
  
  import org.apache.cassandra.cql3.terms.Term;
  import org.apache.cassandra.db.TypeSizes;
  import org.apache.cassandra.exceptions.ConfigurationException;
  import org.apache.cassandra.exceptions.SyntaxException;
  import org.apache.cassandra.serializers.BytesSerializer;
  import org.apache.cassandra.serializers.MarshalException;
  import org.apache.cassandra.serializers.TypeSerializer;
  import org.apache.cassandra.transport.ProtocolVersion;
  import org.apache.cassandra.utils.ByteBufferUtil;
  import org.apache.cassandra.utils.bytecomparable.ByteComparable.Version;
  import org.apache.cassandra.utils.bytecomparable.ByteSource;
  import org.apache.cassandra.utils.bytecomparable.ByteSourceInverse;
  
  import static com.google.common.collect.Iterables.any;
  
  /*
   * The encoding of a DynamicCompositeType column name should be:
   *   <component><component><component> ...
   * where <component> is:
   *   <comparator part><value><'end-of-component' byte>
   * where:
   *   - <comparator part>: either the comparator full name, or a declared
   *     aliases. This is at least 2 bytes (those 2 bytes are called header in
   *     the following). If the first bit of the header is 1, then this
   *     comparator part is an alias, otherwise it's a comparator full name:
   *       - aliases: the actual alias is the 2nd byte of header taken as a
   *         character. The whole <comparator part> is thus 2 byte long.
   *       - comparator full name: the header is the length of the remaining
   *         part. The remaining part is the UTF-8 encoded comparator class
   *         name.
   *   - <value>: the component value bytes preceded by 2 byte containing the
   *     size of value (see CompositeType).
   *   - 'end-of-component' byte is defined as in CompositeType
   */
  public class DynamicCompositeType extends AbstractCompositeType
  {
      public static class Serializer extends BytesSerializer
      {
          // aliases are held to make sure the serializer is unique for each collection of types, this is to make sure it's
          // safe to cache in all cases
          private final Map<Byte, AbstractType<?>> aliases;
  
          public Serializer(Map<Byte, AbstractType<?>> aliases)
          {
              this.aliases = aliases;
          }
  
          @Override
          public boolean equals(Object o)
          {
              if (this == o) return true;
              if (o == null || getClass() != o.getClass()) return false;
              Serializer that = (Serializer) o;
              return aliases.equals(that.aliases);
          }
  
          @Override
          public int hashCode()
          {
              return Objects.hash(aliases);
          }
      }
      private static final Logger logger = LoggerFactory.getLogger(DynamicCompositeType.class);
  
      private static final ByteSource[] EMPTY_BYTE_SOURCE_ARRAY = new ByteSource[0];
      private static final String REVERSED_TYPE = ReversedType.class.getSimpleName();
  
      @VisibleForTesting
      public final Map<Byte, AbstractType<?>> aliases;
      private final Map<AbstractType<?>, Byte> inverseMapping;
      private final Serializer serializer;
  
      // interning instances
      private static final ConcurrentHashMap<Map<Byte, AbstractType<?>>, DynamicCompositeType> instances = new ConcurrentHashMap<>();
  
      public static DynamicCompositeType getInstance(TypeParser parser)
      {
          return getInstance(parser.getAliasParameters());
      }
  
      public static DynamicCompositeType getInstance(Map<Byte, AbstractType<?>> aliases)
      {
          DynamicCompositeType dct = instances.get(aliases);
          return null == dct
               ? instances.computeIfAbsent(aliases, DynamicCompositeType::new)
               : dct;
      }
  
      private DynamicCompositeType(Map<Byte, AbstractType<?>> aliases)
      {
          this.aliases = ImmutableMap.copyOf(aliases);
          this.serializer = new Serializer(this.aliases);
          this.inverseMapping = new HashMap<>();
          for (Map.Entry<Byte, AbstractType<?>> en : aliases.entrySet())
              this.inverseMapping.put(en.getValue(), en.getKey());
      }
  
      public int size()
      {
          return aliases.size();
      }
  
      @Override
      public List<AbstractType<?>> subTypes()
      {
          return new ArrayList<>(aliases.values());
      }
  
      @Override
      public TypeSerializer<ByteBuffer> getSerializer()
      {
          return serializer;
      }
  
      protected <V> boolean readIsStatic(V value, ValueAccessor<V> accessor)
      {
          // We don't have the static nothing for DCT
          return false;
      }
  
      protected int startingOffset(boolean isStatic)
      {
          return 0;
      }
  
      protected <V> int getComparatorSize(int i, V value, ValueAccessor<V> accessor, int offset)
      {
          int header = accessor.getShort(value, offset);
          if ((header & 0x8000) == 0)
          {
              return 2 + header;
          }
          else
          {
              return 2;
          }
      }
  
      private <V> AbstractType<?> getComparator(V value, ValueAccessor<V> accessor, int offset)
      {
          try
          {
              int header = accessor.getShort(value, offset);
              if ((header & 0x8000) == 0)
              {
  
                  String name = accessor.toString(accessor.slice(value, offset + 2, header));
                  return TypeParser.parse(name);
              }
              else
              {
                  return aliases.get((byte)(header & 0xFF));
              }
          }
          catch (CharacterCodingException e)
          {
              throw new RuntimeException(e);
          }
      }
  
      protected <V> AbstractType<?> getComparator(int i, V value, ValueAccessor<V> accessor, int offset)
      {
          return getComparator(value, accessor, offset);
      }
  
      protected <VL, VR> AbstractType<?> getComparator(int i, VL left, ValueAccessor<VL> accessorL, VR right, ValueAccessor<VR> accessorR, int offsetL, int offsetR)
      {
          AbstractType<?> comp1 = getComparator(left, accessorL, offsetL);
          AbstractType<?> comp2 = getComparator(right, accessorR, offsetR);
          AbstractType<?> rawComp = comp1;
  
          /*
           * If both types are ReversedType(Type), we need to compare on the wrapped type (which may differ between the two types) to avoid
           * incompatible comparisons being made.
           */
          if ((comp1 instanceof ReversedType) && (comp2 instanceof ReversedType))
          {
              comp1 = ((ReversedType<?>) comp1).baseType;
              comp2 = ((ReversedType<?>) comp2).baseType;
          }
  
          // Fast test if the comparator uses singleton instances
          if (comp1 != comp2)
          {
              /*
               * We compare component of different types by comparing the
               * comparator class names. We start with the simple classname
               * first because that will be faster in almost all cases, but
               * fallback on the full name if necessary
               */
              int cmp = comp1.getClass().getSimpleName().compareTo(comp2.getClass().getSimpleName());
              if (cmp != 0)
                  return cmp < 0 ? FixedValueComparator.alwaysLesserThan : FixedValueComparator.alwaysGreaterThan;
  
              cmp = comp1.getClass().getName().compareTo(comp2.getClass().getName());
              if (cmp != 0)
                  return cmp < 0 ? FixedValueComparator.alwaysLesserThan : FixedValueComparator.alwaysGreaterThan;
  
              // if cmp == 0, we're actually having the same type, but one that
              // did not have a singleton instance. It's ok (though inefficient).
          }
          // Use the raw comparator (prior to ReversedType unwrapping)
          return rawComp;
      }
  
      protected <V> AbstractType<?> getAndAppendComparator(int i, V value, ValueAccessor<V> accessor, StringBuilder sb, int offset)
      {
          try
          {
              int header = accessor.getShort(value, offset);
              if ((header & 0x8000) == 0)
              {
                  String name = accessor.toString(accessor.slice(value, offset + 2, header));
                  sb.append(name).append("@");
                  return TypeParser.parse(name);
              }
              else
              {
                  sb.append((char)(header & 0xFF)).append("@");
                  return aliases.get((byte)(header & 0xFF));
              }
          }
          catch (CharacterCodingException e)
          {
              throw new RuntimeException(e);
          }
      }
  
      @Override
      public <V> ByteSource asComparableBytes(ValueAccessor<V> accessor, V data, Version version)
      {
          List<ByteSource> srcs = new ArrayList<>();
          int length = accessor.size(data);
  
          // statics go first
          boolean isStatic = readIsStatic(data, accessor);
          int offset = startingOffset(isStatic);
          srcs.add(isStatic ? null : ByteSource.EMPTY);
  
          byte lastEoc = 0;
          int i = 0;
          while (offset < length)
          {
              // Only the end-of-component byte of the last component of this composite can be non-zero, so the
              // component before can't have a non-zero end-of-component byte.
              assert lastEoc == 0 : lastEoc;
  
              AbstractType<?> comp = getComparator(data, accessor, offset);
              offset += getComparatorSize(i, data, accessor, offset);
              // The comparable bytes for the component need to ensure comparisons consistent with
              // AbstractCompositeType.compareCustom(ByteBuffer, ByteBuffer) and
              // DynamicCompositeType.getComparator(int, ByteBuffer, ByteBuffer):
              if (version == Version.LEGACY || !(comp instanceof ReversedType))
              {
                  // ...most often that means just adding the short name of the type, followed by the full name of the type.
                  srcs.add(ByteSource.of(comp.getClass().getSimpleName(), version));
                  srcs.add(ByteSource.of(comp.getClass().getName(), version));
              }
              else
              {
                  // ...however some times the component uses a complex type (currently the only supported complex type
                  // is ReversedType - we can't have elements that are of MapType, CompositeType, TupleType, etc.)...
                  ReversedType<?> reversedComp = (ReversedType<?>) comp;
                  // ...in this case, we need to add the short name of ReversedType before the short name of the base
                  // type, to ensure consistency with DynamicCompositeType.getComparator(int, ByteBuffer, ByteBuffer).
                  srcs.add(ByteSource.of(REVERSED_TYPE, version));
                  srcs.add(ByteSource.of(reversedComp.baseType.getClass().getSimpleName(), version));
                  srcs.add(ByteSource.of(reversedComp.baseType.getClass().getName(), version));
              }
              // Only then the payload of the component gets encoded.
              int componentLength = accessor.getUnsignedShort(data, offset);
              offset += 2;
              srcs.add(comp.asComparableBytes(accessor, accessor.slice(data, offset, componentLength), version));
              offset += componentLength;
              // The end-of-component byte also takes part in the comparison, and therefore needs to be encoded.
              lastEoc = accessor.getByte(data, offset);
              offset += 1;
              srcs.add(ByteSource.oneByte(version == Version.LEGACY ? lastEoc : lastEoc & 0xFF ^ 0x80));
              ++i;
          }
  
          return ByteSource.withTerminatorMaybeLegacy(version, ByteSource.END_OF_STREAM, srcs.toArray(EMPTY_BYTE_SOURCE_ARRAY));
      }
  
      @Override
      public <V> V fromComparableBytes(ValueAccessor<V> accessor, ByteSource.Peekable comparableBytes, Version version)
      {
          // For ByteComparable.Version.LEGACY the terminator byte is ByteSource.END_OF_STREAM. Just like with
          // CompositeType, this means that in multi-component sequences the terminator may be transformed to a regular
          // component separator, but unlike CompositeType (where we have the expected number of types/components),
          // this can make the end of the whole dynamic composite type indistinguishable from the end of a component
          // somewhere in the middle of the dynamic composite type. Because of that, DynamicCompositeType elements
          // cannot always be safely decoded using that encoding version.
          // Even more so than with CompositeType, we just take advantage of the fact that we don't need to decode from
          // Version.LEGACY, assume that we never do that, and assert it here.
          assert version != Version.LEGACY;
  
          if (comparableBytes == null)
              return accessor.empty();
  
          // The first byte is the isStatic flag which we don't need but must consume to continue past it.
          comparableBytes.next();
  
          List<AbstractType<?>> types = new ArrayList<>();
          List<V> values = new ArrayList<>();
          byte lastEoc = 0;
  
          for (int separator = comparableBytes.next(); separator != ByteSource.TERMINATOR; separator = comparableBytes.next())
          {
              // Solely the end-of-component byte of the last component of this composite can be non-zero.
              assert lastEoc == 0 : lastEoc;
  
              boolean isReversed = false;
              // Decode the next type's simple class name that is encoded before its fully qualified class name (in order
              // for comparisons to work correctly).
              String simpleClassName = ByteSourceInverse.getString(ByteSourceInverse.nextComponentSource(comparableBytes, separator));
              if (REVERSED_TYPE.equals(simpleClassName))
              {
                  // Special-handle if the type is reversed (and decode the actual base type simple class name).
                  isReversed = true;
                  simpleClassName = ByteSourceInverse.getString(ByteSourceInverse.nextComponentSource(comparableBytes));
              }
  
              // Decode the type's fully qualified class name and parse the actual type from it.
              String fullClassName = ByteSourceInverse.getString(ByteSourceInverse.nextComponentSource(comparableBytes));
              assert fullClassName.endsWith(simpleClassName);
              if (isReversed)
                  fullClassName = REVERSED_TYPE + '(' + fullClassName + ')';
              AbstractType<?> type = TypeParser.parse(fullClassName);
              assert type != null;
              types.add(type);
  
              // Decode the payload from this type.
              V value = type.fromComparableBytes(accessor, ByteSourceInverse.nextComponentSource(comparableBytes), version);
              values.add(value);
  
              // Also decode the corresponding end-of-component byte - the last one we decode will be taken into
              // account when we deserialize the decoded data into an object.
              lastEoc = ByteSourceInverse.getSignedByte(ByteSourceInverse.nextComponentSource(comparableBytes));
          }
          return build(accessor, types, inverseMapping, values, lastEoc);
      }
  
      public ByteBuffer build(Map<Byte, Object> valuesMap)
      {
          Sets.SetView<Byte> unknownAliases = Sets.difference(valuesMap.keySet(), aliases.keySet());
          if (!unknownAliases.isEmpty())
              throw new IllegalArgumentException(String.format("Aliases %s used; only valid values are %s", unknownAliases, aliases.keySet()));
          List<AbstractType<?>> types = new ArrayList<>(valuesMap.size());
          List<ByteBuffer> values = new ArrayList<>(valuesMap.size());
          for (Map.Entry<Byte, Object> e : valuesMap.entrySet())
          {
              @SuppressWarnings("rawtype")
              AbstractType type = aliases.get(e.getKey());
              types.add(type);
              values.add(type.decompose(e.getValue()));
          }
          return build(ByteBufferAccessor.instance, types, inverseMapping, values, (byte) 0);
      }
  
      public static ByteBuffer build(List<String> types, List<ByteBuffer> values)
      {
          return build(ByteBufferAccessor.instance,
                       Lists.transform(types, TypeParser::parse),
                       Collections.emptyMap(),
                       values,
                       (byte) 0);
      }
  
      @VisibleForTesting
      public static <V> V build(ValueAccessor<V> accessor,
                                List<AbstractType<?>> types,
                                Map<AbstractType<?>, Byte> inverseMapping,
                                List<V> values,
                                byte lastEoc)
      {
          assert types.size() == values.size();
  
          int numComponents = types.size();
          // Compute the total number of bytes that we'll need to store the types and their payloads.
          int totalLength = 0;
          for (int i = 0; i < numComponents; ++i)
          {
              AbstractType<?> type = types.get(i);
              Byte alias = inverseMapping.get(type);
              int typeNameLength = alias == null ? type.toString().getBytes(StandardCharsets.UTF_8).length : 0;
              // The type data will be stored by means of the type's fully qualified name, not by aliasing, so:
              //   1. The type data header should be the fully qualified name length in bytes.
              //   2. The length should be small enough so that it fits in 15 bits (2 bytes with the first bit zero).
              assert typeNameLength <= 0x7FFF;
              int valueLength = accessor.size(values.get(i));
              // The value length should also expect its first bit to be 0, as the length should be stored as a signed
              // 2-byte value (short).
              assert valueLength <= 0x7FFF;
              totalLength += 2 + typeNameLength + 2 + valueLength + 1;
          }
  
          V result = accessor.allocate(totalLength);
          int offset = 0;
          for (int i = 0; i < numComponents; ++i)
          {
              AbstractType<?> type = types.get(i);
              Byte alias = inverseMapping.get(type);
              if (alias == null)
              {
                  // Write the type data (2-byte length header + the fully qualified type name in UTF-8).
                  byte[] typeNameBytes = type.toString().getBytes(StandardCharsets.UTF_8);
                  accessor.putShort(result,
                                    offset,
                                    (short) typeNameBytes.length); // this should work fine also if length >= 32768
                  offset += 2;
                  accessor.copyByteArrayTo(typeNameBytes, 0, result, offset, typeNameBytes.length);
                  offset += typeNameBytes.length;
              }
              else
              {
                  accessor.putShort(result, offset, (short) (alias | 0x8000));
                  offset += 2;
              }
  
              // Write the type payload data (2-byte length header + the payload).
              V value = values.get(i);
              int bytesToCopy = accessor.size(value);
              if ((short) bytesToCopy != bytesToCopy)
                  throw new IllegalArgumentException(String.format("Value of type %s is of length %d; does not fit in a short", type.asCQL3Type(), bytesToCopy));
              accessor.putShort(result, offset, (short) bytesToCopy);
              offset += 2;
              accessor.copyTo(value, 0, result, accessor, offset, bytesToCopy);
              offset += bytesToCopy;
  
              // Write the end-of-component byte.
              accessor.putByte(result, offset, i != numComponents - 1 ? (byte) 0 : lastEoc);
              offset += 1;
          }
          return result;
      }
  
      protected ParsedComparator parseComparator(int i, String part)
      {
          return new DynamicParsedComparator(part);
      }
  
      protected <V> AbstractType<?> validateComparator(int i, V input, ValueAccessor<V> accessor, int offset) throws MarshalException
      {
          AbstractType<?> comparator = null;
          if (accessor.sizeFromOffset(input, offset) < 2)
              throw new MarshalException("Not enough bytes to header of the comparator part of component " + i);
          int header = accessor.getShort(input, offset);
          offset += TypeSizes.SHORT_SIZE;
          if ((header & 0x8000) == 0)
          {
              if (accessor.sizeFromOffset(input, offset) < header)
                  throw new MarshalException("Not enough bytes to read comparator name of component " + i);
  
              V value = accessor.slice(input, offset, header);
              String valueStr = null;
              try
              {
                  valueStr = accessor.toString(value);
                  comparator = TypeParser.parse(valueStr);
              }
              catch (CharacterCodingException ce)
              {
                  // ByteBufferUtil.string failed.
                  // Log it here and we'll further throw an exception below since comparator == null
                  logger.error("Failed when decoding the byte buffer in ByteBufferUtil.string()", ce);
              }
              catch (Exception e)
              {
                  // parse failed.
                  // Log it here and we'll further throw an exception below since comparator == null
                  logger.error("Failed to parse value string \"{}\" with exception:", valueStr, e);
              }
          }
          else
          {
              comparator = aliases.get((byte)(header & 0xFF));
          }
  
          if (comparator == null)
              throw new MarshalException("Cannot find comparator for component " + i);
          else
              return comparator;
      }
  
      public ByteBuffer decompose(Object... objects)
      {
          throw new UnsupportedOperationException();
      }
  
      @Override
      public boolean isCompatibleWith(AbstractType<?> previous)
      {
          if (this == previous)
              return true;
  
          if (!(previous instanceof DynamicCompositeType))
              return false;
  
          // Adding new aliases is fine (but removing is not)
          // Note that modifying the type for an alias to a compatible type is
          // *not* fine since this would deal correctly with mixed aliased/not
          // aliased component.
          DynamicCompositeType cp = (DynamicCompositeType)previous;
          if (aliases.size() < cp.aliases.size())
              return false;
  
          for (Map.Entry<Byte, AbstractType<?>> entry : cp.aliases.entrySet())
          {
              AbstractType<?> tprev = entry.getValue();
              AbstractType<?> tnew = aliases.get(entry.getKey());
              if (tnew == null || tnew != tprev)
                  return false;
          }
          return true;
      }
  
      @Override
      public <V> boolean referencesUserType(V name, ValueAccessor<V> accessor)
      {
          return any(aliases.values(), t -> t.referencesUserType(name, accessor));
      }
  
      @Override
      public DynamicCompositeType withUpdatedUserType(UserType udt)
      {
          if (!referencesUserType(udt.name))
              return this;
  
          instances.remove(aliases);
  
          return getInstance(Maps.transformValues(aliases, v -> v.withUpdatedUserType(udt)));
      }
  
      @Override
      public AbstractType<?> expandUserTypes()
      {
          return getInstance(Maps.transformValues(aliases, v -> v.expandUserTypes()));
      }
  
      private class DynamicParsedComparator implements ParsedComparator
      {
          final AbstractType<?> type;
          final boolean isAlias;
          final String comparatorName;
          final String remainingPart;
  
          DynamicParsedComparator(String part)
          {
              String[] splits = part.split("@");
              switch (splits.length)
              {
                  default:
                      throw new IllegalArgumentException("Invalid component representation: " + part);
                  case 1:
                  {
                      // empty is allowed for some types, so leave this to the higher level to validate empty makes sense for the type
                      comparatorName = splits[0];
                      remainingPart = "";
                  }
                  break;
                  case 2:
                  {
                      comparatorName = splits[0];
                      remainingPart = splits[1];
                  }
                  break;
              }
  
              try
              {
                  AbstractType<?> t = null;
                  if (comparatorName.length() == 1)
                  {
                      // try for an alias
                      // Note: the char to byte cast is theorically bogus for unicode character. I take full
                      // responsibility if someone get hit by this (without making it on purpose)
                      t = aliases.get((byte)comparatorName.charAt(0));
                  }
                  isAlias = t != null;
                  if (!isAlias)
                  {
                      t = TypeParser.parse(comparatorName);
                  }
                  type = t;
              }
              catch (SyntaxException | ConfigurationException e)
              {
                  throw new IllegalArgumentException(e);
              }
          }
  
          public AbstractType<?> getAbstractType()
          {
              return type;
          }
  
          public String getRemainingPart()
          {
              return remainingPart;
          }
  
          public int getComparatorSerializedSize()
          {
              return isAlias ? 2 : 2 + ByteBufferUtil.bytes(comparatorName).remaining();
          }
  
          public void serializeComparator(ByteBuffer bb)
          {
              int header = 0;
              if (isAlias)
                  header = 0x8000 | (((byte)comparatorName.charAt(0)) & 0xFF);
              else
                  header = comparatorName.length();
              ByteBufferUtil.writeShortLength(bb, header);
  
              if (!isAlias)
                  bb.put(ByteBufferUtil.bytes(comparatorName));
          }
      }
  
      @Override
      public boolean equals(Object o)
      {
          if (this == o) return true;
          if (o == null || getClass() != o.getClass()) return false;
          DynamicCompositeType that = (DynamicCompositeType) o;
          return aliases.equals(that.aliases);
      }
  
      @Override
      public int hashCode()
      {
          return Objects.hash(aliases);
      }
  
      @Override
      public String toString()
      {
          return getClass().getName() + TypeParser.stringifyAliasesParameters(aliases);
      }
  
      /*
       * A comparator that always sorts it's first argument before the second
       * one.
       */
      @VisibleForTesting
      public static class FixedValueComparator extends AbstractType<Void>
      {
          public static final FixedValueComparator alwaysLesserThan = new FixedValueComparator(-1);
          public static final FixedValueComparator alwaysGreaterThan = new FixedValueComparator(1);
  
          private final int cmp;
  
          public FixedValueComparator(int cmp)
          {
              super(ComparisonType.CUSTOM);
              this.cmp = cmp;
          }
  
          public <VL, VR> int compareCustom(VL left, ValueAccessor<VL> accessorL, VR right, ValueAccessor<VR> accessorR)
          {
              return cmp;
          }
  
          @Override
          public <V> Void compose(V value, ValueAccessor<V> accessor)
          {
              throw new UnsupportedOperationException();
          }
  
          public ByteBuffer decompose(Void value)
          {
              throw new UnsupportedOperationException();
          }
  
          public <V> String getString(V value, ValueAccessor<V> accessor)
          {
              throw new UnsupportedOperationException();
          }
  
          public ByteBuffer fromString(String str)
          {
              throw new UnsupportedOperationException();
          }
  
          @Override
          public Term fromJSONObject(Object parsed)
          {
              throw new UnsupportedOperationException();
          }
  
          @Override
          public String toJSONString(ByteBuffer buffer, ProtocolVersion protocolVersion)
          {
              throw new UnsupportedOperationException();
          }
  
          @Override
          public void validate(ByteBuffer bytes)
          {
              throw new UnsupportedOperationException();
          }
  
          public TypeSerializer<Void> getSerializer()
          {
              throw new UnsupportedOperationException();
          }
      }
  }
  