All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.linkedin.restli.common.KeyValueRecord Maven / Gradle / Ivy

Go to download

Pegasus is a framework for building robust, scalable service architectures using dynamic discovery and simple asychronous type-checked REST + JSON APIs.

There is a newer version: 27.7.18
Show newest version
/*
   Copyright (c) 2013 LinkedIn Corp.

   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 com.linkedin.restli.common;


import com.linkedin.data.DataMap;
import com.linkedin.data.schema.DataSchema;
import com.linkedin.data.schema.DataSchemaUtil;
import com.linkedin.data.schema.RecordDataSchema;
import com.linkedin.data.schema.TyperefDataSchema;
import com.linkedin.data.template.DataTemplateUtil;
import com.linkedin.data.template.GetMode;
import com.linkedin.data.template.RecordTemplate;
import com.linkedin.data.template.SetMode;
import com.linkedin.util.CustomTypeUtil;

import java.util.Map;

/**
 * Represents an entity in the form of a key-value pair where the key can be a primitive, a {@link CompoundKey}, or a
 * {@link ComplexResourceKey} and the value is a {@link RecordTemplate}
 *
 * @author kparikh
 */
public class KeyValueRecord extends RecordTemplate
{
  public static final String KEY_FIELD_NAME = "key";
  public static final String VALUE_FIELD_NAME = "value";
  public static final String PARAMS_FIELD_NAME = "$params";

  /**
   * Needed by {@link DataTemplateUtil#templateConstructor(Class)}
   * @param dataMap provides the contents of the key
   */
  public KeyValueRecord(DataMap dataMap)
  {
    super(dataMap, null);
  }

  /*package private*/KeyValueRecord()
  {
    super(new DataMap(), null);
  }

  /**
   * Sets a primitive key. If the key is a typeref the typeref is followed and the primitive value is stored.
   * @param keyField key field
   * @param key the primitive key to set
   * @param keyType the type of the key
   */
  @SuppressWarnings("unchecked")
  void setPrimitiveKey(RecordDataSchema.Field keyField, K key, TypeSpec keyType)
  {
    DataSchema keySchema = keyType.getSchema();
    if (keySchema.isPrimitive())
    {
      putDirect(keyField, keyType.getType(), keyType.getType(), key, SetMode.IGNORE_NULL);
      return;
    }
    switch (keySchema.getType())
    {
      case TYPEREF:
        TyperefDataSchema typerefDataSchema = (TyperefDataSchema)keySchema;
        DataSchema.Type dereferencedType = keySchema.getDereferencedType();
        Class javaClassForSchema = CustomTypeUtil.getJavaCustomTypeClassFromSchema(typerefDataSchema);
        if (javaClassForSchema == null)
        {
          // typeref to a primitive. In this case the keyClass is a primitive, and so is the key.
          putDirect(keyField, keyType.getType(), keyType.getType(), key, SetMode.IGNORE_NULL);
        }
        else
        {
          // typeref to a custom type. In this case the keyClass is the typeref class, but the class of the key
          // is the custom class.
          Class keyDereferencedClass = DataSchemaUtil.dataSchemaTypeToPrimitiveDataSchemaClass(dereferencedType);
          putDirect(keyField, (Class) javaClassForSchema, keyDereferencedClass, key, SetMode.IGNORE_NULL);
        }
        break;
      case ENUM:
        putDirect(keyField, keyType.getType(), String.class, key, SetMode.IGNORE_NULL);
        break;
      default:
        throw new IllegalArgumentException("key is not a primitive, typeref, or an enum!");
    }
  }

  /**
   * Sets a {@link ComplexResourceKey}
   * @param keyField key field
   * @param paramsField params field
   * @param key the complex key to set
   * @param complexKeyType the type of the {@link ComplexResourceKey}
   * @param  type of keyKeyClass
   * @param  type of keyParamsClass
   */
   void setComplexKey(RecordDataSchema.Field keyField,
                                                                            RecordDataSchema.Field paramsField,
                                                                            K key,
                                                                            ComplexKeySpec complexKeyType)
  {
    if (!(key instanceof ComplexResourceKey))
    {
      throw new IllegalArgumentException("Key must be a ComplexResourceKey!");
    }
    @SuppressWarnings("unchecked")
    ComplexResourceKey complexResourceKey = (ComplexResourceKey)key;
    KK keyKey = complexResourceKey.getKey();
    KP keyParams = complexResourceKey.getParams();

    putWrapped(keyField, complexKeyType.getKeyType().getType(), keyKey, SetMode.IGNORE_NULL);
    putWrapped(paramsField, complexKeyType.getParamsType().getType(), keyParams, SetMode.IGNORE_NULL);
  }

  /**
   * Sets a {@link CompoundKey} key.
   * The "key" field in this object will hold a {@link DataMap}. The keys in this {@code DataMap} will be the key names
   * from the {@code compoundKey}. The values will be the values for these keys.
   *
   * @param keyField compoundKey field
   * @param key the key to set.
   */
  void setCompoundKey(RecordDataSchema.Field keyField, K key, Map fieldTypes)
  {
    if (!(key instanceof CompoundKey))
    {
      throw new IllegalArgumentException("Key must be a CompoundKey!");
    }
    CompoundKey compoundKey = (CompoundKey)key;
    DataMap compoundKeyData = compoundKey.toDataMap(fieldTypes);
    putDirect(keyField, DataMap.class, DataMap.class, compoundKeyData, SetMode.IGNORE_NULL);
  }

