/**
* CloudGraph Community Edition (CE) License
*
* This is a community release of CloudGraph, a dual-license suite of
* Service Data Object (SDO) 2.1 services designed for relational and
* big-table style "cloud" databases, such as HBase and others.
* This particular copy of the software is released under the
* version 2 of the GNU General Public License. CloudGraph was developed by
* TerraMeta Software, Inc.
*
* Copyright (c) 2013, TerraMeta Software, Inc. All rights reserved.
*
* General License information can be found below.
*
* This distribution may include materials developed by third
* parties. For license and attribution notices for these
* materials, please refer to the documentation that accompanies
* this distribution (see the "Licenses for Third-Party Components"
* appendix) or view the online documentation at
* <http://cloudgraph.org/licenses/>.
*/
package org.cloudgraph.store.lang;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.cloudgraph.store.service.GraphServiceException;
import org.plasma.query.collector.SelectionCollector;
import org.plasma.query.model.Where;
import org.plasma.sdo.PlasmaDataObject;
import org.plasma.sdo.PlasmaProperty;
import org.plasma.sdo.PlasmaType;
import org.plasma.sdo.access.DataAccessException;
import org.plasma.sdo.access.provider.common.PropertyPair;
import org.plasma.sdo.profile.KeyType;
import commonj.sdo.Property;
import commonj.sdo.Type;
/**
* Default assembler functionality without any shared objects related
* to graph assembly, such that it is usable by parallel tasks.
*
* @author Scott Cinnamond
* @since 0.6.2
*/
public abstract class AssemblerSupport {
private static Log log = LogFactory.getLog(AssemblerSupport.class);
protected SelectionCollector collector;
protected StatementFactory statementFactory;
protected StatementExecutor statementExecutor;
@SuppressWarnings("unused")
private AssemblerSupport() {}
public AssemblerSupport(SelectionCollector collector,
StatementFactory statementFactory, StatementExecutor statementExecutor) {
this.collector = collector;
this.statementFactory = statementFactory;
this.statementExecutor = statementExecutor;
}
public StatementFactory getStatementFactory() {
return statementFactory;
}
public StatementExecutor getStatementExecutor() {
return statementExecutor;
}
protected List<PropertyPair> getChildKeyPairs(PropertyPair pair)
{
List<PropertyPair> childKeyProps = new ArrayList<PropertyPair>();
PlasmaProperty supplier = pair.getProp().getKeySupplier();
if (supplier != null) {
PropertyPair childPair = new PropertyPair(supplier,
pair.getValue());
childPair.setValueProp(supplier);
childKeyProps.add(childPair);
}
else {
List<Property> childPkProps = ((PlasmaType)pair.getProp().getType()).findProperties(KeyType.primary);
if (childPkProps.size() == 1) {
childKeyProps.add(
new PropertyPair((PlasmaProperty)childPkProps.get(0),
pair.getValue()));
}
else
throwPriKeyError(childPkProps,
pair.getProp().getType(), pair.getProp());
}
return childKeyProps;
}
protected List<PropertyPair> getChildKeyPairs(PlasmaDataObject dataObject, PlasmaProperty prop)
{
PlasmaProperty opposite = (PlasmaProperty)prop.getOpposite();
if (opposite == null)
throw new DataAccessException("no opposite property found"
+ " - cannot map from many property, " + prop.toString());
List<PropertyPair> childKeyProps = new ArrayList<PropertyPair>();
List<Property> pkProps = ((PlasmaType)dataObject.getType()).findProperties(KeyType.primary);
if (pkProps.size() == 1) {
PlasmaProperty pkProp = (PlasmaProperty)pkProps.get(0);
Object value = dataObject.get(pkProp);
if (value != null) {
PropertyPair pair = new PropertyPair(opposite, value);
pair.setValueProp(pkProp);
childKeyProps.add(pair);
}
else
throw new GraphServiceException("no value found for key property, "
+ pkProp.toString());
}
else
throwPriKeyError(pkProps,
dataObject.getType(), prop);
return childKeyProps;
}
protected List<List<PropertyPair>> getPredicateResult(PlasmaType targetType, PlasmaProperty sourceProperty,
Set<Property> props,
List<PropertyPair> childKeyPairs) {
List<List<PropertyPair>> result = null;
Where where = this.collector.getPredicate(sourceProperty);
if (where == null) {
List<Object> params = new ArrayList<Object>();
StringBuilder query = this.statementFactory.createSelect(targetType,
props, childKeyPairs, params);
//The partition key is the first field in the primary key definition
//The absence of this partition key makes that Cassandra has to send your query
//to all nodes in the cluster, which is inefficient and therefore disabled
//by default. The 'ALLOW FILTERING' clause enables such searches,
Object[] paramArray = new Object[params.size()];
params.toArray(paramArray);
result = this.statementExecutor.fetch(targetType, query, props, paramArray);
}
else {
FilterAssembler filterAssembler = this.statementFactory.createFilterAssembler(where, targetType);
List<Object> params = new ArrayList<Object>();
StringBuilder query = this.statementFactory.createSelect(targetType,
props, childKeyPairs, filterAssembler, params);
//The partition key is the first field in the primary key definition
//The absence of this partition key makes that Cassandra has to send your query
//to all nodes in the cluster, which is inefficient and therefore disabled
//by default. The 'ALLOW FILTERING' clause enables such searches,
Object[] paramArray = new Object[params.size()];
params.toArray(paramArray);
result = this.statementExecutor.fetch(targetType, query, props, paramArray);
}
return result;
}
protected List<PropertyPair> getNextKeyPairs(PlasmaDataObject target, PropertyPair pair, int level)
{
List<PropertyPair> nextKeyPairs = new ArrayList<PropertyPair>();
List<Property> nextKeyProps = ((PlasmaType)pair.getProp().getType()).findProperties(KeyType.primary);
if (nextKeyProps.size() == 1) {
if (log.isDebugEnabled())
log.debug(String.valueOf(level) + ":found single PK for type, " + pair.getProp().getType());
PlasmaProperty next = (PlasmaProperty)nextKeyProps.get(0);
PropertyPair nextPair = new PropertyPair(next, pair.getValue());
nextKeyPairs.add(nextPair);
if (log.isDebugEnabled())
log.debug(String.valueOf(level) + ":added single PK, " + next.toString());
}
else {
PlasmaProperty opposite = (PlasmaProperty)pair.getProp().getOpposite();
if (opposite == null)
throw new DataAccessException("no opposite property found"
+ " - cannot map from singular property, "
+ pair.getProp().toString());
PlasmaProperty supplier = opposite.getKeySupplier();
if (supplier != null) {
PlasmaProperty nextProp = supplier;
PropertyPair nextPair = this.findNextKeyValue(target,
nextProp, opposite);
nextKeyPairs.add(nextPair);
}
else {
if (log.isDebugEnabled())
log.debug(String.valueOf(level) + ":found multiple PK's - throwing PK error");
this.throwPriKeyError(nextKeyProps,
pair.getProp().getType(), pair.getProp());
}
}
return nextKeyPairs;
}
protected List<PropertyPair> getChildKeyProps(PlasmaDataObject target, PlasmaType targetType, PlasmaProperty prop)
{
PlasmaProperty opposite = (PlasmaProperty)prop.getOpposite();
if (opposite == null)
throw new DataAccessException("no opposite property found"
+ " - cannot map from many property, "
+ prop.getType() + "." + prop.getName());
List<PropertyPair> childKeyProps = new ArrayList<PropertyPair>();
List<Property> nextKeyProps = ((PlasmaType)targetType).findProperties(KeyType.primary);
PlasmaProperty supplier = opposite.getKeySupplier();
if (supplier != null) {
PlasmaProperty nextProp = supplier;
PropertyPair pair = this.findNextKeyValue(target,
nextProp, opposite);
childKeyProps.add(pair);
}
else if (nextKeyProps.size() == 1) {
PlasmaProperty nextProp = (PlasmaProperty)nextKeyProps.get(0);
PropertyPair pair = this.findNextKeyValue(target,
nextProp, opposite);
childKeyProps.add(pair);
}
else {
this.throwPriKeyError(nextKeyProps,
targetType, prop);
}
return childKeyProps;
}
/**
* If the given property is a datatype property, returns a property pair
* with the given property set as the pair value property, otherwise
* traverses the data object graph via opposite property links until a
* datatype property is found, then returns the property value pair with the
* traversal end point property set as the pair value property.
*
* @param dataObject
* the data object
* @param prop
* the property
* @param opposite
* the opposite property
* @return the property value pair
*/
protected PropertyPair findNextKeyValue(PlasmaDataObject dataObject,
PlasmaProperty prop, PlasmaProperty opposite) {
PlasmaDataObject valueTarget = dataObject;
PlasmaProperty valueProp = prop;
Object value = valueTarget.get(valueProp.getName());
while (!valueProp.getType().isDataType()) {
valueTarget = (PlasmaDataObject) value;
valueProp = this.statementFactory.getOppositePriKeyProperty(valueProp);
value = valueTarget.get(valueProp.getName()); // FIXME use prop API
}
if (value != null) {
PropertyPair pair = new PropertyPair(opposite, value);
pair.setValueProp(valueProp);
return pair;
} else
throw new GraphServiceException("no value found for key property, "
+ valueProp.toString());
}
protected void throwPriKeyError(List<Property> rootPkProps, Type type,
Property prop) {
if (prop.isMany())
if (rootPkProps.size() == 0)
throw new DataAccessException("no pri-keys found for "
+ type.getURI() + "#" + type.getName()
+ " - cannot map from many property, " + prop.getType()
+ "." + prop.getName());
else
throw new DataAccessException("multiple pri-keys found for "
+ type.getURI() + "#" + type.getName()
+ " - cannot map from many property, " + prop.getType()
+ "." + prop.getName());
else if (rootPkProps.size() == 0)
throw new DataAccessException("no pri-keys found for "
+ type.getURI() + "#" + type.getName()
+ " - cannot map from singular property, " + prop.getType()
+ "." + prop.getName());
else
throw new DataAccessException("multiple pri-keys found for "
+ type.getURI() + "#" + type.getName()
+ " - cannot map from singular property, " + prop.getType()
+ "." + prop.getName());
}
}