private void appendPredicateAndValue( CQLAttribute attribute, StringBuilder hql, List<java.lang.Object> parameters, CQLObject queryObject) throws QueryTranslationException { LOG.debug("Appending predicate to HQL determining object value"); // determine if the predicate is unary boolean unaryPredicate = attribute.getUnaryPredicate() != null; LOG.debug("Predicate is " + (unaryPredicate ? "not " : "") + "unary"); // append the predicate hql.append(' '); String predicateAsString = null; if (unaryPredicate) { predicateAsString = predicateValues.get(attribute.getUnaryPredicate()); } else { predicateAsString = predicateValues.get(attribute.getBinaryPredicate()); } if (!unaryPredicate) { hql.append(predicateAsString).append(' '); // add a placeholder parameter to the HQL query hql.append('?'); // convert the attribute value to the specific data type of the attribute Class<?> attributeType = null; try { attributeType = typesInformationResolver.getJavaDataType( stripGeneric(queryObject.getClassName()), attribute.getName()); } catch (TypesInformationException ex) { LOG.error("Error determining type: " + ex.getMessage(), ex); throw new QueryTranslationException(ex.getMessage(), ex); } if (attributeType == null) { throw new QueryTranslationException( "No type could be determined for attribute " + queryObject.getClassName() + "." + attribute.getName()); } LOG.debug("Determined java type to be " + attribute.getName()); java.lang.Object value = getAttributeValueObject(attributeType, attribute.getAttributeValue()); // add a positional parameter value to the list parameters.add(value); } else { // binary predicates just get appended w/o values associated with them hql.append(predicateAsString); } }
/** * Processes a CQL query attribute into HQL * * @param attribute The CQL attribute * @param hql The HQL statement fragment * @param parameters The positional parameters list * @param associationTrace The trace of associations * @param objectClassName The class name of the object to which this association belongs * @throws QueryTranslationException */ private void processAttribute( CQLAttribute attribute, StringBuilder hql, List<java.lang.Object> parameters, CQLObject queryObject, String queryObjectAlias, Stack<CQLAssociatedObject> associationStack, List<CqlDataBucket> typesProcessingList) throws QueryTranslationException { LOG.debug("Processing attribute " + queryObject.getClassName() + "." + attribute.getName()); // determine what the flavor of this attribute is DatatypeFlavor flavor = typesProcessingList.get(typesProcessingList.size() - 1).datatypeFlavor; LOG.debug("Datatype flavor is " + flavor.name()); // DSET<Ii>, (and TEL and CD) ends up as "COMPLEX_WITH_SIMPLE_CONTENT" because it's modeled as // an // association to DSET, then to Ii, which is that type. Appears to work OK. // FIXME: DSET<Ad> doesn't work because I can't get the information about the part names inside // the AD // out of the Hibernate configuration object API. Interestingly, AD by itself is fine. switch (flavor) { case STANDARD: case ENUMERATION: processStandardAttribute(attribute, hql, parameters, queryObject, queryObjectAlias); break; case COMPLEX_WITH_SIMPLE_CONTENT: processComplexAttributeWithSimpleOrMixedContent( attribute, hql, parameters, associationStack, typesProcessingList); break; case COMPLEX_WITH_MIXED_CONTENT: processComplexAttributeWithSimpleOrMixedContent( attribute, hql, parameters, associationStack, typesProcessingList); break; case COMPLEX_WITH_COLLECTION_OF_COMPLEX: if (currentlyWrappedByDset(typesProcessingList)) { processDsetOfComplexDatatypeWithCollectionOfComplexAttributesWithSimpleContent( attribute, hql, parameters, associationStack, typesProcessingList); } else { processComplexAttributeWithCollectionOfComplexAttributesWithSimpleContent( attribute, hql, parameters, associationStack, typesProcessingList); } break; case COLLECTION_OF_COMPLEX_WITH_SIMPLE_CONTENT: processComplexAttributeWithSimpleOrMixedContent( attribute, hql, parameters, associationStack, typesProcessingList); break; case COLLECTION_OF_COMPLEX_WITH_COLLECTION_OF_COMPLEX_WITH_SIMPLE_CONTENT: // gah break; } }
/** * Processes a CQL group into HQL * * @param group The CQL Group * @param hql The HQL fragment which will be edited * @param parameters The positional HQL query parameters * @param associationTrace The trace of associations * @param sourceClassName The class to which this group belongs * @throws QueryTranslationException */ private void processGroup( CQLGroup group, StringBuilder hql, List<java.lang.Object> parameters, Stack<CQLAssociatedObject> associationStack, List<CqlDataBucket> typesProcessingList, CQLObject sourceQueryObject, String sourceAlias) throws QueryTranslationException { LOG.debug("Processing group on " + sourceQueryObject.getClassName()); String logic = convertLogicalOperator(group.getLogicalOperation()); boolean mustAddLogic = false; // open the group hql.append('('); if (group.getCQLAttribute() != null) { for (int i = 0; i < group.getCQLAttribute().length; i++) { mustAddLogic = true; processAttribute( group.getCQLAttribute(i), hql, parameters, sourceQueryObject, sourceAlias, associationStack, typesProcessingList); if (i + 1 < group.getCQLAttribute().length) { hql.append(' ').append(logic).append(' '); } } } if (group.getCQLAssociatedObject() != null) { if (mustAddLogic) { hql.append(' ').append(logic).append(' '); } for (int i = 0; i < group.getCQLAssociatedObject().length; i++) { mustAddLogic = true; processAssociation( group.getCQLAssociatedObject(i), hql, parameters, associationStack, typesProcessingList, sourceQueryObject, sourceAlias); if (i + 1 < group.getCQLAssociatedObject().length) { hql.append(' ').append(logic).append(' '); } } } if (group.getCQLGroup() != null) { if (mustAddLogic) { hql.append(' ').append(logic).append(' '); } for (int i = 0; i < group.getCQLGroup().length; i++) { processGroup( group.getCQLGroup(i), hql, parameters, associationStack, typesProcessingList, sourceQueryObject, sourceAlias); if (i + 1 < group.getCQLGroup().length) { hql.append(' ').append(logic).append(' '); } } } // close the group hql.append(')'); }
/** * Processes CQL associations into HQL * * @param association The CQL association * @param hql The HQL fragment which will be edited * @param parameters The positional HQL query parameters * @param associationTrace The trace of associations * @param sourceClassName The class name of the type to which this association belongs * @throws QueryTranslationException */ private void processAssociation( CQLAssociatedObject association, StringBuilder hql, List<java.lang.Object> parameters, Stack<CQLAssociatedObject> associationStack, List<CqlDataBucket> typesProcessingList, CQLObject sourceQueryObject, String sourceAlias) throws QueryTranslationException { LOG.debug( "Processing association " + sourceQueryObject.getClassName() + " to " + association.getClassName()); // get the association's role name String roleName = association.getEndName(); if (roleName == null) { try { roleName = typesInformationResolver.getRoleName( sourceQueryObject.getClassName(), association.getClassName()); } catch (TypesInformationException ex) { throw new QueryTranslationException(ex.getMessage(), ex); } } if (roleName == null) { // still null?? no association to the object! throw new QueryTranslationException( "No role name for an association between " + sourceQueryObject.getClassName() + " and " + association.getClassName() + " cound be determined. Maybe the association doesn't exist?"); } LOG.debug("Role name determined to be " + roleName); // determine the alias for this association String alias = getAssociationAlias(sourceQueryObject.getClassName(), association.getClassName(), roleName); LOG.debug("Association alias determined to be " + alias); // add this association to the stack associationStack.push(association); DatatypeFlavor flavor = null; try { flavor = DatatypeFlavor.getFlavorOfClass(Class.forName(stripGeneric(association.getClassName()))); } catch (ClassNotFoundException ex) { throw new QueryTranslationException( "Error determining datatype flavor of " + association.getClassName() + ": " + ex.getMessage(), ex); } addTypeProcessingInformation( typesProcessingList, association.getClassName(), flavor == DatatypeFlavor.STANDARD ? alias : roleName); if (DatatypeFlavor.STANDARD.equals(flavor)) { // flag indicates the query is only verifying the association is populated boolean simpleNullCheck = true; if (association.getCQLAttribute() != null) { simpleNullCheck = false; hql.append(sourceAlias).append('.').append(roleName); hql.append(".id in (select ").append(alias).append(".id from "); hql.append(association.getClassName()).append(" as ").append(alias).append(" where "); processAttribute( association.getCQLAttribute(), hql, parameters, association, alias, associationStack, typesProcessingList); hql.append(") "); } if (association.getCQLAssociatedObject() != null) { simpleNullCheck = false; // add clause to select things from this association hql.append(sourceAlias).append('.').append(roleName); hql.append(".id in (select ").append(alias).append(".id from "); hql.append(association.getClassName()).append(" as ").append(alias).append(" where "); processAssociation( association.getCQLAssociatedObject(), hql, parameters, associationStack, typesProcessingList, association, alias); hql.append(") "); } if (association.getCQLGroup() != null) { simpleNullCheck = false; hql.append(sourceAlias).append('.').append(roleName); hql.append(".id in (select ").append(alias).append(".id from "); hql.append(association.getClassName()).append(" as ").append(alias).append(" where "); processGroup( association.getCQLGroup(), hql, parameters, associationStack, typesProcessingList, association, alias); hql.append(") "); } if (simpleNullCheck) { // query is checking for the association to exist and be non-null hql.append(sourceAlias).append('.').append(roleName).append(".id is not null "); } } else { // complex datatype association (modeled as an attribute, so saying "Person.AD is not null" // doesn't make sense... // "Person.AD.NullFlavor = NullFlavor.NI, however, is fine // FIXME: have to handle complex types here boolean simpleNullCheck = true; if (association.getCQLAssociatedObject() != null) { simpleNullCheck = false; // continue processing // TODO: does alias need to be role name? processAssociation( association.getCQLAssociatedObject(), hql, parameters, associationStack, typesProcessingList, association, alias); } if (association.getCQLGroup() != null) { simpleNullCheck = false; // continue processing // TODO: does alias need to be role name? processGroup( association.getCQLGroup(), hql, parameters, associationStack, typesProcessingList, association, alias); } if (association.getCQLAttribute() != null) { simpleNullCheck = false; // TODO: does sourceAlias need to be roleName?? processAttribute( association.getCQLAttribute(), hql, parameters, sourceQueryObject, sourceAlias, associationStack, typesProcessingList); } if (simpleNullCheck) { // checking for the type not to be null, but .id doesn't work.... // depending on the sequence of datatype flavors leading to this point // we have to construct the HQL in different ways String path = getAssociationNavigationPath(typesProcessingList, 4); if (path.startsWith("join ")) { // throw away the "where" part of the existing query if (hql.toString().endsWith("where ")) { removeLastWhereStatement(hql); } } hql.append(path).append(" is not null"); } } // pop this association off the stack associationStack.pop(); clipTypeProcessingInformation(typesProcessingList); LOG.debug(associationStack.size() + " associations remain on the stack"); }