blob: 05e3473bf6b3dd69ff2562f2996a5b90b2a02214 [file] [log] [blame]
/*
* 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.ignite.cache.store.cassandra.persistence;
import java.io.Serializable;
import java.lang.annotation.Annotation;
import java.util.List;
import com.datastax.driver.core.DataType;
import com.datastax.driver.core.Row;
import org.apache.ignite.cache.query.annotations.QuerySqlField;
import org.apache.ignite.cache.store.cassandra.common.PropertyMappingHelper;
import org.apache.ignite.cache.store.cassandra.serializer.Serializer;
import org.w3c.dom.Element;
/**
* Descriptor for particular field in a POJO object, specifying how this field
* should be written to or loaded from Cassandra.
*/
public abstract class PojoField implements Serializable {
/** Name attribute of XML element describing Pojo field. */
private static final String NAME_ATTR = "name";
/** Column attribute of XML element describing Pojo field. */
private static final String COLUMN_ATTR = "column";
/** Field name. */
private String name;
/** Field column name in Cassandra table. */
private String col;
/** Field column DDL. */
private String colDDL;
/** Indicator for calculated field. */
private Boolean calculated;
/** Field property accessor. */
private transient PojoFieldAccessor accessor;
/**
* Checks if list contains POJO field with the specified name.
*
* @param fields list of POJO fields.
* @param fieldName field name.
* @return true if list contains field or false otherwise.
*/
public static boolean containsField(List<? extends PojoField> fields, String fieldName) {
if (fields == null || fields.isEmpty())
return false;
for (PojoField field : fields) {
if (field.getName().equals(fieldName))
return true;
}
return false;
}
/**
* Creates instance of {@link PojoField} based on it's description in XML element.
*
* @param el XML element describing Pojo field
* @param pojoCls Pojo java class.
*/
public PojoField(Element el, Class<?> pojoCls) {
if (el == null)
throw new IllegalArgumentException("DOM element representing POJO field object can't be null");
if (!el.hasAttribute(NAME_ATTR)) {
throw new IllegalArgumentException("DOM element representing POJO field object should have '"
+ NAME_ATTR + "' attribute");
}
this.name = el.getAttribute(NAME_ATTR).trim();
this.col = el.hasAttribute(COLUMN_ATTR) ? el.getAttribute(COLUMN_ATTR).trim() : name.toLowerCase();
init(PropertyMappingHelper.getPojoFieldAccessor(pojoCls, name));
}
/**
* Creates instance of {@link PojoField} from its field accessor.
*
* @param accessor field accessor.
*/
public PojoField(PojoFieldAccessor accessor) {
this.name = accessor.getName();
QuerySqlField sqlField = (QuerySqlField)accessor.getAnnotation(QuerySqlField.class);
col = sqlField != null && sqlField.name() != null && !sqlField.name().isEmpty() ?
sqlField.name() : name.toLowerCase();
init(accessor);
}
/**
* Creates instance of {@link PojoField} from the other instance
* and java class.
*/
public PojoField(PojoField field, Class<?> pojoCls) {
this.name = field.name;
this.col = field.col;
this.colDDL = field.colDDL;
init(PropertyMappingHelper.getPojoFieldAccessor(pojoCls, name));
}
/**
* @return field name.
*/
public String getName() {
return name;
}
/**
* Returns java class of the field.
*
* @return Java class.
*/
public Class getJavaClass() {
return accessor.getFieldType();
}
/**
* @return Cassandra table column name.
*/
public String getColumn() {
return col;
}
/**
* @return Cassandra table column DDL statement.
*/
public String getColumnDDL() {
return colDDL;
}
/**
* Indicates if it's a calculated field - field which value just generated based on other field values.
* Such field will be stored in Cassandra as all other POJO fields, but it's value shouldn't be read from
* Cassandra - cause it's again just generated based on other field values. One of the good applications of such
* kind of fields - Cassandra materialized views build on top of other tables.
*
* @return {@code true} if it's auto generated field, {@code false} if not.
*/
public boolean calculatedField() {
if (calculated != null)
return calculated;
return calculated = accessor.isReadOnly();
}
/**
* Gets field value as an object having Cassandra compatible type.
* This it could be stored directly into Cassandra without any conversions.
*
* @param obj Object instance.
* @param serializer {@link org.apache.ignite.cache.store.cassandra.serializer.Serializer} to use.
* @return Object to store in Cassandra table column.
*/
public Object getValueFromObject(Object obj, Serializer serializer) {
Object val = accessor.getValue(obj);
if (val == null)
return null;
DataType.Name cassandraType = PropertyMappingHelper.getCassandraType(val.getClass());
if (cassandraType != null)
return val;
if (serializer == null) {
throw new IllegalStateException("Can't serialize value from object '" +
val.getClass().getName() + "' field '" + name + "', cause there is no BLOB serializer specified");
}
return serializer.serialize(val);
}
/**
* Returns POJO field annotation.
*
* @return annotation.
*/
public Annotation getAnnotation(Class clazz) {
return accessor.getAnnotation(clazz);
}
/**
* Sets object field value from a {@link com.datastax.driver.core.Row} returned by Cassandra CQL statement.
*
* @param row {@link com.datastax.driver.core.Row}
* @param obj object which field should be populated from {@link com.datastax.driver.core.Row}
* @param serializer {@link org.apache.ignite.cache.store.cassandra.serializer.Serializer} to use.
*/
public void setValueFromRow(Row row, Object obj, Serializer serializer) {
if (calculatedField())
return;
Object val = PropertyMappingHelper.getCassandraColumnValue(row, col, accessor.getFieldType(), serializer);
accessor.setValue(obj, val);
}
/**
* Initializes field info from property descriptor.
*
* @param accessor {@link PojoFieldAccessor} accessor.
*/
private void init(PojoFieldAccessor accessor) {
DataType.Name cassandraType = PropertyMappingHelper.getCassandraType(accessor.getFieldType());
cassandraType = cassandraType == null ? DataType.Name.BLOB : cassandraType;
this.colDDL = "\"" + col + "\" " + cassandraType.toString();
this.accessor = accessor;
}
}