diff --git a/com.epistimis.uddl/src/com/epistimis/uddl/QueryProcessor.java b/com.epistimis.uddl/src/com/epistimis/uddl/QueryProcessor.java index 455a0b3..eb57559 100644 --- a/com.epistimis.uddl/src/com/epistimis/uddl/QueryProcessor.java +++ b/com.epistimis.uddl/src/com/epistimis/uddl/QueryProcessor.java @@ -9,6 +9,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Set; import org.apache.log4j.Logger; import org.eclipse.emf.common.util.EList; @@ -68,19 +69,7 @@ */ // TODO: Insert references to EntityProcessor -public abstract class QueryProcessor> { +public abstract class QueryProcessor> { // @Inject // private Provider resourceSetProvider; // @@ -105,7 +94,7 @@ public abstract class QueryProcessor matchQuerytoUDDL(Query q, QueryStatement qstmt) IteratorExtensions.toIterable(qstmt.eAllContents()), SelectedEntity.class); for (SelectedEntity se : selectedEntities) { - Pair ea = getEntityAndAlias(se); + Pair ea = getEntityAndAlias(se); String entityName = ea.getFirst(); String alias = ea.getSecond(); - + /** * TODO: Gets a scope that is for this container hierarchy only. For imports, * need to use IndexUtilities to get all visible IEObjectDescriptions and filter * those. That will require RQNs processing. * - * NOTE: The unmodifiableLists below are to address an NPE I was getting when attempting - * to get a value from globalDecs. + * NOTE: The unmodifiableLists below are to address an NPE I was getting when + * attempting to get a value from globalDecs. */ IScope searchScope = entityScope(q.eContainer()); List listOfDescriptions = Collections.unmodifiableList(IterableExtensions @@ -450,11 +440,7 @@ public Map matchQuerytoUDDL(Query q, QueryStatement qstmt) } break; default: - /** found multiple - so print out their names */ - String nameCollisionsMsg = ndxUtil.printIEObjectDescriptionNameCollisions(queryFQN, - getQueryType().getName(), globalDescs); - potentialExcp.addSuppressed(new NameCollisionException(nameCollisionsMsg)); - // logger.info(nameCollisionsMsg); + handleMultipleIEObjectDescriptions(globalDescs, chosenEntities, potentialExcp, queryFQN, alias); break; } } @@ -466,11 +452,7 @@ public Map matchQuerytoUDDL(Query q, QueryStatement qstmt) } break; default: - /** found multiple - so print out their names */ - String nameCollisionsMsg = ndxUtil.printIEObjectDescriptionNameCollisions(queryFQN, - getQueryType().getName(), listOfDescriptions); - potentialExcp.addSuppressed(new NameCollisionException(nameCollisionsMsg)); - // logger.info(nameCollisionsMsg); + handleMultipleIEObjectDescriptions(listOfDescriptions, chosenEntities, potentialExcp, queryFQN, alias); break; } } @@ -482,6 +464,34 @@ public Map matchQuerytoUDDL(Query q, QueryStatement qstmt) return chosenEntities; } + /** + * Common function for handling multipleIEObjectDescriptions, no matter how we got them + * @param descriptions + * @param chosenEntities + * @param potentialExcp + * @param queryFQN + * @param alias + */ + private void handleMultipleIEObjectDescriptions(List descriptions, + Map chosenEntities, QueryMatchException potentialExcp, String queryFQN, String alias) { + /** + * found multiple - first we need to reduce this to a set of EObjects because we + * might have multiple descriptions for the same EObject + */ + Set objSet = IndexUtilities.uniqueObjectsFromDescriptions(descriptions); + if (objSet.size() == 1) { + // There's really only 1, so use it + addChosenEntity(objSet.iterator().next(), alias, chosenEntities); + } else { + /* so print out their names */ + String nameCollisionsMsg = ndxUtil.printEObjectNameCollisions(queryFQN, getQueryType().getName(), objSet); + potentialExcp.addSuppressed(new NameCollisionException(nameCollisionsMsg)); + // logger.info(nameCollisionsMsg); + + } + + } + @SuppressWarnings("unchecked") private void addChosenEntity(EObject obj, String key, Map chosenEntities) { Class realType = getEntityType(); @@ -507,7 +517,7 @@ private void addChosenEntity(EObject obj, String key, Map chosen * @returns A map of (rolename, characteristic) */ public Map selectCharacteristicsFromUDDL(Query q, QueryStatement qstmt, - Map matchedEntities) { + Map matchedEntities) { // Initially, we just get the Entity names Map selectedCharacteristics = new HashMap(); @@ -534,16 +544,16 @@ public Map selectCharacteristicsFromUDDL(Query q, QueryS } else { // pce must be an ExplicitSelectedEntityCharacteristicReference ExplicitSelectedEntityCharacteristicReference esecr = (ExplicitSelectedEntityCharacteristicReference) pce; - Pair cRoleAndAlias = getCharacteristicRolenameAndAlias(esecr); - + Pair cRoleAndAlias = getCharacteristicRolenameAndAlias(esecr); + SelectedEntityCharacteristicReference secr = esecr.getSelectedEntityCharacteristicReference(); String roleNameOrAlias = cRoleAndAlias.getSecond(); - - Characteristic result = getSelectedEntityCharacteristic(secr,roleNameOrAlias, matchedEntities); - + + Characteristic result = getSelectedEntityCharacteristic(secr, roleNameOrAlias, matchedEntities); + selectedCharacteristics.put(roleNameOrAlias, result); - // QueryIdentifier se = (QueryIdentifier) .getSelectedEntity(); + // QueryIdentifier se = (QueryIdentifier) .getSelectedEntity(); // if (se == null) { // // If no selected entity, that means the characteristic only exists in one of // // the entities specified @@ -577,14 +587,15 @@ public Map selectCharacteristicsFromUDDL(Query q, QueryS return selectedCharacteristics; } - + /** - * Extract the entity name - and the value for the alias/ lookup key. The alias will be the entity name unless another value is provided + * Extract the entity name - and the value for the alias/ lookup key. The alias + * will be the entity name unless another value is provided * * @param se * @return first is the entity name, second is the value for the alias */ - private Pair getEntityAndAlias(SelectedEntity se) { + private Pair getEntityAndAlias(SelectedEntity se) { QueryIdentifier qid = (QueryIdentifier) se.getEntityType(); String entityName = qid.getId(); String alias = entityName; @@ -592,31 +603,36 @@ private Pair getEntityAndAlias(SelectedEntity se) { if (sea != null && (sea.getId().trim().length() > 0)) { alias = sea.getId(); } - return new Pair(entityName, alias); + return new Pair(entityName, alias); } /** - * Extract the rolename - and the value for the alias/ lookup key. The alias will be the rolename unless another value is provided + * Extract the rolename - and the value for the alias/ lookup key. The alias + * will be the rolename unless another value is provided + * * @param esecr * @return first is the rolename, second is the value for the alias */ - private Pair getCharacteristicRolenameAndAlias(ExplicitSelectedEntityCharacteristicReference esecr) { + private Pair getCharacteristicRolenameAndAlias( + ExplicitSelectedEntityCharacteristicReference esecr) { SelectedEntityCharacteristicReference secr = esecr.getSelectedEntityCharacteristicReference(); QueryIdentifier alias = (QueryIdentifier) esecr.getProjectedCharacteristicAlias(); String roleName = ((QueryIdentifier) secr.getCharacteristic()).getId().trim(); String roleNameOrAlias = (alias != null) ? alias.getId() : roleName; - return new Pair(roleName, roleNameOrAlias); + return new Pair(roleName, roleNameOrAlias); } - + /** * Return the referenced Characteristic - if it exists + * * @param secr * @param roleNameOrAlias - the name it will go by - * @param matchedEntities - the entities that this query references, keyed by entity name or alias + * @param matchedEntities - the entities that this query references, keyed by + * entity name or alias * @return */ - private Characteristic getSelectedEntityCharacteristic(SelectedEntityCharacteristicReference secr, String roleNameOrAlias, - Map matchedEntities) { + private Characteristic getSelectedEntityCharacteristic(SelectedEntityCharacteristicReference secr, + String roleNameOrAlias, Map matchedEntities) { QueryIdentifier se = (QueryIdentifier) secr.getSelectedEntity(); Characteristic result = null; @@ -627,12 +643,12 @@ private Characteristic getSelectedEntityCharacteristic(SelectedEntityCharacteris boolean found = false; for (Entity ent : matchedEntities.values()) { try { - result = getCharacteristicByRolename(ent,roleNameOrAlias); + result = getCharacteristicByRolename(ent, roleNameOrAlias); if (found) { // Found a second time - ambiguous. throw new NameCollisionException(roleNameOrAlias); } else { - found = true; + found = true; } } catch (CharacteristicNotFoundException e) { // Not found in that ent - try the next one @@ -642,25 +658,31 @@ private Characteristic getSelectedEntityCharacteristic(SelectedEntityCharacteris } else { String entityOrAlias = ((QueryIdentifier) se).getId(); Entity ent = matchedEntities.get(entityOrAlias); - result = getCharacteristicByRolename(ent,roleNameOrAlias); + result = getCharacteristicByRolename(ent, roleNameOrAlias); } return result; } + /** - * Process the joins. This method assumes joins are unambiguous - order of join expressions doesn't matter and references in the join criteria map uniquely based - * on alias if specified or else name/rolename (No use of name/rolename if alias is specified). + * Process the joins. This method assumes joins are unambiguous - order of join + * expressions doesn't matter and references in the join criteria map uniquely + * based on alias if specified or else name/rolename (No use of name/rolename if + * alias is specified). + * * @param q * @param qstmt * @param matchedEntities * @param rawSelections * @return */ - public Map> processJoins(Query q, QueryStatement qstmt, Map matchedEntities, Map rawSelections) { + public Map> processJoins(Query q, QueryStatement qstmt, + Map matchedEntities, Map rawSelections) { // final Iterable joinCriteria = Iterables.filter( // IteratorExtensions.toIterable(qstmt.eAllContents()), EntityJoinCriteria.class); Map> result = new HashMap<>(); - // Initialize results with the content of rawSelections - this gives us the base cardinality - for (Map.Entry entry: rawSelections.entrySet()) { + // Initialize results with the content of rawSelections - this gives us the base + // cardinality + for (Map.Entry entry : rawSelections.entrySet()) { QuerySelectedComposition selection = new QuerySelectedComposition(); selection.alias = entry.getKey(); selection.roleName = getCharacteristicRolename(entry.getValue()); @@ -670,12 +692,13 @@ public Map> processJoins(Query result.put(entry.getKey(), selection); } EList ejes = qstmt.getSelectedEntityExpression().getFrom().getEntity().getEje(); - for (EntityJoinExpression eje: ejes) { + for (EntityJoinExpression eje : ejes) { EntityJoinCriteria ejc = eje.getEntityJoinCriteria(); - for (EntityTypeCharacteristicEquivalenceExpression etcee: ejc.getEtcee()) { + for (EntityTypeCharacteristicEquivalenceExpression etcee : ejc.getEtcee()) { SelectedEntityCharacteristicReference secr = etcee.getSecr(); - String roleNameOrAlias = ((QueryIdentifier)secr.getCharacteristic()).getId(); - Characteristic joiningCharacteristic = getSelectedEntityCharacteristic(secr, roleNameOrAlias,matchedEntities); + String roleNameOrAlias = ((QueryIdentifier) secr.getCharacteristic()).getId(); + Characteristic joiningCharacteristic = getSelectedEntityCharacteristic(secr, roleNameOrAlias, + matchedEntities); ComposableElement ce = getCharacteristicType(joiningCharacteristic); QualifiedName joiningCharTypeQN = qnp.getFullyQualifiedName(ce); @@ -683,42 +706,51 @@ public Map> processJoins(Query SelectedEntity se = (SelectedEntity) eje.getJoinEntity(); Entity joinedEnt = null; // Is there a reference to the joined entity? - QueryIdentifier ser = (QueryIdentifier)etcee.getSelectedEntity(); + QueryIdentifier ser = (QueryIdentifier) etcee.getSelectedEntity(); if (ser == null) { - // If the SelectEntityReference is null, then use the join entity + // If the SelectEntityReference is null, then use the join entity Pair entAlias = getEntityAndAlias(se); joinedEnt = matchedEntities.get(entAlias.getSecond()); } else { - // If the SelectEntityReference is not null, then join on that referenced entity - we assume the reference uses + // If the SelectEntityReference is not null, then join on that referenced entity + // - we assume the reference uses // the alias if one is specified. joinedEnt = matchedEntities.get(ser.getId()); } - // Check: we need to match the type. - // That means that SelectedEntityCharacteristicReference must reference an element whose type matches - // the type of the joined entity. Because these are model elements, the type is captured in the FQN of the element + // Check: we need to match the type. + // That means that SelectedEntityCharacteristicReference must reference an + // element whose type matches + // the type of the joined entity. Because these are model elements, the type is + // captured in the FQN of the element QualifiedName joinedEntQN = qnp.getFullyQualifiedName(joinedEnt); if (!joinedEntQN.equals(joiningCharTypeQN)) { - String msg = MessageFormat.format(WRONG_TYPE_FMT, qnp.getFullyQualifiedName(joiningCharacteristic).toString(), joinedEntQN.toString(), + String msg = MessageFormat.format(WRONG_TYPE_FMT, + qnp.getFullyQualifiedName(joiningCharacteristic).toString(), joinedEntQN.toString(), joiningCharTypeQN.toString()); throw new WrongTypeException(msg); } - //If we get here, the join will work - - // Next: Determine impact on cardinality of characteristics from the join - cardinality is impacted by the - // joining Entity/Characteristic relationship - but applies to all the selected characteristics of the joinedEnt + // If we get here, the join will work - + // Next: Determine impact on cardinality of characteristics from the join - + // cardinality is impacted by the + // joining Entity/Characteristic relationship - but applies to all the selected + // characteristics of the joinedEnt int applicableLB = getCharacteristicLowerBound(joiningCharacteristic); int applicableUB = getCharacteristicUpperBound(joiningCharacteristic); - // Now loop through the selected characteristics - find the ones from the joinedEntity and update their bounds - for (QuerySelectedComposition sel: result.values()) { + // Now loop through the selected characteristics - find the ones from the + // joinedEntity and update their bounds + for (QuerySelectedComposition sel : result.values()) { if (sel.referencedCharacteristic.eContainer() == joinedEnt) { sel.updateBounds(applicableLB, applicableUB); } } } } - // At this point, all the selection cardinalities have been updated bsaed on the joins + // At this point, all the selection cardinalities have been updated bsaed on the + // joins return result; } + /** * Taken from the book, SmallJavaLib.getSmallJavaObjectClass - and converted * from XTend to Java @@ -771,7 +803,7 @@ protected Collection getCharacteristics(Entity obj) { * @return The found characteristic */ protected Characteristic getCharacteristicByRolename(Entity ent, String roleName) - /*throws CharacteristicNotFoundException*/ { + /* throws CharacteristicNotFoundException */ { return eproc.getCharacteristicByRolename(ent, roleName); } diff --git a/com.epistimis.uddl/src/com/epistimis/uddl/util/IndexUtilities.xtend b/com.epistimis.uddl/src/com/epistimis/uddl/util/IndexUtilities.xtend index 72206d3..d59eea6 100644 --- a/com.epistimis.uddl/src/com/epistimis/uddl/util/IndexUtilities.xtend +++ b/com.epistimis.uddl/src/com/epistimis/uddl/util/IndexUtilities.xtend @@ -40,6 +40,7 @@ import static java.util.Objects.requireNonNull; import java.util.HashMap import com.epistimis.uddl.scoping.ECrossReferenceAdapterCrossReferenceProvider import com.epistimis.uddl.scoping.ResourceSetRootEObjectProvider +import java.util.stream.Collectors //import org.eclipse.acceleo.query.validation.type.IType //import org.eclipse.acceleo.query.validation.type.EClassifierType @@ -268,6 +269,14 @@ class IndexUtilities { context.eResource.objectFromDescription(it) ]); } + + /** + * Convert a list of descriptions into a set of EObjects. Needed because, apparently, we can have + * multiple IEObjectDescriptions for the same EObject + */ + def static uniqueObjectsFromDescriptions(List descriptions) { + return descriptions.stream().map([d|d.EObjectOrProxy]).collect(Collectors.toSet()); + } /** * Get the EObject from the EObjectDescription @@ -355,7 +364,7 @@ class IndexUtilities { * @param typeName The type we were looking for * @param descriptions What we found */ - def printIEObjectDescriptionNameCollisions(String qn, String typeName, List descriptions) { + def printIEObjectDescriptionNameCollisions(String qn, String typeName, Collection descriptions) { var msg = MessageFormat.format(COLLISION_MSG_FMT, qn, typeName); for (IEObjectDescription d : descriptions) { msg += MessageFormat.format("\t {0}\n", d.getQualifiedName().toString()); @@ -369,7 +378,7 @@ class IndexUtilities { * @param typeName The type we were looking for * @param objects What we found */ - def printEObjectNameCollisions(String qn, String typeName, List objects) { + def printEObjectNameCollisions(String qn, String typeName, Collection objects) { var msg = MessageFormat.format(COLLISION_MSG_FMT, qn, typeName); for (EObject o : objects) { msg += MessageFormat.format("\t {0}\n", qnp.getFullyQualifiedName(o).toString());