com.github.helenusdriver.driver.impl.RootClassInfoImpl Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of impl Show documentation
Show all versions of impl Show documentation
JPA-like syntax for annotating POJO classes for persistence via Cassandra's Java driver - Implementation
The newest version!
/*
* Copyright (C) 2015-2015 The Helenus Driver Project 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 com.github.helenusdriver.driver.impl;
import java.lang.reflect.Array;
import java.lang.reflect.Modifier;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import com.datastax.driver.core.ColumnDefinitions;
import com.datastax.driver.core.Row;
import com.github.helenusdriver.driver.ObjectConversionException;
import com.github.helenusdriver.persistence.RootEntity;
/**
* The RootClassInfoImpl
class provides information about a
* particular root element POJO class.
*
* @copyright 2015-2015 The Helenus Driver Project Authors
*
* @author The Helenus Driver Project Authors
* @version 1 - Jan 19, 2015 - paouelle - Creation
*
* @param The type of POJO represented by this root class
*
* @since 2.0
*/
@lombok.ToString(callSuper=true, of={"ntypes"})
@lombok.EqualsAndHashCode(callSuper=true)
public class RootClassInfoImpl extends ClassInfoImpl {
/**
* Finds the class info for all type classes for this root element class.
*
* @author paouelle
*
* @param The type of POJO represented by this root element class
*
* @param mgr the non-null
statement manager
* @param clazz the root POJO class
* @return the non-null
map of class info for all types
* @throws NullPointerException if clazz
is null
* @throws IllegalArgumentException if any of the type classes are invalid
*/
private static Map, TypeClassInfoImpl extends T>> findTypeInfos(
StatementManagerImpl mgr, Class clazz
) {
org.apache.commons.lang3.Validate.notNull(clazz, "invalid null root POJO class");
final RootEntity re = clazz.getAnnotation(RootEntity.class);
final int hsize = re.types().length * 3 / 2;
final Map, TypeClassInfoImpl extends T>> types
= new HashMap<>(hsize);
final Set names = new HashSet<>(hsize);
for (final Class> type: re.types()) {
org.apache.commons.lang3.Validate.isTrue(
clazz.isAssignableFrom(type),
"type class '%s' must extends root element class: %s",
type.getName(), clazz.getName()
);
@SuppressWarnings("unchecked") // tested above!
final TypeClassInfoImpl extends T> tcinfo
= new TypeClassInfoImpl<>(mgr, clazz, (Class extends T>)type);
org.apache.commons.lang3.Validate.isTrue(
types.put(tcinfo.getObjectClass(), tcinfo) == null,
"duplicate type element class '%s' defined for root element class '%s'",
type.getSimpleName(), clazz.getSimpleName()
);
org.apache.commons.lang3.Validate.isTrue(
names.add(tcinfo.getType()),
"duplicate type name '%s' defined by class '%s' for root element class '%s'",
tcinfo.getType(), type.getSimpleName(), clazz.getSimpleName()
);
}
return types;
}
/**
* The Context
class extends the {@link ClassInfoImpl.Context}.
*
* @copyright 2015-2015 The Helenus Driver Project Authors
*
* @author The Helenus Driver Project Authors
* @version 1 - Jan 19, 2015 - paouelle - Creation
*
* @since 2.0
*/
public class Context extends ClassInfoImpl.Context {
/**
* Holds the contexts for all defined types.
*
* @author paouelle
*/
private final Map, TypeClassInfoImpl extends T>.Context> contexts;
/**
* Instantiates a new Context
object.
*
* @author paouelle
*/
@SuppressWarnings("synthetic-access")
Context() {
this.contexts = ctypes.values().stream()
.collect(
Collectors.toMap(
tcinfo -> tcinfo.getObjectClass(),
tcinfo -> tcinfo.newContext()
)
);
}
/**
* {@inheritDoc}
*
* @author paouelle
*
* @see com.github.helenusdriver.driver.impl.ClassInfoImpl.Context#getInitialObjects()
*/
@Override
@SuppressWarnings("unchecked")
public T[] getInitialObjects() {
return contexts.values().stream()
.flatMap(
tc -> {
final T[] ts = tc.getInitialObjects();
return (ts != null) ? Arrays.stream(ts) : Stream.empty();
}
).toArray(
sz -> (T[])Array.newInstance(clazz, sz)
);
}
/**
* {@inheritDoc}
*
* @author paouelle
*
* @see com.github.helenusdriver.driver.impl.ClassInfoImpl.Context#addSuffix(java.lang.String, java.lang.Object)
*/
@Override
public void addSuffix(String suffix, Object value) {
super.addSuffix(suffix, value);
contexts.values().forEach(
tc -> tc.addSuffix(suffix, value)
);
}
}
/**
* The POJOContext
class extends the
* {@link ClassInfoImpl.POJOContext}.
*
* @copyright 2015-2015 The Helenus Driver Project Authors
*
* @author The Helenus Driver Project Authors
* @version 1 - Jan 19, 2015 - paouelle - Creation
*
* @since 2.0
*/
public class POJOContext extends ClassInfoImpl.POJOContext {
/**
* Holds the corresponding type context for the POJO.
*
* @author paouelle
*/
private final TypeClassInfoImpl extends T>.POJOContext tcontext;
/**
* Instantiates a new POJORootContext
object.
*
* @author paouelle
*
* @param object the POJO object
* @throws NullPointerException if object
is null
* @throws IllegalArgumentException if object
is not of the
* appropriate class
*/
@SuppressWarnings("synthetic-access")
public POJOContext(T object) {
super(object);
final TypeClassInfoImpl extends T> tcinfo = ctypes.get(object.getClass());
org.apache.commons.lang3.Validate.isTrue(
tcinfo != null,
"invalid POJO class '%s'; expecting one of %s",
object.getClass().getName(), ctypes.keySet()
);
this.tcontext = tcinfo.newContextFromRoot(object);
}
/**
* {@inheritDoc}
*
* @author paouelle
*
* @see com.github.helenusdriver.driver.impl.ClassInfoImpl.POJOContext#getColumnValues(java.lang.String)
*/
@Override
public Map getColumnValues(String tname) {
return tcontext.getColumnValues(tname);
}
/**
* {@inheritDoc}
*
* @author paouelle
*
* @see com.github.helenusdriver.driver.impl.ClassInfoImpl.POJOContext#getPartitionKeyColumnValues(java.lang.String)
*/
@Override
public Map getPartitionKeyColumnValues(String tname) {
return tcontext.getPartitionKeyColumnValues(tname);
}
/**
* {@inheritDoc}
*
* @author paouelle
*
* @see com.github.helenusdriver.driver.impl.ClassInfoImpl.POJOContext#getSuffixAndPartitionKeyColumnValues(java.lang.String)
*/
@Override
public Map getSuffixAndPartitionKeyColumnValues(String tname) {
return tcontext.getSuffixAndPartitionKeyColumnValues(tname);
}
/**
* {@inheritDoc}
*
* @author paouelle
*
* @see com.github.helenusdriver.driver.impl.ClassInfoImpl.POJOContext#getPrimaryKeyColumnValues(java.lang.String)
*/
@Override
public Map getPrimaryKeyColumnValues(String tname) {
return tcontext.getPrimaryKeyColumnValues(tname);
}
/**
* {@inheritDoc}
*
* @author paouelle
*
* @see com.github.helenusdriver.driver.impl.ClassInfoImpl.POJOContext#getSuffixKeyValues()
*/
@Override
public Map getSuffixKeyValues() {
return tcontext.getSuffixKeyValues();
}
/**
* {@inheritDoc}
*
* @author paouelle
*
* @see com.github.helenusdriver.driver.impl.ClassInfoImpl.POJOContext#getSuffixAndPrimaryKeyColumnValues(java.lang.String)
*/
@Override
public Map getSuffixAndPrimaryKeyColumnValues(String tname) {
return tcontext.getSuffixAndPrimaryKeyColumnValues(tname);
}
/**
* {@inheritDoc}
*
* @author paouelle
*
* @see com.github.helenusdriver.driver.impl.ClassInfoImpl.POJOContext#getMandatoryAndPrimaryKeyColumnValues(java.lang.String)
*/
@Override
public Map getMandatoryAndPrimaryKeyColumnValues(
String tname
) {
return tcontext.getMandatoryAndPrimaryKeyColumnValues(tname);
}
/**
* {@inheritDoc}
*
* @author paouelle
*
* @see com.github.helenusdriver.driver.impl.ClassInfoImpl.POJOContext#getNonPrimaryKeyColumnValues(java.lang.String)
*/
@Override
public Map getNonPrimaryKeyColumnValues(String tname) {
return tcontext.getNonPrimaryKeyColumnValues(tname);
}
/**
* {@inheritDoc}
*
* @author paouelle
*
* @see com.github.helenusdriver.driver.impl.ClassInfoImpl.POJOContext#getColumnValue(java.lang.String, java.lang.CharSequence)
*/
@Override
public Object getColumnValue(String tname, CharSequence name) {
return tcontext.getColumnValue(tname, name);
}
/**
* {@inheritDoc}
*
* @author paouelle
*
* @see com.github.helenusdriver.driver.impl.ClassInfoImpl.POJOContext#getColumnValues(java.lang.String, java.lang.Iterable)
*/
@Override
public Map getColumnValues(
String tname, Iterable names
) {
return tcontext.getColumnValues(tname, names);
}
/**
* {@inheritDoc}
*
* @author paouelle
*
* @see com.github.helenusdriver.driver.impl.ClassInfoImpl.POJOContext#getColumnValues(java.lang.String, java.lang.CharSequence[])
*/
@Override
public Map getColumnValues(
String tname, CharSequence... names
) {
return tcontext.getColumnValues(tname, names);
}
/**
* {@inheritDoc}
*
* @author paouelle
*
* @see com.github.helenusdriver.driver.impl.ClassInfoImpl.Context#getKeyspace()
*/
@Override
public String getKeyspace() {
return tcontext.getKeyspace();
}
/**
* {@inheritDoc}
*
* @author paouelle
*
* @see com.github.helenusdriver.driver.impl.ClassInfoImpl.Context#addSuffix(java.lang.String, java.lang.Object)
*/
@Override
public void addSuffix(String suffix, Object value) {
tcontext.addSuffix(suffix, value);
}
/**
* {@inheritDoc}
*
* @author paouelle
*
* @see com.github.helenusdriver.driver.impl.ClassInfoImpl.Context#getObject(com.datastax.driver.core.Row)
*/
@Override
public T getObject(Row row) {
return tcontext.getObject(row);
}
/**
* {@inheritDoc}
*
* @author paouelle
*
* @see com.github.helenusdriver.driver.impl.ClassInfoImpl.Context#getInitialObjects()
*/
@Override
public T[] getInitialObjects() {
return tcontext.getInitialObjects();
}
}
/**
* Holds all the type element subclasses for this root element base class
* keyed by their type class.
*
* @author paouelle
*/
private final Map, TypeClassInfoImpl extends T>> ctypes;
/**
* Holds all the type element subclasses for this root element base class
* keyed by their type names.
*
* @author paouelle
*/
private final Map> ntypes;
/**
* Instantiates a new RootClassInfoImpl
object.
*
* @author paouelle
*
* @param mgr the non-null
statement manager
* @param clazz the root class of POJO for which to get a class info object for
* @param types the non-null
map of all type class infos
* @throws NullPointerException if clazz
is null
* @throws IllegalArgumentException if clazz
or any of its type
* classes don't represent valid POJO classes
*/
private RootClassInfoImpl(
StatementManagerImpl mgr,
Class clazz,
Map, TypeClassInfoImpl extends T>> types
) {
super(mgr, clazz, RootEntity.class);
// first make sure the class is abstract
org.apache.commons.lang3.Validate.isTrue(
Modifier.isAbstract(clazz.getModifiers()),
"root entity class '%s', must be abstract", clazz.getSimpleName()
);
this.ctypes = types;
this.ntypes = types.values().stream()
.collect(
Collectors.toMap(
tcinfo -> tcinfo.getType(),
tcinfo -> tcinfo
)
);
validateAndComplementSchema();
}
/**
* Instantiates a new RootClassInfoImpl
object.
*
* @author paouelle
*
* @param mgr the non-null
statement manager
* @param clazz the root class of POJO for which to get a class info object for
* @throws NullPointerException if clazz
is null
* @throws IllegalArgumentException if clazz
or any of its type
* classes don't represent valid POJO classes
*/
RootClassInfoImpl(StatementManagerImpl mgr, Class clazz) {
this(mgr, clazz, RootClassInfoImpl.findTypeInfos(mgr, clazz));
}
/**
* Validates this root entity class and complement its schema with the
* additional columns defined by the type entities.
*
* @author paouelle
*
* @throws IllegalArgumentException if the POJO class is improperly annotated
*/
private void validateAndComplementSchema() {
// check all tables
tables().forEach(
t -> {
// check type key
org.apache.commons.lang3.Validate.isTrue(
t.getTypeKey().isPresent(),
"%s must annotate one field as a type key for table '%s'",
clazz.getSimpleName(), t.getName()
);
ctypes.values().forEach(
tcinfo -> {
tcinfo.getTableImpl(t.getName()).getNonPrimaryKeys().stream()
.forEach(
c -> t.addNonPrimaryColumn(c)
);
}
);
}
);
}
/**
* {@inheritDoc}
*
* @author paouelle
*
* @see com.github.helenusdriver.driver.impl.ClassInfoImpl#objectClasses()
*/
@Override
public Stream> objectClasses() {
return Stream.concat(
Stream.of(clazz), ctypes.values().stream().map(t -> t.getObjectClass())
);
}
/**
* {@inheritDoc}
*
* @author paouelle
*
* @see com.github.helenusdriver.driver.impl.ClassInfoImpl#classInfos()
*/
@Override
public Stream> classInfos() {
return Stream.concat(
Stream.of(this), ctypes.values().stream()
);
}
/**
* {@inheritDoc}
*
* @author paouelle
*
* @see com.github.helenusdriver.driver.impl.ClassInfoImpl#newContext()
*/
@Override
public Context newContext() {
return new Context();
}
/**
* {@inheritDoc}
*
* @author paouelle
*
* @see com.github.helenusdriver.driver.impl.ClassInfoImpl#newContext(java.lang.Object)
*/
@Override
@SuppressWarnings({"unchecked", "rawtypes"})
public POJOContext newContext(T object) {
return new POJOContext(object);
}
/**
* Gets the class info for the specified type entity defined from this root
* entity.
*
* @author paouelle
*
* @param the type of POJO for the type entity
*
* @param clazz the POJO class of the type entity to retrieve its info
* @return the corresponding type entity POJO class information or null
* if none defined for the given class
*/
@SuppressWarnings("unchecked")
public TypeClassInfoImpl getType(Class clazz) {
return (TypeClassInfoImpl)ctypes.get(clazz);
}
/**
* Gets the class info for the specified type entity defined from this root
* entity.
*
* @author paouelle
*
* @param name the name of the type entity to retrieve its info
* @return the corresponding type entity POJO class information or null
* if none defined for the given name
*/
public TypeClassInfoImpl extends T> getType(String name) {
return ntypes.get(name);
}
/**
* {@inheritDoc}
*
* @author paouelle
*
* @see com.github.helenusdriver.driver.impl.ClassInfoImpl#getObject(com.datastax.driver.core.Row, java.util.Map)
*/
@Override
public T getObject(Row row, Map suffixes) {
if (row == null) {
return null;
}
// extract the type so we know which object we are creating
for (final ColumnDefinitions.Definition coldef: row.getColumnDefinitions()) {
// find the table for this column
final TableInfoImpl table = (TableInfoImpl)getTable(coldef.getTable());
if (table != null) {
// find the field in the table for this column
final FieldInfoImpl field = table.getColumn(coldef.getName());
if ((field != null) && field.isTypeKey()) { // get the POJO type
final String type = Objects.toString(field.decodeValue(row), null);
final TypeClassInfoImpl extends T> tcinfo = ntypes.get(type);
if (tcinfo == null) {
throw new ObjectConversionException(
clazz, row, "unknown POJO type: " + type
);
}
return tcinfo.getObject(row, type, suffixes);
}
}
}
throw new ObjectConversionException(clazz, row, "missing POJO type column");
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy