use of org.broadleafcommerce.openadmin.dto.ForeignKey in project BroadleafCommerce by BroadleafCommerce.
the class AdornedTargetCollectionFieldMetadataProvider method buildAdornedTargetCollectionMetadata.
protected void buildAdornedTargetCollectionMetadata(Class<?> parentClass, Class<?> targetClass, Map<String, FieldMetadata> attributes, FieldInfo field, FieldMetadataOverride adornedTargetCollectionMetadata, DynamicEntityDao dynamicEntityDao) {
AdornedTargetCollectionMetadata serverMetadata = (AdornedTargetCollectionMetadata) attributes.get(field.getName());
Class<?> resolvedClass = parentClass == null ? targetClass : parentClass;
AdornedTargetCollectionMetadata metadata;
if (serverMetadata != null) {
metadata = serverMetadata;
} else {
metadata = new AdornedTargetCollectionMetadata();
}
metadata.setTargetClass(targetClass.getName());
metadata.setFieldName(field.getName());
if (adornedTargetCollectionMetadata.getReadOnly() != null) {
metadata.setMutable(!adornedTargetCollectionMetadata.getReadOnly());
}
if (adornedTargetCollectionMetadata.getShowIfProperty() != null) {
metadata.setShowIfProperty(adornedTargetCollectionMetadata.getShowIfProperty());
}
if (adornedTargetCollectionMetadata.getShowIfFieldEquals() != null) {
metadata.setShowIfFieldEquals(adornedTargetCollectionMetadata.getShowIfFieldEquals());
}
org.broadleafcommerce.openadmin.dto.OperationTypes dtoOperationTypes = new org.broadleafcommerce.openadmin.dto.OperationTypes(OperationType.ADORNEDTARGETLIST, OperationType.ADORNEDTARGETLIST, OperationType.ADORNEDTARGETLIST, OperationType.ADORNEDTARGETLIST, OperationType.BASIC);
if (adornedTargetCollectionMetadata.getAddType() != null) {
dtoOperationTypes.setAddType(adornedTargetCollectionMetadata.getAddType());
}
if (adornedTargetCollectionMetadata.getRemoveType() != null) {
dtoOperationTypes.setRemoveType(adornedTargetCollectionMetadata.getRemoveType());
}
if (adornedTargetCollectionMetadata.getFetchType() != null) {
dtoOperationTypes.setFetchType(adornedTargetCollectionMetadata.getFetchType());
}
if (adornedTargetCollectionMetadata.getInspectType() != null) {
dtoOperationTypes.setInspectType(adornedTargetCollectionMetadata.getInspectType());
}
if (adornedTargetCollectionMetadata.getUpdateType() != null) {
dtoOperationTypes.setUpdateType(adornedTargetCollectionMetadata.getUpdateType());
}
// don't allow additional non-persistent properties or additional foreign keys for an advanced collection datasource - they don't make sense in this context
PersistencePerspective persistencePerspective;
if (serverMetadata != null) {
persistencePerspective = metadata.getPersistencePerspective();
persistencePerspective.setOperationTypes(dtoOperationTypes);
} else {
persistencePerspective = new PersistencePerspective(dtoOperationTypes, new String[] {}, new ForeignKey[] {});
metadata.setPersistencePerspective(persistencePerspective);
}
String parentObjectProperty = null;
if (serverMetadata != null) {
parentObjectProperty = ((AdornedTargetList) serverMetadata.getPersistencePerspective().getPersistencePerspectiveItems().get(PersistencePerspectiveItemType.ADORNEDTARGETLIST)).getLinkedObjectPath();
}
if (!StringUtils.isEmpty(adornedTargetCollectionMetadata.getParentObjectProperty())) {
parentObjectProperty = adornedTargetCollectionMetadata.getParentObjectProperty();
}
if (parentObjectProperty == null && !StringUtils.isEmpty(field.getOneToManyMappedBy())) {
parentObjectProperty = field.getOneToManyMappedBy();
}
if (parentObjectProperty == null && !StringUtils.isEmpty(field.getManyToManyMappedBy())) {
parentObjectProperty = field.getManyToManyMappedBy();
}
if (StringUtils.isEmpty(parentObjectProperty)) {
throw new IllegalArgumentException("Unable to infer a parentObjectProperty for the @AdminPresentationAdornedTargetCollection annotated field(" + field.getName() + "). If not using the mappedBy property of @OneToMany or @ManyToMany, please make sure to explicitly define the parentObjectProperty property");
}
String sortProperty = null;
if (serverMetadata != null) {
sortProperty = ((AdornedTargetList) serverMetadata.getPersistencePerspective().getPersistencePerspectiveItems().get(PersistencePerspectiveItemType.ADORNEDTARGETLIST)).getSortField();
}
if (!StringUtils.isEmpty(adornedTargetCollectionMetadata.getSortProperty())) {
sortProperty = adornedTargetCollectionMetadata.getSortProperty();
}
metadata.setParentObjectClass(resolvedClass.getName());
if (adornedTargetCollectionMetadata.getMaintainedAdornedTargetFields() != null) {
metadata.setMaintainedAdornedTargetFields(adornedTargetCollectionMetadata.getMaintainedAdornedTargetFields());
}
if (adornedTargetCollectionMetadata.getGridVisibleFields() != null) {
metadata.setGridVisibleFields(adornedTargetCollectionMetadata.getGridVisibleFields());
}
if (adornedTargetCollectionMetadata.getSelectizeVisibleField() != null) {
metadata.setSelectizeVisibleField(adornedTargetCollectionMetadata.getSelectizeVisibleField());
}
String parentObjectIdProperty = null;
if (serverMetadata != null) {
parentObjectIdProperty = ((AdornedTargetList) serverMetadata.getPersistencePerspective().getPersistencePerspectiveItems().get(PersistencePerspectiveItemType.ADORNEDTARGETLIST)).getLinkedIdProperty();
}
if (adornedTargetCollectionMetadata.getParentObjectIdProperty() != null) {
parentObjectIdProperty = adornedTargetCollectionMetadata.getParentObjectIdProperty();
}
String targetObjectProperty = null;
if (serverMetadata != null) {
targetObjectProperty = ((AdornedTargetList) serverMetadata.getPersistencePerspective().getPersistencePerspectiveItems().get(PersistencePerspectiveItemType.ADORNEDTARGETLIST)).getTargetObjectPath();
}
if (adornedTargetCollectionMetadata.getTargetObjectProperty() != null) {
targetObjectProperty = adornedTargetCollectionMetadata.getTargetObjectProperty();
}
if (StringUtils.isEmpty(parentObjectIdProperty)) {
throw new IllegalArgumentException("targetObjectProperty not defined");
}
String joinEntityClass = null;
if (serverMetadata != null) {
joinEntityClass = ((AdornedTargetList) serverMetadata.getPersistencePerspective().getPersistencePerspectiveItems().get(PersistencePerspectiveItemType.ADORNEDTARGETLIST)).getJoinEntityClass();
}
if (adornedTargetCollectionMetadata.getJoinEntityClass() != null) {
joinEntityClass = adornedTargetCollectionMetadata.getJoinEntityClass();
}
Class<?> collectionTarget = null;
try {
checkCeiling: {
try {
ParameterizedType pt = (ParameterizedType) field.getGenericType();
java.lang.reflect.Type collectionType = pt.getActualTypeArguments()[0];
collectionTarget = (Class<?>) collectionType;
// Hibernate class is from that
if (collectionTarget.isInterface()) {
collectionTarget = entityConfiguration.lookupEntityClass(collectionTarget.getName());
}
break checkCeiling;
} catch (NoSuchBeanDefinitionException e) {
// We weren't successful at looking at entity configuration to find the type of this collection.
// We will continue and attempt to find it via the Hibernate annotations
}
if (!StringUtils.isEmpty(field.getOneToManyTargetEntity()) && !void.class.getName().equals(field.getOneToManyTargetEntity())) {
collectionTarget = Class.forName(field.getOneToManyTargetEntity());
break checkCeiling;
}
if (!StringUtils.isEmpty(field.getManyToManyTargetEntity()) && !void.class.getName().equals(field.getManyToManyTargetEntity())) {
collectionTarget = Class.forName(field.getManyToManyTargetEntity());
break checkCeiling;
}
}
if (StringUtils.isNotBlank(joinEntityClass)) {
collectionTarget = Class.forName(joinEntityClass);
}
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
if (collectionTarget == null) {
throw new IllegalArgumentException("Unable to infer the type of the collection from the targetEntity property of a OneToMany or ManyToMany collection.");
}
Field collectionTargetField = dynamicEntityDao.getFieldManager().getField(collectionTarget, targetObjectProperty);
ManyToOne manyToOne = collectionTargetField.getAnnotation(ManyToOne.class);
String ceiling = null;
checkCeiling: {
if (manyToOne != null && manyToOne.targetEntity() != void.class) {
ceiling = manyToOne.targetEntity().getName();
break checkCeiling;
}
ceiling = collectionTargetField.getType().getName();
}
if (!StringUtils.isEmpty(ceiling)) {
metadata.setCollectionCeilingEntity(ceiling);
}
String targetObjectIdProperty = null;
if (serverMetadata != null) {
targetObjectIdProperty = ((AdornedTargetList) serverMetadata.getPersistencePerspective().getPersistencePerspectiveItems().get(PersistencePerspectiveItemType.ADORNEDTARGETLIST)).getTargetIdProperty();
}
if (adornedTargetCollectionMetadata.getTargetObjectIdProperty() != null) {
targetObjectIdProperty = adornedTargetCollectionMetadata.getTargetObjectIdProperty();
}
Boolean isAscending = true;
if (serverMetadata != null) {
isAscending = ((AdornedTargetList) serverMetadata.getPersistencePerspective().getPersistencePerspectiveItems().get(PersistencePerspectiveItemType.ADORNEDTARGETLIST)).getSortAscending();
}
if (adornedTargetCollectionMetadata.isSortAscending() != null) {
isAscending = adornedTargetCollectionMetadata.isSortAscending();
}
if (serverMetadata != null) {
AdornedTargetList adornedTargetList = (AdornedTargetList) serverMetadata.getPersistencePerspective().getPersistencePerspectiveItems().get(PersistencePerspectiveItemType.ADORNEDTARGETLIST);
adornedTargetList.setCollectionFieldName(field.getName());
adornedTargetList.setLinkedObjectPath(parentObjectProperty);
adornedTargetList.setLinkedIdProperty(parentObjectIdProperty);
adornedTargetList.setTargetObjectPath(targetObjectProperty);
adornedTargetList.setTargetIdProperty(targetObjectIdProperty);
adornedTargetList.setJoinEntityClass(joinEntityClass);
adornedTargetList.setIdProperty((String) dynamicEntityDao.getIdMetadata(collectionTarget).get("name"));
adornedTargetList.setAdornedTargetEntityClassname(collectionTarget.getName());
adornedTargetList.setSortField(sortProperty);
adornedTargetList.setSortAscending(isAscending);
adornedTargetList.setMutable(metadata.isMutable());
} else {
AdornedTargetList adornedTargetList = new AdornedTargetList(field.getName(), parentObjectProperty, parentObjectIdProperty, targetObjectProperty, targetObjectIdProperty, collectionTarget.getName(), sortProperty, isAscending);
adornedTargetList.setJoinEntityClass(joinEntityClass);
adornedTargetList.setIdProperty((String) dynamicEntityDao.getIdMetadata(collectionTarget).get("name"));
adornedTargetList.setMutable(metadata.isMutable());
persistencePerspective.addPersistencePerspectiveItem(PersistencePerspectiveItemType.ADORNEDTARGETLIST, adornedTargetList);
}
if (adornedTargetCollectionMetadata.getExcluded() != null) {
if (LOG.isDebugEnabled()) {
if (adornedTargetCollectionMetadata.getExcluded()) {
LOG.debug("buildAdornedTargetCollectionMetadata:Excluding " + field.getName() + " because it was explicitly declared in config");
} else {
LOG.debug("buildAdornedTargetCollectionMetadata:Showing " + field.getName() + " because it was explicitly declared in config");
}
}
metadata.setExcluded(adornedTargetCollectionMetadata.getExcluded());
}
if (adornedTargetCollectionMetadata.getLazyFetch() != null) {
metadata.setLazyFetch(adornedTargetCollectionMetadata.getLazyFetch());
}
if (adornedTargetCollectionMetadata.getManualFetch() != null) {
metadata.setManualFetch(adornedTargetCollectionMetadata.getManualFetch());
}
if (adornedTargetCollectionMetadata.getFriendlyName() != null) {
metadata.setFriendlyName(adornedTargetCollectionMetadata.getFriendlyName());
}
if (adornedTargetCollectionMetadata.getSecurityLevel() != null) {
metadata.setSecurityLevel(adornedTargetCollectionMetadata.getSecurityLevel());
}
if (adornedTargetCollectionMetadata.getOrder() != null) {
metadata.setOrder(adornedTargetCollectionMetadata.getOrder());
}
if (adornedTargetCollectionMetadata.getTab() != null) {
metadata.setTab(adornedTargetCollectionMetadata.getTab());
}
if (adornedTargetCollectionMetadata.getTabOrder() != null) {
metadata.setTabOrder(adornedTargetCollectionMetadata.getTabOrder());
}
if (adornedTargetCollectionMetadata.getGroup() != null) {
metadata.setGroup(adornedTargetCollectionMetadata.getGroup());
}
if (adornedTargetCollectionMetadata.getCustomCriteria() != null) {
metadata.setCustomCriteria(adornedTargetCollectionMetadata.getCustomCriteria());
}
if (adornedTargetCollectionMetadata.getUseServerSideInspectionCache() != null) {
persistencePerspective.setUseServerSideInspectionCache(adornedTargetCollectionMetadata.getUseServerSideInspectionCache());
}
if (adornedTargetCollectionMetadata.isIgnoreAdornedProperties() != null) {
metadata.setIgnoreAdornedProperties(adornedTargetCollectionMetadata.isIgnoreAdornedProperties());
}
if (adornedTargetCollectionMetadata.getAdornedTargetAddMethodType() != null) {
metadata.setAdornedTargetAddMethodType(adornedTargetCollectionMetadata.getAdornedTargetAddMethodType());
}
if (adornedTargetCollectionMetadata.getCurrencyCodeField() != null) {
metadata.setCurrencyCodeField(adornedTargetCollectionMetadata.getCurrencyCodeField());
}
attributes.put(field.getName(), metadata);
}
use of org.broadleafcommerce.openadmin.dto.ForeignKey in project BroadleafCommerce by BroadleafCommerce.
the class DynamicEntityDaoImpl method applyForeignKeyPrecedence.
protected void applyForeignKeyPrecedence(ForeignKey foreignField, ForeignKey[] additionalForeignFields, Map<String, FieldMetadata> mergedProperties) {
for (String key : mergedProperties.keySet()) {
boolean isForeign = false;
if (foreignField != null) {
isForeign = foreignField.getManyToField().equals(key);
}
if (!isForeign && !ArrayUtils.isEmpty(additionalForeignFields)) {
for (ForeignKey foreignKey : additionalForeignFields) {
isForeign = foreignKey.getManyToField().equals(key);
if (isForeign) {
break;
}
}
}
if (isForeign) {
FieldMetadata metadata = mergedProperties.get(key);
metadata.setExcluded(false);
}
}
}
use of org.broadleafcommerce.openadmin.dto.ForeignKey in project BroadleafCommerce by BroadleafCommerce.
the class DynamicEntityDaoImpl method getCacheKey.
protected String getCacheKey(String ceilingEntityFullyQualifiedClassname, ForeignKey foreignField, String[] additionalNonPersistentProperties, ForeignKey[] additionalForeignFields, MergedPropertyType mergedPropertyType, Boolean populateManyToOneFields, Class<?> clazz, String configurationKey, Boolean isParentExcluded) {
StringBuilder sb = new StringBuilder(150);
sb.append(ceilingEntityFullyQualifiedClassname);
sb.append(clazz.hashCode());
sb.append(foreignField == null ? "" : foreignField.toString());
sb.append(configurationKey);
sb.append(isParentExcluded);
if (additionalNonPersistentProperties != null) {
for (String prop : additionalNonPersistentProperties) {
sb.append(prop);
}
}
if (additionalForeignFields != null) {
for (ForeignKey key : additionalForeignFields) {
sb.append(key.toString());
}
}
sb.append(mergedPropertyType);
sb.append(populateManyToOneFields);
String digest;
try {
MessageDigest md = MessageDigest.getInstance("MD5");
byte[] messageDigest = md.digest(sb.toString().getBytes());
BigInteger number = new BigInteger(1, messageDigest);
digest = number.toString(16);
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e);
}
String key = pad(digest, 32, '0');
if (LOG.isDebugEnabled()) {
LOG.debug("Created cache key: " + key + " from the following string: " + sb.toString());
}
return key;
}
use of org.broadleafcommerce.openadmin.dto.ForeignKey in project BroadleafCommerce by BroadleafCommerce.
the class BasicPersistenceModule method remove.
@Override
public void remove(PersistencePackage persistencePackage) throws ServiceException {
Entity entity = persistencePackage.getEntity();
PersistencePerspective persistencePerspective = persistencePackage.getPersistencePerspective();
ForeignKey foreignKey = (ForeignKey) persistencePerspective.getPersistencePerspectiveItems().get(PersistencePerspectiveItemType.FOREIGNKEY);
if (foreignKey != null && !foreignKey.getMutable()) {
throw new SecurityServiceException("Entity not mutable");
}
try {
Class<?>[] entities = persistenceManager.getPolymorphicEntities(persistencePackage.getCeilingEntityFullyQualifiedClassname());
Map<String, FieldMetadata> mergedUnfilteredProperties = persistenceManager.getDynamicEntityDao().getMergedProperties(persistencePackage.getCeilingEntityFullyQualifiedClassname(), entities, foreignKey, persistencePerspective.getAdditionalNonPersistentProperties(), persistencePerspective.getAdditionalForeignKeys(), MergedPropertyType.PRIMARY, persistencePerspective.getPopulateToOneFields(), persistencePerspective.getIncludeFields(), persistencePerspective.getExcludeFields(), persistencePerspective.getConfigurationKey(), "");
Map<String, FieldMetadata> mergedProperties = filterOutCollectionMetadata(mergedUnfilteredProperties);
Object primaryKey = getPrimaryKey(entity, mergedProperties);
Serializable instance = persistenceManager.getDynamicEntityDao().retrieve(Class.forName(entity.getType()[0]), primaryKey);
Assert.isTrue(instance != null, "Entity not found");
switch(persistencePerspective.getOperationTypes().getRemoveType()) {
case NONDESTRUCTIVEREMOVE:
FieldManager fieldManager = getFieldManager();
FieldMetadata manyToFieldMetadata = mergedUnfilteredProperties.get(foreignKey.getManyToField());
Object foreignKeyValue = entity.getPMap().get(foreignKey.getManyToField()).getValue();
try {
foreignKeyValue = Long.valueOf((String) foreignKeyValue);
} catch (NumberFormatException e) {
LOG.warn("Foreign primary key is not of type Long, assuming String for remove lookup");
}
Serializable foreignInstance = persistenceManager.getDynamicEntityDao().retrieve(Class.forName(foreignKey.getForeignKeyClass()), foreignKeyValue);
Collection collection = (Collection) fieldManager.getFieldValue(foreignInstance, foreignKey.getOriginatingField());
collection.remove(instance);
// set the manyTo field to null so that subsequent lookups will not find it
if (manyToFieldMetadata instanceof BasicFieldMetadata) {
if (BooleanUtils.isTrue(((BasicFieldMetadata) manyToFieldMetadata).getRequired())) {
throw new ServiceException("Could not remove from the collection as the ManyToOne side is a" + " non-optional relationship. Consider changing 'optional=true' in the @ManyToOne annotation" + " or nullable=true within the @JoinColumn annotation");
}
// Since this is occuring on a remove persistence package, merge up-front (before making a change) for proper operation in the presence of the enterprise module
instance = persistenceManager.getDynamicEntityDao().merge(instance);
Field manyToField = fieldManager.getField(instance.getClass(), foreignKey.getManyToField());
Object manyToObject = manyToField.get(instance);
if (manyToObject != null && !(manyToObject instanceof Collection) && !(manyToObject instanceof Map)) {
manyToField.set(instance, null);
instance = persistenceManager.getDynamicEntityDao().merge(instance);
}
}
break;
case BASIC:
persistenceManager.getDynamicEntityDao().remove(instance);
break;
}
} catch (Exception e) {
throw new ServiceException("Problem removing entity : " + e.getMessage(), e);
}
}
use of org.broadleafcommerce.openadmin.dto.ForeignKey in project BroadleafCommerce by BroadleafCommerce.
the class MapStructurePersistenceModule method fetch.
@Override
public DynamicResultSet fetch(PersistencePackage persistencePackage, CriteriaTransferObject cto) throws ServiceException {
Entity[] payload;
int totalRecords;
String ceilingEntityFullyQualifiedClassname = persistencePackage.getCeilingEntityFullyQualifiedClassname();
if (StringUtils.isEmpty(persistencePackage.getFetchTypeFullyQualifiedClassname())) {
persistencePackage.setFetchTypeFullyQualifiedClassname(ceilingEntityFullyQualifiedClassname);
}
try {
PersistencePerspective persistencePerspective = persistencePackage.getPersistencePerspective();
Class<?>[] entities = persistenceManager.getPolymorphicEntities(ceilingEntityFullyQualifiedClassname);
Map<String, FieldMetadata> mergedProperties = persistenceManager.getDynamicEntityDao().getMergedProperties(ceilingEntityFullyQualifiedClassname, entities, (ForeignKey) persistencePerspective.getPersistencePerspectiveItems().get(PersistencePerspectiveItemType.FOREIGNKEY), persistencePerspective.getAdditionalNonPersistentProperties(), persistencePerspective.getAdditionalForeignKeys(), MergedPropertyType.PRIMARY, persistencePerspective.getPopulateToOneFields(), persistencePerspective.getIncludeFields(), persistencePerspective.getExcludeFields(), persistencePerspective.getConfigurationKey(), "");
MapStructure mapStructure = (MapStructure) persistencePerspective.getPersistencePerspectiveItems().get(PersistencePerspectiveItemType.MAPSTRUCTURE);
PersistentClass persistentClass = persistenceManager.getDynamicEntityDao().getPersistentClass(mapStructure.getValueClassName());
Map<String, FieldMetadata> valueUnfilteredMergedProperties;
if (persistentClass == null) {
valueUnfilteredMergedProperties = persistenceManager.getDynamicEntityDao().getPropertiesForPrimitiveClass(((SimpleValueMapStructure) mapStructure).getValuePropertyName(), ((SimpleValueMapStructure) mapStructure).getValuePropertyFriendlyName(), Class.forName(mapStructure.getValueClassName()), Class.forName(ceilingEntityFullyQualifiedClassname), MergedPropertyType.MAPSTRUCTUREVALUE);
} else {
String valueClassName = mapStructure.getValueClassName();
Class<?>[] mapEntities = persistenceManager.getPolymorphicEntities(valueClassName);
valueUnfilteredMergedProperties = persistenceManager.getDynamicEntityDao().getMergedProperties(valueClassName, mapEntities, null, new String[] {}, new ForeignKey[] {}, MergedPropertyType.MAPSTRUCTUREVALUE, false, new String[] {}, new String[] {}, null, "");
}
Map<String, FieldMetadata> valueMergedProperties = filterOutCollectionMetadata(valueUnfilteredMergedProperties);
List<FilterMapping> filterMappings = getFilterMappings(persistencePerspective, cto, persistencePackage.getFetchTypeFullyQualifiedClassname(), mergedProperties);
if (CollectionUtils.isNotEmpty(cto.getAdditionalFilterMappings())) {
filterMappings.addAll(cto.getAdditionalFilterMappings());
}
totalRecords = getTotalRecords(persistencePackage.getFetchTypeFullyQualifiedClassname(), filterMappings);
if (totalRecords > 1) {
throw new ServiceException("Queries to retrieve an entity containing a MapStructure must return only 1 entity. Your query returned (" + totalRecords + ") values.");
}
List<Serializable> records = getPersistentRecords(persistencePackage.getFetchTypeFullyQualifiedClassname(), filterMappings, cto.getFirstResult(), cto.getMaxResults());
Map<String, FieldMetadata> ceilingMergedProperties = getSimpleMergedProperties(ceilingEntityFullyQualifiedClassname, persistencePerspective);
payload = getMapRecords(records.get(0), mapStructure, ceilingMergedProperties, valueMergedProperties, null, persistencePackage.getCustomCriteria());
} catch (Exception e) {
throw new ServiceException("Unable to fetch results for " + ceilingEntityFullyQualifiedClassname, e);
}
DynamicResultSet results = new DynamicResultSet(null, payload, payload.length);
return results;
}
Aggregations