  /**
   * Sets the value to be stored
   * @param valueField value field
   * @param value the value to set
   * @param valueClass the class of the {@code value}
   */
  public void setValue(RecordDataSchema.Field valueField, V value, Class valueClass)
  {
    putWrapped(valueField, valueClass, value, SetMode.IGNORE_NULL);
  }

  /**
   * Get the stored primitive key
   * @param keyClass class of the key
   * @return the stored key as an object of {@code keyClass}
   */
  public K getPrimitiveKey(Class keyClass)
  {
    return getPrimitiveKey(TypeSpec.forClassMaybeNull(keyClass));
  }

  @SuppressWarnings("unchecked")
  public K getPrimitiveKey(TypeSpec keyType)
  {
    StringBuilder sb = new StringBuilder(10);

    DataSchema keySchema = keyType.getSchema();

    RecordDataSchema.Field keyField = new RecordDataSchema.Field(keySchema);
    keyField.setName(KEY_FIELD_NAME, sb);

    if (keySchema.isPrimitive() || keySchema.getType() == DataSchema.Type.ENUM)
    {
      return obtainDirect(keyField, keyType.getType(), GetMode.DEFAULT);
    }
    else if (keySchema.getType() == DataSchema.Type.TYPEREF)
    {
      TyperefDataSchema typerefDataSchema = (TyperefDataSchema)keySchema;
      Class javaClass = CustomTypeUtil.getJavaCustomTypeClassFromSchema(typerefDataSchema);
      if (javaClass == null)
      {
        // typeref to a primitive. keyClass is a primitive
        return obtainDirect(keyField, keyType.getType(), GetMode.DEFAULT);
      }
      else
      {
        // typeref to a custom type. javaClass is the custom type that the typeref refers to.
        return (K) obtainDirect(keyField, javaClass, GetMode.DEFAULT);
      }
    }
    else
    {
      throw new IllegalArgumentException("key is not a primitive, typeref, or an enum!");
    }
  }

  /**
   * Get the stored {@link ComplexResourceKey}
   * @param keyKeyClass the class of the key in the {@link ComplexResourceKey}
   * @param keyParamsClass the class of the params in the {@link ComplexResourceKey}
   * @param  type of keyKeyClass
   * @param  type of keyParamsClass
   * @return a key
   */
  public  ComplexResourceKey getComplexKey(Class keyKeyClass,
                                                                          Class keyParamsClass)
  {
    return getComplexKey(ComplexKeySpec.forClassesMaybeNull(keyKeyClass, keyParamsClass));
  }

  /**
   * Get the stored {@link ComplexResourceKey}
   * @param complexKeyType the type of the {@link ComplexResourceKey}
   * @param  type of keyKeyClass
   * @param  type of keyParamsClass
   * @return a key
   */
  public  ComplexResourceKey getComplexKey(ComplexKeySpec complexKeyType)
  {
    StringBuilder sb = new StringBuilder(10);

    RecordDataSchema.Field keyField = new RecordDataSchema.Field(complexKeyType.getKeyType().getSchema());
    keyField.setName(KEY_FIELD_NAME, sb);
    RecordDataSchema.Field paramsField = new RecordDataSchema.Field(complexKeyType.getParamsType().getSchema());
    paramsField.setName(PARAMS_FIELD_NAME, sb);

    KK keyKey = obtainWrapped(keyField, complexKeyType.getKeyType().getType(), GetMode.DEFAULT);
    KP keyParams = obtainWrapped(paramsField, complexKeyType.getParamsType().getType(), GetMode.DEFAULT);

    return new ComplexResourceKey(keyKey, keyParams);
  }

  /**
   * Gets the stored {@link CompoundKey}
   * @param fieldTypes mapping of key name to {@link CompoundKey.TypeInfo} for that key
   * @return the stored {@link CompoundKey}
   */
  public CompoundKey getCompoundKey(Map fieldTypes)
  {
    StringBuilder sb = new StringBuilder(10);
    RecordDataSchema.Field keyField = new RecordDataSchema.Field(KeyValueRecordFactory.COMPOUND_KEY_SCHEMA);
    keyField.setName(KEY_FIELD_NAME, sb);

    DataMap compoundKeyData = obtainDirect(keyField, DataMap.class, GetMode.DEFAULT);

    if (compoundKeyData.size() != fieldTypes.size())
    {
      throw new IllegalArgumentException("Number of keys must be the same! Number of keys stored in the KeyValueRecord" +
                                             "is: " + compoundKeyData.size() + ". Number of keys in fieldTypes is: " + fieldTypes.size());
    }

    return CompoundKey.fromValues(compoundKeyData, fieldTypes);
  }

  public V getValue(Class valueClass)
  {
    return getValue(new TypeSpec(valueClass));
  }

  /**
   * Get the stored value
   * @param valueTypeSpec the expected class of the stored value
   * @return the stored value as an object of {@code valueClass}
   */
  public V getValue(TypeSpec valueTypeSpec)
  {
    StringBuilder sb = new StringBuilder(10);

    RecordDataSchema.Field valueField = new RecordDataSchema.Field(valueTypeSpec.getSchema());
    valueField.setName(VALUE_FIELD_NAME, sb);

    return obtainWrapped(valueField, valueTypeSpec.getType(), GetMode.DEFAULT);
  }
}