Package org.springframework.data.cassandra.mapping

Source Code of org.springframework.data.cassandra.mapping.BasicCassandraPersistentProperty

/*
* Copyright 2013-2014 the original author or authors
*
* 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.springframework.data.cassandra.mapping;

import static org.springframework.cassandra.core.cql.CqlIdentifier.cqlId;

import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Set;

import org.springframework.cassandra.core.Ordering;
import org.springframework.cassandra.core.PrimaryKeyType;
import org.springframework.cassandra.core.cql.CqlIdentifier;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.expression.BeanFactoryAccessor;
import org.springframework.context.expression.BeanFactoryResolver;
import org.springframework.dao.InvalidDataAccessApiUsageException;
import org.springframework.data.cassandra.util.SpelUtils;
import org.springframework.data.mapping.Association;
import org.springframework.data.mapping.PropertyHandler;
import org.springframework.data.mapping.model.AnnotationBasedPersistentProperty;
import org.springframework.data.util.ClassTypeInformation;
import org.springframework.data.util.TypeInformation;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;

import com.datastax.driver.core.DataType;

/**
* Cassandra specific {@link org.springframework.data.mapping.model.AnnotationBasedPersistentProperty} implementation.
*
* @author Alex Shvid
* @author Matthew T. Adams
*/
public class BasicCassandraPersistentProperty extends AnnotationBasedPersistentProperty<CassandraPersistentProperty>
    implements CassandraPersistentProperty, ApplicationContextAware {

  protected ApplicationContext context;
  protected StandardEvaluationContext spelContext;
  /**
   * An unmodifiable list of this property's column names.
   */
  protected List<CqlIdentifier> columnNames;
  /**
   * An unmodifiable list of this property's explicitly set column names.
   */
  protected List<CqlIdentifier> explicitColumnNames;
  /**
   * Whether this property has been explicitly instructed to force quote column names.
   */
  protected Boolean forceQuote;

  /**
   * Creates a new {@link BasicCassandraPersistentProperty}.
   *
   * @param field
   * @param propertyDescriptor
   * @param owner
   * @param simpleTypeHolder
   */
  public BasicCassandraPersistentProperty(Field field, PropertyDescriptor propertyDescriptor,
      CassandraPersistentEntity<?> owner, CassandraSimpleTypeHolder simpleTypeHolder) {

    super(field, propertyDescriptor, owner, simpleTypeHolder);

    if (owner != null && owner.getApplicationContext() != null) {
      setApplicationContext(owner.getApplicationContext());
    }
  }

  @Override
  public void setApplicationContext(ApplicationContext context) {

    Assert.notNull(context);

    this.context = context;
    spelContext = new StandardEvaluationContext();
    spelContext.addPropertyAccessor(new BeanFactoryAccessor());
    spelContext.setBeanResolver(new BeanFactoryResolver(context));
    spelContext.setRootObject(context);
  }

  @Override
  public CassandraPersistentEntity<?> getOwner() {
    return (CassandraPersistentEntity<?>) super.getOwner();
  }

  @Override
  public boolean isCompositePrimaryKey() {
    return getField().getType().isAnnotationPresent(PrimaryKeyClass.class);
  }

  public Class<?> getCompositePrimaryKeyType() {
    if (!isCompositePrimaryKey()) {
      return null;
    }

    return getField().getType();
  }

  @Override
  public TypeInformation<?> getCompositePrimaryKeyTypeInformation() {
    if (!isCompositePrimaryKey()) {
      return null;
    }

    return ClassTypeInformation.from(getCompositePrimaryKeyType());
  }

  @Override
  public CqlIdentifier getColumnName() {

    List<CqlIdentifier> columnNames = getColumnNames();
    if (columnNames.size() != 1) {
      throw new IllegalStateException("property does not have a single column mapping");
    }

    return columnNames.get(0);
  }

  @Override
  public Ordering getPrimaryKeyOrdering() {

    PrimaryKeyColumn anno = findAnnotation(PrimaryKeyColumn.class);

    return anno == null ? null : anno.ordering();
  }

  @Override
  public DataType getDataType() {

    CassandraType annotation = findAnnotation(CassandraType.class);
    if (annotation != null) {
      return getDataTypeFor(annotation);
    }

    if (isMap()) {

      List<TypeInformation<?>> args = getTypeInformation().getTypeArguments();
      ensureTypeArguments(args.size(), 2);

      return DataType.map(getDataTypeFor(args.get(0).getType()), getDataTypeFor(args.get(1).getType()));
    }

    if (isCollectionLike()) {

      List<TypeInformation<?>> args = getTypeInformation().getTypeArguments();
      ensureTypeArguments(args.size(), 1);

      if (Set.class.isAssignableFrom(getType())) {
        return DataType.set(getDataTypeFor(args.get(0).getType()));
      }
      if (List.class.isAssignableFrom(getType())) {
        return DataType.list(getDataTypeFor(args.get(0).getType()));
      }
    }

    DataType dataType = CassandraSimpleTypeHolder.getDataTypeFor(getType());
    if (dataType == null) {
      throw new InvalidDataAccessApiUsageException(
          String
              .format(
                  "unknown type for property [%s], type [%s] in entity [%s]; only primitive types and collections or maps of primitive types are allowed",
                  getName(), getType(), getOwner().getName()));
    }
    return dataType;
  }

  private DataType getDataTypeFor(CassandraType annotation) {

    DataType.Name type = annotation.type();

    if (type.isCollection()) {
      switch (type) {

        case MAP:
          ensureTypeArguments(annotation.typeArguments().length, 2);
          return DataType.map(getDataTypeFor(annotation.typeArguments()[0]),
              getDataTypeFor(annotation.typeArguments()[1]));

        case LIST:
          ensureTypeArguments(annotation.typeArguments().length, 1);
          return DataType.list(getDataTypeFor(annotation.typeArguments()[0]));

        case SET:
          ensureTypeArguments(annotation.typeArguments().length, 1);
          return DataType.set(getDataTypeFor(annotation.typeArguments()[0]));

        default:
          throw new InvalidDataAccessApiUsageException(String.format(
              "unknown multivalued DataType [%s] for property [%s] in entity [%s]", type, getType(), getOwner()
                  .getName()));
      }
    } else {

      return CassandraSimpleTypeHolder.getDataTypeFor(type);
    }
  }

  @Override
  public boolean isIndexed() {
    return isAnnotationPresent(Indexed.class);
  }

  @Override
  public boolean isPartitionKeyColumn() {

    PrimaryKeyColumn anno = findAnnotation(PrimaryKeyColumn.class);

    return anno != null && anno.type() == PrimaryKeyType.PARTITIONED;
  }

  @Override
  public boolean isClusterKeyColumn() {

    PrimaryKeyColumn anno = findAnnotation(PrimaryKeyColumn.class);

    return anno != null && anno.type() == PrimaryKeyType.CLUSTERED;
  }

  @Override
  public boolean isPrimaryKeyColumn() {
    return isAnnotationPresent(PrimaryKeyColumn.class);
  }

  protected DataType getDataTypeFor(DataType.Name typeName) {
    DataType dataType = CassandraSimpleTypeHolder.getDataTypeFor(typeName);
    if (dataType == null) {
      throw new InvalidDataAccessApiUsageException(
          "only primitive types are allowed inside collections for the property  '" + this.getName() + "' type is '"
              + this.getType() + "' in the entity " + this.getOwner().getName());
    }
    return dataType;
  }

  protected DataType getDataTypeFor(Class<?> javaType) {
    DataType dataType = CassandraSimpleTypeHolder.getDataTypeFor(javaType);
    if (dataType == null) {
      throw new InvalidDataAccessApiUsageException(
          "only primitive types are allowed inside collections for the property  '" + this.getName() + "' type is '"
              + this.getType() + "' in the entity " + this.getOwner().getName());
    }
    return dataType;
  }

  protected void ensureTypeArguments(int args, int expected) {
    if (args != expected) {
      throw new InvalidDataAccessApiUsageException("expected " + expected + " of typed arguments for the property  '"
          + this.getName() + "' type is '" + this.getType() + "' in the entity " + this.getOwner().getName());
    }
  }

  @Override
  public List<CqlIdentifier> getColumnNames() {

    if (this.columnNames != null) {
      return columnNames;
    }

    return this.columnNames = Collections.unmodifiableList(determineColumnNames());
  }

  protected List<CqlIdentifier> determineColumnNames() {

    List<CqlIdentifier> columnNames = new ArrayList<CqlIdentifier>();

    if (isCompositePrimaryKey()) { // then the id type has @PrimaryKeyClass

      addCompositePrimaryKeyColumnNames(getCompositePrimaryKeyEntity(), columnNames);
      return columnNames;
    }

    // else we're dealing with a single-column field
    String defaultName = getField().getName(); // TODO: replace with naming strategy class
    String overriddenName = null;
    boolean forceQuote = false;

    if (isIdProperty()) { // then the id is of a simple type (since it's not a composite primary key)

      PrimaryKey anno = findAnnotation(PrimaryKey.class);
      overriddenName = anno == null ? null : anno.value();
      forceQuote = anno == null ? forceQuote : anno.forceQuote();

    } else if (isPrimaryKeyColumn()) { // then it's a simple type

      PrimaryKeyColumn anno = findAnnotation(PrimaryKeyColumn.class);
      overriddenName = anno == null ? null : anno.name();
      forceQuote = anno == null ? forceQuote : anno.forceQuote();

    } else { // then it's a vanilla column with the assumption that it's mapped to a single column

      Column anno = findAnnotation(Column.class);
      overriddenName = anno == null ? null : anno.value();
      forceQuote = anno == null ? forceQuote : anno.forceQuote();

    }

    columnNames.add(createColumnName(defaultName, overriddenName, forceQuote));

    return columnNames;
  }

  protected CqlIdentifier createColumnName(String defaultName, String overriddenName, boolean forceQuote) {

    String name = defaultName;

    if (StringUtils.hasText(overriddenName)) {
      name = spelContext == null ? overriddenName : SpelUtils.evaluate(overriddenName, spelContext);
    }

    return cqlId(name, forceQuote);
  }

  protected void addCompositePrimaryKeyColumnNames(CassandraPersistentEntity<?> compositePrimaryKeyEntity,
      final List<CqlIdentifier> columnNames) {

    compositePrimaryKeyEntity.doWithProperties(new PropertyHandler<CassandraPersistentProperty>() {

      @Override
      public void doWithPersistentProperty(CassandraPersistentProperty p) {
        if (p.isCompositePrimaryKey()) {
          addCompositePrimaryKeyColumnNames(p.getCompositePrimaryKeyEntity(), columnNames);
        } else {
          columnNames.add(p.getColumnName());
        }
      }
    });
  }

  @Override
  public void setColumnName(CqlIdentifier columnName) {

    Assert.notNull(columnName);
    setColumnNames(Arrays.asList(new CqlIdentifier[] { columnName }));
  }

  @Override
  public void setColumnNames(List<CqlIdentifier> columnNames) {

    Assert.notNull(columnNames);

    // force calculation of columnNames if not yet known
    if (this.columnNames == null) {
      getColumnNames();
    }
    if (this.columnNames.size() != columnNames.size()) {
      throw new IllegalStateException(String.format(
          "property [%s] on entity [%s] is mapped to [%s] column%s, but given column name list has size [%s]",
          getName(), getOwner().getType().getName(), this.columnNames.size(), this.columnNames.size() == 1 ? "" : "s",
          columnNames.size()));
    }

    this.columnNames = this.explicitColumnNames = Collections
        .unmodifiableList(new ArrayList<CqlIdentifier>(columnNames));
  }

  @Override
  public void setForceQuote(boolean forceQuote) {

    if (this.forceQuote != null && this.forceQuote == forceQuote) {
      return;
    } else {
      this.forceQuote = forceQuote;
    }

    List<CqlIdentifier> columnNames = new ArrayList<CqlIdentifier>(this.columnNames == null ? 0
        : this.columnNames.size());
    for (CqlIdentifier columnName : getColumnNames()) {
      columnNames.add(cqlId(columnName.getUnquoted(), forceQuote));
    }

    setColumnNames(columnNames);
  }

  @Override
  public List<CassandraPersistentProperty> getCompositePrimaryKeyProperties() {

    if (!isCompositePrimaryKey()) {
      throw new IllegalStateException(String.format("[%s] does not represent a composite primary key property",
          getField()));
    }

    return getCompositePrimaryKeyEntity().getCompositePrimaryKeyProperties();
  }

  @Override
  public CassandraPersistentEntity<?> getCompositePrimaryKeyEntity() {
    CassandraMappingContext mappingContext = getOwner().getMappingContext();
    if (mappingContext == null) {
      throw new IllegalStateException("need CassandraMappingContext");
    }
    return mappingContext.getPersistentEntity(getCompositePrimaryKeyTypeInformation());
  }

  @Override
  public Association<CassandraPersistentProperty> getAssociation() {
    throw new UnsupportedOperationException("Cassandra does not support associations");
  }

  @Override
  protected Association<CassandraPersistentProperty> createAssociation() {
    return new Association<CassandraPersistentProperty>(this, null);
  }
}
TOP

Related Classes of org.springframework.data.cassandra.mapping.BasicCassandraPersistentProperty

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.