use of org.eclipse.n4js.ts.types.Type in project n4js by eclipse.
the class N4JSSyntaxValidator method checkInterfaceDeclaration.
/**
* Checks that no "with" or "role" is used and that list of implemented interfaces is separated with commas and not
* with keywords. These checks (with some warnings created instead of errors) should help the transition from roles
* to interfaces. However, they may be useful later on as well, e.g., if an interface is manually refactored into a
* class or vice versa.
* <p>
* Note that "with" is used in Dart for roles, so maybe it is useful to have a user-friendly message instead of a
* parser error.
* <p>
* "role" will be removed in grammar.
*/
@Check
public void checkInterfaceDeclaration(N4InterfaceDeclaration n4InterfaceDecl) {
ICompositeNode node = NodeModelUtils.findActualNodeFor(n4InterfaceDecl);
ILeafNode keywordNode;
keywordNode = findLeafWithKeyword(n4InterfaceDecl, "{", node, IMPLEMENTS_KEYWORD, false);
if (keywordNode != null) {
TInterface tinterface = n4InterfaceDecl.getDefinedTypeAsInterface();
if (tinterface == null) {
// avoid consequential errors
return;
}
if (tinterface.getSuperInterfaceRefs().isEmpty()) {
// ok
return;
}
if (tinterface.getSuperInterfaceRefs().stream().allMatch(superTypeRef -> superTypeRef.getDeclaredType() instanceof TInterface)) {
List<? extends IdentifiableElement> interfaces = tinterface.getSuperInterfaceRefs().stream().flatMap((ParameterizedTypeRef ref) -> {
Type declaredType = ref.getDeclaredType();
if (declaredType instanceof TInterface) {
return Stream.of((TInterface) declaredType);
}
return Stream.empty();
}).collect(Collectors.toList());
String message = getMessageForSYN_KW_EXTENDS_IMPLEMENTS_MIXED_UP(validatorMessageHelper.description(tinterface), "implement", "interface" + (interfaces.size() > 1 ? "s " : " ") + validatorMessageHelper.names(interfaces), EXTENDS_KEYWORD);
addIssue(message, n4InterfaceDecl, keywordNode.getTotalOffset(), keywordNode.getLength(), SYN_KW_EXTENDS_IMPLEMENTS_MIXED_UP);
}
}
}
use of org.eclipse.n4js.ts.types.Type in project n4js by eclipse.
the class InternalTypeSystem method applyRuleExpectedTypeOfArgument.
protected Result<TypeRef> applyRuleExpectedTypeOfArgument(final RuleEnvironment G, final RuleApplicationTrace _trace_, final Argument argument, final Expression argumentExpression) throws RuleFailedException {
// output parameter
TypeRef T = null;
final EObject expr = argument.eContainer();
if ((expr instanceof NewExpression)) {
boolean _contains = ((NewExpression) expr).getArguments().contains(argument);
boolean _not = (!_contains);
if (_not) {
} else {
/* G |- expr.callee : var TypeTypeRef ctorTypeRef */
Expression _callee = ((NewExpression) expr).getCallee();
TypeTypeRef ctorTypeRef = null;
Result<TypeRef> result = typeInternal(G, _trace_, _callee);
checkAssignableTo(result.getFirst(), TypeTypeRef.class);
ctorTypeRef = (TypeTypeRef) result.getFirst();
TypeRef typeRefOfInstanceToCreate = this.typeSystemHelper.createTypeRefFromStaticType(G, ctorTypeRef, ((TypeArgument[]) Conversions.unwrapArray(((NewExpression) expr).getTypeArgs(), TypeArgument.class)));
Type _declaredType = typeRefOfInstanceToCreate.getDeclaredType();
ContainerType<?> typeOfInstanceToCreate = ((ContainerType<?>) _declaredType);
final RuleEnvironment G2 = RuleEnvironmentExtensions.wrap(G);
this.typeSystemHelper.addSubstitutions(G2, typeRefOfInstanceToCreate);
RuleEnvironmentExtensions.addThisType(G2, typeRefOfInstanceToCreate);
TMethod ctor = this.containerTypesHelper.fromContext(((NewExpression) expr).eResource()).findConstructor(typeOfInstanceToCreate);
TFormalParameter _fparForArgIdx = null;
if (ctor != null) {
_fparForArgIdx = ctor.getFparForArgIdx(ECollections.indexOf(((NewExpression) expr).getArguments(), argument, 0));
}
final TFormalParameter fpar = _fparForArgIdx;
if ((fpar == null)) {
T = TypeRefsFactory.eINSTANCE.createUnknownTypeRef();
} else {
final TypeRef paramType = fpar.getTypeRef();
if ((paramType == null)) {
T = RuleEnvironmentExtensions.anyTypeRef(G2);
} else {
/* G2 |- paramType ~> T */
Result<TypeArgument> result_1 = substTypeVariablesInternal(G2, _trace_, paramType);
checkAssignableTo(result_1.getFirst(), TypeRef.class);
T = (TypeRef) result_1.getFirst();
}
}
}
} else {
if ((expr instanceof ParameterizedCallExpression)) {
boolean _contains_1 = ((ParameterizedCallExpression) expr).getArguments().contains(argument);
/* expr.arguments.contains(argument) */
if (!_contains_1) {
sneakyThrowRuleFailedException("expr.arguments.contains(argument)");
}
/* G |- expr.target : var TypeRef targetTypeRef */
Expression _target = ((ParameterizedCallExpression) expr).getTarget();
TypeRef targetTypeRef = null;
Result<TypeRef> result_2 = typeInternal(G, _trace_, _target);
checkAssignableTo(result_2.getFirst(), TypeRef.class);
targetTypeRef = (TypeRef) result_2.getFirst();
if ((targetTypeRef instanceof FunctionTypeExprOrRef)) {
final FunctionTypeExprOrRef F = ((FunctionTypeExprOrRef) targetTypeRef);
final int argIndex = ECollections.indexOf(((ParameterizedCallExpression) expr).getArguments(), argument, 0);
final TFormalParameter fpar_1 = F.getFparForArgIdx(argIndex);
if ((fpar_1 == null)) {
T = TypeRefsFactory.eINSTANCE.createUnknownTypeRef();
} else {
final TypeRef paramType_1 = fpar_1.getTypeRef();
if ((paramType_1 == null)) {
T = RuleEnvironmentExtensions.anyTypeRef(G);
} else {
final RuleEnvironment G2_1 = RuleEnvironmentExtensions.wrap(G);
this.typeSystemHelper.addSubstitutions(G2_1, ((ParameterizedCallExpression) expr), F);
Expression _target_1 = ((ParameterizedCallExpression) expr).getTarget();
if ((_target_1 instanceof SuperLiteral)) {
N4ClassDeclaration _containerOfType = EcoreUtil2.<N4ClassDeclaration>getContainerOfType(expr, N4ClassDeclaration.class);
Type _definedType = null;
if (_containerOfType != null) {
_definedType = _containerOfType.getDefinedType();
}
final Type containingClass = _definedType;
if ((containingClass instanceof TClass)) {
RuleEnvironmentExtensions.addThisType(G2_1, TypeExtensions.ref(containingClass));
ParameterizedTypeRef _superClassRef = ((TClass) containingClass).getSuperClassRef();
boolean _tripleNotEquals = (_superClassRef != null);
if (_tripleNotEquals) {
this.typeSystemHelper.addSubstitutions(G2_1, ((TClass) containingClass).getSuperClassRef());
}
if ((paramType_1 instanceof ThisTypeRefStructural)) {
RuleEnvironmentExtensions.addThisType(G2_1, ((TClass) containingClass).getSuperClassRef());
}
}
}
/* G2 |- paramType ~> T */
Result<TypeArgument> result_3 = substTypeVariablesInternal(G2_1, _trace_, paramType_1);
checkAssignableTo(result_3.getFirst(), TypeRef.class);
T = (TypeRef) result_3.getFirst();
}
}
} else {
T = TypeRefsFactory.eINSTANCE.createUnknownTypeRef();
}
}
}
return new Result<TypeRef>(T);
}
use of org.eclipse.n4js.ts.types.Type in project n4js by eclipse.
the class InternalTypeSystem method applyRuleTypeIndexedAccessExpression.
protected Result<TypeRef> applyRuleTypeIndexedAccessExpression(final RuleEnvironment G, final RuleApplicationTrace _trace_, final IndexedAccessExpression expr) throws RuleFailedException {
// output parameter
TypeRef T = null;
/* { expr.target === null || expr.index === null; T = TypeRefsFactory.eINSTANCE.createUnknownTypeRef } or { expr.target instanceof SuperLiteral T = TypeRefsFactory.eINSTANCE.createUnknownTypeRef } or { G |- expr.target : var TypeRef targetTypeRef targetTypeRef = typeSystemHelper.resolveType(G, targetTypeRef); G |- expr.index : var TypeRef indexTypeRef; val targetDeclType = targetTypeRef.declaredType; val targetIsLiteralOfStringBasedEnum = targetDeclType instanceof TEnum && AnnotationDefinition.STRING_BASED.hasAnnotation(targetDeclType); val indexIsNumeric = { G |- indexTypeRef <: G.numberTypeRef }; val indexValue = ASTMetaInfoUtils.getCompileTimeValue(expr.index); val memberName = N4JSLanguageUtils.derivePropertyNameFromCompileTimeValue(indexValue); if (indexIsNumeric && (targetTypeRef.isArrayLike || targetIsLiteralOfStringBasedEnum)) { if (targetDeclType.generic && targetTypeRef.typeArgs.isEmpty) { T = G.anyTypeRef } else { val G2 = G.wrap typeSystemHelper.addSubstitutions(G2, targetTypeRef) G2.addThisType(targetTypeRef) val elementTypeRef = if(targetIsLiteralOfStringBasedEnum) { G.stringType.elementType } else { targetDeclType.elementType }; G2 |- elementTypeRef ~> T } } else if (memberName!==null) { val staticAccess = (targetTypeRef instanceof TypeTypeRef) val checkVisibility = false val scope = memberScopingHelper.createMemberScope(targetTypeRef, expr, checkVisibility, staticAccess) val memberDesc = if(memberName!==null && !memberName.isEmpty()) { scope.getSingleElement(qualifiedNameConverter.toQualifiedName(memberName)) }; val member = if(memberDesc!==null && !IEObjectDescriptionWithError.isErrorDescription(memberDesc)) { memberDesc.getEObjectOrProxy() }; if(member instanceof TMember && !member.eIsProxy) { G |- (member as TMember) : var TypeRef memberTypeRef val G2 = G.wrap typeSystemHelper.addSubstitutions(G2,targetTypeRef) G2.addThisType(targetTypeRef) G2 |- memberTypeRef ~> T } else if (targetTypeRef.dynamic) { T = G.anyTypeRefDynamic } else { T = TypeRefsFactory.eINSTANCE.createUnknownTypeRef } } else if (targetTypeRef.dynamic) { T = G.anyTypeRefDynamic } else { T = G.anyTypeRef } } */
{
RuleFailedException previousFailure = null;
try {
/* expr.target === null || expr.index === null */
if (!((expr.getTarget() == null) || (expr.getIndex() == null))) {
sneakyThrowRuleFailedException("expr.target === null || expr.index === null");
}
T = TypeRefsFactory.eINSTANCE.createUnknownTypeRef();
} catch (Exception e) {
previousFailure = extractRuleFailedException(e);
/* { expr.target instanceof SuperLiteral T = TypeRefsFactory.eINSTANCE.createUnknownTypeRef } or { G |- expr.target : var TypeRef targetTypeRef targetTypeRef = typeSystemHelper.resolveType(G, targetTypeRef); G |- expr.index : var TypeRef indexTypeRef; val targetDeclType = targetTypeRef.declaredType; val targetIsLiteralOfStringBasedEnum = targetDeclType instanceof TEnum && AnnotationDefinition.STRING_BASED.hasAnnotation(targetDeclType); val indexIsNumeric = { G |- indexTypeRef <: G.numberTypeRef }; val indexValue = ASTMetaInfoUtils.getCompileTimeValue(expr.index); val memberName = N4JSLanguageUtils.derivePropertyNameFromCompileTimeValue(indexValue); if (indexIsNumeric && (targetTypeRef.isArrayLike || targetIsLiteralOfStringBasedEnum)) { if (targetDeclType.generic && targetTypeRef.typeArgs.isEmpty) { T = G.anyTypeRef } else { val G2 = G.wrap typeSystemHelper.addSubstitutions(G2, targetTypeRef) G2.addThisType(targetTypeRef) val elementTypeRef = if(targetIsLiteralOfStringBasedEnum) { G.stringType.elementType } else { targetDeclType.elementType }; G2 |- elementTypeRef ~> T } } else if (memberName!==null) { val staticAccess = (targetTypeRef instanceof TypeTypeRef) val checkVisibility = false val scope = memberScopingHelper.createMemberScope(targetTypeRef, expr, checkVisibility, staticAccess) val memberDesc = if(memberName!==null && !memberName.isEmpty()) { scope.getSingleElement(qualifiedNameConverter.toQualifiedName(memberName)) }; val member = if(memberDesc!==null && !IEObjectDescriptionWithError.isErrorDescription(memberDesc)) { memberDesc.getEObjectOrProxy() }; if(member instanceof TMember && !member.eIsProxy) { G |- (member as TMember) : var TypeRef memberTypeRef val G2 = G.wrap typeSystemHelper.addSubstitutions(G2,targetTypeRef) G2.addThisType(targetTypeRef) G2 |- memberTypeRef ~> T } else if (targetTypeRef.dynamic) { T = G.anyTypeRefDynamic } else { T = TypeRefsFactory.eINSTANCE.createUnknownTypeRef } } else if (targetTypeRef.dynamic) { T = G.anyTypeRefDynamic } else { T = G.anyTypeRef } } */
{
try {
Expression _target = expr.getTarget();
/* expr.target instanceof SuperLiteral */
if (!(_target instanceof SuperLiteral)) {
sneakyThrowRuleFailedException("expr.target instanceof SuperLiteral");
}
T = TypeRefsFactory.eINSTANCE.createUnknownTypeRef();
} catch (Exception e_1) {
previousFailure = extractRuleFailedException(e_1);
/* G |- expr.target : var TypeRef targetTypeRef */
Expression _target_1 = expr.getTarget();
TypeRef targetTypeRef = null;
Result<TypeRef> result = typeInternal(G, _trace_, _target_1);
checkAssignableTo(result.getFirst(), TypeRef.class);
targetTypeRef = (TypeRef) result.getFirst();
targetTypeRef = this.typeSystemHelper.resolveType(G, targetTypeRef);
/* G |- expr.index : var TypeRef indexTypeRef */
Expression _index = expr.getIndex();
TypeRef indexTypeRef = null;
Result<TypeRef> result_1 = typeInternal(G, _trace_, _index);
checkAssignableTo(result_1.getFirst(), TypeRef.class);
indexTypeRef = (TypeRef) result_1.getFirst();
final Type targetDeclType = targetTypeRef.getDeclaredType();
final boolean targetIsLiteralOfStringBasedEnum = ((targetDeclType instanceof TEnum) && AnnotationDefinition.STRING_BASED.hasAnnotation(targetDeclType));
/* G |- indexTypeRef <: G.numberTypeRef */
ParameterizedTypeRef _numberTypeRef = RuleEnvironmentExtensions.numberTypeRef(G);
boolean _ruleinvocation = subtypeSucceeded(G, _trace_, indexTypeRef, _numberTypeRef);
final boolean indexIsNumeric = _ruleinvocation;
final CompileTimeValue indexValue = ASTMetaInfoUtils.getCompileTimeValue(expr.getIndex());
final String memberName = N4JSLanguageUtils.derivePropertyNameFromCompileTimeValue(indexValue);
if ((indexIsNumeric && (targetTypeRef.isArrayLike() || targetIsLiteralOfStringBasedEnum))) {
if ((targetDeclType.isGeneric() && targetTypeRef.getTypeArgs().isEmpty())) {
T = RuleEnvironmentExtensions.anyTypeRef(G);
} else {
final RuleEnvironment G2 = RuleEnvironmentExtensions.wrap(G);
this.typeSystemHelper.addSubstitutions(G2, targetTypeRef);
RuleEnvironmentExtensions.addThisType(G2, targetTypeRef);
TypeRef _xifexpression = null;
if (targetIsLiteralOfStringBasedEnum) {
_xifexpression = RuleEnvironmentExtensions.stringType(G).getElementType();
} else {
_xifexpression = targetDeclType.getElementType();
}
final TypeRef elementTypeRef = _xifexpression;
/* G2 |- elementTypeRef ~> T */
Result<TypeArgument> result_2 = substTypeVariablesInternal(G2, _trace_, elementTypeRef);
checkAssignableTo(result_2.getFirst(), TypeRef.class);
T = (TypeRef) result_2.getFirst();
}
} else {
if ((memberName != null)) {
final boolean staticAccess = (targetTypeRef instanceof TypeTypeRef);
final boolean checkVisibility = false;
final IScope scope = this.memberScopingHelper.createMemberScope(targetTypeRef, expr, checkVisibility, staticAccess);
IEObjectDescription _xifexpression_1 = null;
if (((memberName != null) && (!memberName.isEmpty()))) {
_xifexpression_1 = scope.getSingleElement(this.qualifiedNameConverter.toQualifiedName(memberName));
}
final IEObjectDescription memberDesc = _xifexpression_1;
EObject _xifexpression_2 = null;
if (((memberDesc != null) && (!IEObjectDescriptionWithError.isErrorDescription(memberDesc)))) {
_xifexpression_2 = memberDesc.getEObjectOrProxy();
}
final EObject member = _xifexpression_2;
if (((member instanceof TMember) && (!member.eIsProxy()))) {
/* G |- (member as TMember) : var TypeRef memberTypeRef */
TypeRef memberTypeRef = null;
Result<TypeRef> result_3 = typeInternal(G, _trace_, ((TMember) member));
checkAssignableTo(result_3.getFirst(), TypeRef.class);
memberTypeRef = (TypeRef) result_3.getFirst();
final RuleEnvironment G2_1 = RuleEnvironmentExtensions.wrap(G);
this.typeSystemHelper.addSubstitutions(G2_1, targetTypeRef);
RuleEnvironmentExtensions.addThisType(G2_1, targetTypeRef);
/* G2 |- memberTypeRef ~> T */
Result<TypeArgument> result_4 = substTypeVariablesInternal(G2_1, _trace_, memberTypeRef);
checkAssignableTo(result_4.getFirst(), TypeRef.class);
T = (TypeRef) result_4.getFirst();
} else {
boolean _isDynamic = targetTypeRef.isDynamic();
if (_isDynamic) {
T = RuleEnvironmentExtensions.anyTypeRefDynamic(G);
} else {
T = TypeRefsFactory.eINSTANCE.createUnknownTypeRef();
}
}
} else {
boolean _isDynamic_1 = targetTypeRef.isDynamic();
if (_isDynamic_1) {
T = RuleEnvironmentExtensions.anyTypeRefDynamic(G);
} else {
T = RuleEnvironmentExtensions.anyTypeRef(G);
}
}
}
}
}
}
}
return new Result<TypeRef>(T);
}
use of org.eclipse.n4js.ts.types.Type in project n4js by eclipse.
the class BoundSet method resolveRawTypes.
/**
* Handle raw types in given type bounds.
* <p>
* Even though raw type are not supported in N4JS, they can occur during type inference due to ClassifierTypeRef.
* The basic idea of this method is that a raw type is removed if at least one non-raw type for the same declared
* type exists: if we have A and A<string> as upper bounds, we can remove A because it does not add any
* information.
* <p>
* TODO revise handling of raw types (inefficient implementation, does not handle all cases (e.g. nested raw types))
*/
private Set<TypeBound> resolveRawTypes(Set<TypeBound> typeBounds) {
if (// this is the 98% case, so we optimize for that
!haveRawTypeRef)
return typeBounds;
if (typeBounds.isEmpty())
return typeBounds;
final List<TypeBound> result = new ArrayList<>(typeBounds);
final Set<Type> genTypesWithNonRawTypeRefs = new HashSet<>();
final Set<TypeBound> boundsWithRawTypeRef = new HashSet<>();
// collect
for (TypeBound tb : result) {
if (tb.right instanceof ParameterizedTypeRef) {
final ParameterizedTypeRef ptr = (ParameterizedTypeRef) tb.right;
final Type declType = ptr.getDeclaredType();
if (declType != null && declType.isGeneric()) {
final boolean isRaw = TypeUtils.isRawTypeRef(ptr);
if (isRaw) {
boundsWithRawTypeRef.add(tb);
} else {
genTypesWithNonRawTypeRefs.add(declType);
}
}
}
}
// remove all raw type bounds for which at least 1 other non-raw typeRef exists
for (TypeBound currTB : boundsWithRawTypeRef) {
if (genTypesWithNonRawTypeRefs.contains(currTB.right.getDeclaredType())) {
result.remove(currTB);
}
}
// remaining raw types must be sanitized
final int len = result.size();
for (int i = 0; i < len; i++) {
result.set(i, result.get(i).sanitizeRawTypeRef());
}
return new LinkedHashSet<>(result);
}
use of org.eclipse.n4js.ts.types.Type in project n4js by eclipse.
the class Reducer method reduceParameterizedTypeRefNominal.
/**
* Reduction for parameterized type references according to nominal subtyping rules.
* <p>
* NOTE: 'left' might be a structural type reference iff variance == CO / 'right' might be structural (iff variance
* == CONTRA) but that is irrelevant and the reduction must still follow nominal subtyping rules (because the RHS of
* the subtype relation determines whether to use nominal or structural rules).
*/
private boolean reduceParameterizedTypeRefNominal(ParameterizedTypeRef left, ParameterizedTypeRef right, Variance variance) {
// e.g., ⟨ Iterable3<int,string,int> :> Array<α> ⟩ should be reduced to ⟨ int >: α ⟩ and ⟨ string >: α ⟩
if ((variance == CO && isSpecialCaseOfArraySubtypeIterableN(left, right)) || (variance == CONTRA && isSpecialCaseOfArraySubtypeIterableN(right, left))) {
final List<TypeArgument> typeArgsOfArray = variance == CO ? left.getTypeArgs() : right.getTypeArgs();
final List<TypeArgument> typeArgsOfIterableN = variance == CO ? right.getTypeArgs() : left.getTypeArgs();
final List<TypeVariable> typeParamsOfIterableN = variance == CO ? right.getDeclaredType().getTypeVars() : left.getDeclaredType().getTypeVars();
final TypeArgument singleTypeArgOfArray = !typeArgsOfArray.isEmpty() ? typeArgsOfArray.get(0) : null;
boolean wasAdded = false;
final int len = Math.min(typeArgsOfIterableN.size(), typeParamsOfIterableN.size());
for (int idx = 0; idx < len; idx++) {
TypeArgument currTypeArgOfIterableN = typeArgsOfIterableN.get(idx);
TypeVariable curTypeParamOfIterableN = typeParamsOfIterableN.get(idx);
wasAdded |= reduceConstraintForTypeArgumentPair(currTypeArgOfIterableN, curTypeParamOfIterableN, singleTypeArgOfArray);
}
return wasAdded;
}
// standard cases:
final TypeRef leftRaw = TypeUtils.createTypeRef(left.getDeclaredType());
// note: enforcing nominal here!
final TypeRef rightRaw = TypeUtils.createTypeRef(right.getDeclaredType());
if ((variance == CO && !ts.subtypeSucceeded(G, leftRaw, rightRaw)) || (variance == CONTRA && !ts.subtypeSucceeded(G, rightRaw, leftRaw)) || (variance == INV && !ts.equaltypeSucceeded(G, leftRaw, rightRaw))) {
return giveUp(left, right, variance);
}
// IV <-> string
if (variance == CO) {
// normalize ⟨ B <: A ⟩ to ⟨ A :> B ⟩ to make sure the (raw) subtype is on the right-hand side
final ParameterizedTypeRef tmp = left;
left = right;
right = tmp;
variance = CONTRA;
}
boolean wasAdded = false;
final RuleEnvironment Gx = RuleEnvironmentExtensions.newRuleEnvironment(G);
tsh.addSubstitutions(Gx, right);
final Type leftType = left.getDeclaredType();
final List<TypeArgument> leftArgs = left.getTypeArgs();
final List<TypeVariable> leftParams = leftType.getTypeVars();
final int len = Math.min(leftArgs.size(), leftParams.size());
for (int idx = 0; idx < len; ++idx) {
final TypeArgument leftArg = leftArgs.get(idx);
final TypeVariable leftParam = leftParams.get(idx);
if (RuleEnvironmentExtensions.hasSubstitutionFor(Gx, leftParam)) {
final TypeArgument leftParamSubst = ts.substTypeVariables(Gx, TypeUtils.createTypeRef(leftParam)).getValue();
wasAdded |= reduceConstraintForTypeArgumentPair(leftArg, leftParam, leftParamSubst);
}
}
return wasAdded;
}
Aggregations