* @param not Whether the operator is "!instanceof"
* @return Whether this expression is an instance of the provided type
*/
public BooleanExpression is(SQLExpression expr, boolean not)
{
RDBMSStoreManager storeMgr = stmt.getRDBMSManager();
ClassLoaderResolver clr = stmt.getClassLoaderResolver();
// Extract instanceOf type
String instanceofClassName = (String)((StringLiteral)expr).getValue();
Class type = null;
try
{
type = stmt.getQueryGenerator().resolveClass(instanceofClassName);
}
catch (ClassNotResolvedException cnre)
{
type = null;
}
if (type == null)
{
throw new NucleusUserException(LOCALISER_CORE.msg("037016", instanceofClassName));
}
// Extract type of member and check obvious conditions
SQLExpressionFactory exprFactory = stmt.getSQLExpressionFactory();
Class memberType = clr.classForName(mapping.getType());
if (!memberType.isAssignableFrom(type) && !type.isAssignableFrom(memberType))
{
// Member type and instanceof type are totally incompatible, so just return false
JavaTypeMapping m = exprFactory.getMappingForType(boolean.class, true);
return exprFactory.newLiteral(stmt, m, true).eq(exprFactory.newLiteral(stmt, m, not));
}
else if (memberType == type)
{
// instanceof type is the same as the member type therefore must comply (can't store supertypes)
JavaTypeMapping m = exprFactory.getMappingForType(boolean.class, true);
return exprFactory.newLiteral(stmt, m, true).eq(exprFactory.newLiteral(stmt, m, !not));
}
if (mapping instanceof EmbeddedMapping)
{
// Don't support embedded instanceof expressions
JavaTypeMapping m = exprFactory.getMappingForType(boolean.class, true);
return exprFactory.newLiteral(stmt, m, true).eq(exprFactory.newLiteral(stmt, m, not));
}
else if (mapping instanceof PersistableMapping || mapping instanceof ReferenceMapping)
{
// Field has its own table, so join to it
AbstractClassMetaData fieldCmd = storeMgr.getMetaDataManager().getMetaDataForClass(
mapping.getType(), clr);
DatastoreClass memberTable = null;
if (fieldCmd.getInheritanceMetaData().getStrategy() == InheritanceStrategy.SUBCLASS_TABLE)
{
// Field is a PC class that uses "subclass-table" inheritance strategy (and so has multiple possible tables to join to)
AbstractClassMetaData[] cmds = storeMgr.getClassesManagingTableForClass(fieldCmd, clr);
if (cmds != null)
{
// Join to the first table
// TODO Allow for all possible tables. Can we do an OR of the tables ? How ?
if (cmds.length > 1)
{
NucleusLogger.QUERY.warn(LOCALISER_CORE.msg("037006",
mapping.getMemberMetaData().getFullFieldName(), cmds[0].getFullClassName()));
}
memberTable = storeMgr.getDatastoreClass(cmds[0].getFullClassName(), clr);
}
else
{
// No subclasses with tables to join to, so throw a user error
throw new NucleusUserException(LOCALISER_CORE.msg("037005",
mapping.getMemberMetaData().getFullFieldName()));
}
}
else
{
// Class of the field will have its own table
memberTable = storeMgr.getDatastoreClass(mapping.getType(), clr);
}
DiscriminatorMetaData dismd = memberTable.getDiscriminatorMetaData();
DiscriminatorMapping discMapping = (DiscriminatorMapping)memberTable.getDiscriminatorMapping(false);
if (discMapping != null)
{
SQLTable targetSqlTbl = null;
if (mapping.getDatastoreContainer() != memberTable)
{
// FK is on source table so inner join to target table (holding the discriminator)
targetSqlTbl = stmt.getTable(memberTable, null);
if (targetSqlTbl == null)
{
targetSqlTbl = stmt.innerJoin(getSQLTable(), mapping, memberTable, null, memberTable.getIdMapping(),
null, null);
}
}
else
{
// FK is on target side and already joined
targetSqlTbl = SQLStatementHelper.getSQLTableForMappingOfTable(stmt, getSQLTable(), discMapping);
}
// Add restrict to discriminator for the instanceOf type and subclasses
SQLTable discSqlTbl = targetSqlTbl;
BooleanExpression discExpr =
SQLStatementHelper.getExpressionForDiscriminatorForClass(stmt, type.getName(),
dismd, discMapping, discSqlTbl, clr);
Iterator subclassIter = storeMgr.getSubClassesForClass(type.getName(), true, clr).iterator();
boolean hasSubclass = false;
while (subclassIter.hasNext())
{
String subclassName = (String)subclassIter.next();
BooleanExpression discExprSub =
SQLStatementHelper.getExpressionForDiscriminatorForClass(stmt, subclassName,
dismd, discMapping, discSqlTbl, clr);
discExpr = discExpr.ior(discExprSub);
hasSubclass = true;
}
if (hasSubclass)
{
discExpr.encloseInParentheses();
}
return (not ? discExpr.not() : discExpr);
}
else
{
// Join to relevant table
DatastoreClass table = null;
if (fieldCmd.getInheritanceMetaData().getStrategy() == InheritanceStrategy.SUBCLASS_TABLE)
{
// Field is a PC class that uses "subclass-table" inheritance strategy (and so has multiple possible tables to join to)
AbstractClassMetaData[] cmds = storeMgr.getClassesManagingTableForClass(fieldCmd, clr);
if (cmds != null)
{
// Join to the first table
// TODO Allow for all possible tables. Can we do an OR of the tables ? How ?
if (cmds.length > 1)
{
NucleusLogger.QUERY.warn(LOCALISER_CORE.msg("037006",
mapping.getMemberMetaData().getFullFieldName(), cmds[0].getFullClassName()));
}
table = storeMgr.getDatastoreClass(cmds[0].getFullClassName(), clr);
}
else
{
// No subclasses with tables to join to, so throw a user error
throw new NucleusUserException(LOCALISER_CORE.msg("037005",
mapping.getMemberMetaData().getFullFieldName()));
}
}
else
{
// Class of the field will have its own table
table = storeMgr.getDatastoreClass(mapping.getType(), clr);
}
if (table.managesClass(type.getName()))
{
// This type is managed in this table so must be an instance
JavaTypeMapping m = exprFactory.getMappingForType(boolean.class, true);
return exprFactory.newLiteral(stmt, m, true).eq(
exprFactory.newLiteral(stmt, m, !not));
}
else
{
if (table == stmt.getPrimaryTable().getTable())
{
// Candidate is used in instanceof, and union statement so restrict some unions
// Note that this is only really valid is wanting "a instanceof SUB1".
// It fails when we want to do "a instanceof SUB1 || a instanceof SUB2"
// TODO How do we handle those cases?
JavaTypeMapping m = exprFactory.getMappingForType(boolean.class, true);
if (stmt.getNumberOfUnions() > 0)
{
Class mainCandidateCls = clr.classForName(stmt.getCandidateClassName());
if (type.isAssignableFrom(mainCandidateCls) == not)
{
SQLExpression unionClauseExpr = exprFactory.newLiteral(stmt, m, true).eq(
exprFactory.newLiteral(stmt, m, false));
stmt.whereAnd((BooleanExpression)unionClauseExpr, false);
}
List<SQLStatement> unionStmts = stmt.getUnions();
Iterator<SQLStatement> iter = unionStmts.iterator();
while (iter.hasNext())
{
SQLStatement unionStmt = iter.next();
Class unionCandidateCls = clr.classForName(unionStmt.getCandidateClassName());
if (type.isAssignableFrom(unionCandidateCls) == not)
{
SQLExpression unionClauseExpr = exprFactory.newLiteral(unionStmt, m, true).eq(
exprFactory.newLiteral(unionStmt, m, false));
unionStmt.whereAnd((BooleanExpression)unionClauseExpr, false);
}
}
// Just return true since we applied the condition direct to the unions
SQLExpression returnExpr = exprFactory.newLiteral(stmt, m, true).eq(
exprFactory.newLiteral(stmt, m, true));
return (BooleanExpression)returnExpr;
}
else
{
Class mainCandidateCls = clr.classForName(stmt.getCandidateClassName());
if (!type.isAssignableFrom(mainCandidateCls))
{
return exprFactory.newLiteral(stmt, m, true).eq(
exprFactory.newLiteral(stmt, m, not));
}
else
{
return exprFactory.newLiteral(stmt, m, true).eq(
exprFactory.newLiteral(stmt, m, !not));
}
}
}
else
{
// Do inner join to this table to impose the instanceOf
DatastoreClass instanceofTable = storeMgr.getDatastoreClass(type.getName(), clr);
if (stmt.getNumberOfUnions() > 0)
{
// Inner join will likely not give the right result
NucleusLogger.QUERY.debug("InstanceOf for " + table +
" but no discriminator so adding inner join to " + instanceofTable +