Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
com.blazebit.persistence.criteria.impl.InternalQuery Maven / Gradle / Ivy
/*
* Copyright 2014 - 2021 Blazebit.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.blazebit.persistence.criteria.impl;
import com.blazebit.persistence.BaseSubqueryBuilder;
import com.blazebit.persistence.CriteriaBuilder;
import com.blazebit.persistence.FromBuilder;
import com.blazebit.persistence.FullQueryBuilder;
import com.blazebit.persistence.GroupByBuilder;
import com.blazebit.persistence.HavingBuilder;
import com.blazebit.persistence.JoinOnBuilder;
import com.blazebit.persistence.JoinType;
import com.blazebit.persistence.MultipleSubqueryInitiator;
import com.blazebit.persistence.OrderByBuilder;
import com.blazebit.persistence.SelectBuilder;
import com.blazebit.persistence.SelectObjectBuilder;
import com.blazebit.persistence.SubqueryBuilder;
import com.blazebit.persistence.SubqueryInitiator;
import com.blazebit.persistence.WhereBuilder;
import com.blazebit.persistence.criteria.BlazeAbstractQuery;
import com.blazebit.persistence.criteria.BlazeJoin;
import com.blazebit.persistence.criteria.BlazeOrder;
import com.blazebit.persistence.criteria.BlazeRoot;
import com.blazebit.persistence.criteria.BlazeSubquery;
import com.blazebit.persistence.criteria.impl.RenderContext.ClauseType;
import com.blazebit.persistence.criteria.impl.expression.AbstractSelection;
import com.blazebit.persistence.criteria.impl.expression.SubqueryExpression;
import com.blazebit.persistence.criteria.impl.path.AbstractFrom;
import com.blazebit.persistence.criteria.impl.path.AbstractJoin;
import com.blazebit.persistence.criteria.impl.path.EntityJoin;
import com.blazebit.persistence.criteria.impl.path.RootImpl;
import com.blazebit.persistence.criteria.impl.path.TreatedPath;
import javax.persistence.Tuple;
import javax.persistence.criteria.Expression;
import javax.persistence.criteria.Join;
import javax.persistence.criteria.Order;
import javax.persistence.criteria.ParameterExpression;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import javax.persistence.criteria.Selection;
import javax.persistence.criteria.Subquery;
import javax.persistence.metamodel.Attribute;
import javax.persistence.metamodel.EntityType;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* @author Christian Beikov
* @since 1.2.0
*/
public class InternalQuery implements Serializable {
private static final long serialVersionUID = 1L;
private final BlazeAbstractQuery owner;
private final BlazeCriteriaBuilderImpl criteriaBuilder;
private final boolean isSubQuery;
private boolean distinct;
private Selection selection;
private final Set> roots = new LinkedHashSet<>();
private Set> correlationRoots;
private Predicate restriction;
private List> groupList = Collections.emptyList();
private Predicate having;
private List orderList = Collections.emptyList();
private List> subqueries;
public InternalQuery(BlazeAbstractQuery owner, BlazeCriteriaBuilderImpl criteriaBuilder) {
this.owner = owner;
this.criteriaBuilder = criteriaBuilder;
this.isSubQuery = owner instanceof Subquery;
}
/* Select */
public boolean isDistinct() {
return distinct;
}
public void setDistinct(boolean distinct) {
this.distinct = distinct;
}
@SuppressWarnings("unchecked")
public Selection getSelection() {
return (Selection) selection;
}
public void setSelection(Selection selection) {
// NOTE: checks for duplicate aliases and predicate selects are already done
this.selection = selection;
}
/* From */
@SuppressWarnings("unchecked")
public Set> getRoots() {
return (Set>) (Set) roots;
}
@SuppressWarnings("unchecked")
public Set> getBlazeRoots() {
return (Set>) (Set) roots;
}
public BlazeRoot from(Class entityClass, String alias) {
EntityType entityType = criteriaBuilder.getEntityMetamodel().entity(entityClass);
if (entityType == null) {
throw new IllegalArgumentException(entityClass + " is not an entity");
}
return from(entityType, alias);
}
public BlazeRoot from(EntityType entityType, String alias) {
RootImpl root = new RootImpl(criteriaBuilder, entityType, alias, true);
roots.add(root);
return root;
}
/* Correlation */
public void addCorrelationRoot(AbstractFrom fromImplementor) {
if (!isSubQuery) {
throw new IllegalStateException("Query is not identified as sub-query");
}
if (correlationRoots == null) {
correlationRoots = new HashSet>();
}
correlationRoots.add(fromImplementor);
}
public Set> collectCorrelatedJoins() {
if (!isSubQuery) {
throw new IllegalStateException("Query is not identified as sub-query");
}
final Set> correlatedJoins;
if (correlationRoots != null) {
correlatedJoins = new HashSet>();
for (AbstractFrom correlationRoot : correlationRoots) {
correlatedJoins.addAll(correlationRoot.getJoins());
}
} else {
correlatedJoins = Collections.emptySet();
}
return correlatedJoins;
}
/* Where */
public Predicate getRestriction() {
return restriction;
}
public void setRestriction(Predicate restriction) {
this.restriction = restriction;
}
/* Group by */
public List> getGroupList() {
return groupList;
}
public void setGroupList(List> groupList) {
this.groupList = groupList;
}
/* Having */
public Predicate getGroupRestriction() {
return having;
}
public void setHaving(Predicate having) {
this.having = having;
}
/* Order by */
public List getBlazeOrderList() {
return orderList;
}
public void setBlazeOrderList(List orderList) {
this.orderList = orderList;
}
@SuppressWarnings({"unchecked"})
public List getOrderList() {
return (List) (List) orderList;
}
@SuppressWarnings({"unchecked"})
public void setOrderList(List orderList) {
this.orderList = (List) (List) orderList;
}
/* Parameters */
public Set> getParameters() {
// NOTE: we have to always visit them because it's not possible to cache that easily
ParameterVisitor visitor = new ParameterVisitor();
visitor.visit(selection);
visitor.visit(restriction);
if (subqueries != null) {
for (Subquery subquery : subqueries) {
visitor.visit(subquery);
}
}
for (RootImpl r : roots) {
r.visit(visitor);
}
for (AbstractFrom r : correlationRoots) {
r.visit(visitor);
}
visitor.visit(having);
if (groupList != null) {
for (Expression grouping : groupList) {
visitor.visit(grouping);
}
}
if (orderList != null) {
for (Order ordering : orderList) {
visitor.visit(ordering.getExpression());
}
}
return visitor.getParameters();
}
/* Subquery */
public List> internalGetSubqueries() {
if (subqueries == null) {
subqueries = new ArrayList>();
}
return subqueries;
}
public BlazeSubquery subquery(Class subqueryType) {
SubqueryExpression subquery = new SubqueryExpression(criteriaBuilder, subqueryType, owner);
internalGetSubqueries().add(subquery);
return subquery;
}
/* Rendering */
public CriteriaBuilder render(CriteriaBuilder cb) {
if (distinct) {
cb.distinct();
}
RenderContextImpl context = new RenderContextImpl();
renderFrom(cb, context);
List> treatedSelections = renderSelect(cb, context);
renderWhere(cb, context, treatedSelections);
renderGroupBy(cb, context);
renderHaving(cb, context);
renderOrderBy(cb, context);
for (ImplicitParameterBinding b : context.getImplicitParameterBindings()) {
b.bind(cb);
}
for (Map.Entry> entry : context.getExplicitParameterNameMapping().entrySet()) {
cb.setParameterType(entry.getKey(), entry.getValue().getParameterType());
}
return cb;
}
public void renderSubquery(RenderContext context) {
RenderContextImpl contextImpl = (RenderContextImpl) context;
SubqueryInitiator initiator = context.getSubqueryInitiator();
SubqueryBuilder cb = renderSubqueryFrom(initiator, contextImpl);
if (distinct) {
cb.distinct();
}
List> treatedSelections = renderSelect(cb, contextImpl);
renderWhere(cb, contextImpl, treatedSelections);
renderGroupBy(cb, contextImpl);
renderHaving(cb, contextImpl);
renderOrderBy(cb, contextImpl);
cb.end();
}
private List> renderSelect(SelectBuilder cb, final RenderContextImpl context) {
if (selection == null) {
return Collections.emptyList();
}
final List> treatedSelections = new ArrayList<>();
context.setClauseType(ClauseType.SELECT);
if (selection.isCompoundSelection()) {
Class selectionType = selection.getJavaType();
if (selectionType.isArray()) {
for (Selection s : selection.getCompoundSelectionItems()) {
renderSelection(cb, context, s, treatedSelections);
}
} else if (Tuple.class.isAssignableFrom(selectionType)) {
if (cb instanceof CriteriaBuilder) {
((CriteriaBuilder) cb).selectNew(new JpaTupleObjectBuilder(criteriaBuilder, selection.getCompoundSelectionItems()) {
@Override
protected void renderSelection(SelectBuilder cb, Selection s) {
InternalQuery.this.renderSelection(cb, context, s, treatedSelections);
}
});
} else {
for (Selection s : selection.getCompoundSelectionItems()) {
renderSelection(cb, context, s, treatedSelections);
}
}
} else {
if (!(cb instanceof FullQueryBuilder)) {
throw new IllegalArgumentException("Invalid subquery found that uses select new!");
}
SelectObjectBuilder b = ((FullQueryBuilder) cb).selectNew(selectionType);
for (Selection s : selection.getCompoundSelectionItems()) {
if (s instanceof Subquery) {
if (s.getAlias() != null) {
context.pushSubqueryInitiator(b.withSubquery(s.getAlias()));
} else {
context.pushSubqueryInitiator(b.withSubquery());
}
((SubqueryExpression) s).renderSubquery(context);
context.popSubqueryInitiator();
} else {
((AbstractSelection) s).render(context);
String expr = context.takeBuffer();
Map> aliasToSubqueries = context.takeAliasToSubqueryMap();
if (aliasToSubqueries.isEmpty()) {
if (s.getAlias() != null && !(s instanceof AbstractFrom)) {
b.with(expr, s.getAlias());
} else {
b.with(expr);
}
} else {
MultipleSubqueryInitiator initiator;
if (s.getAlias() != null) {
initiator = b.withSubqueries(expr, s.getAlias());
} else {
initiator = b.withSubqueries(expr);
}
for (Map.Entry> subqueryEntry : aliasToSubqueries.entrySet()) {
context.pushSubqueryInitiator(initiator.with(subqueryEntry.getKey()));
subqueryEntry.getValue().renderSubquery(context);
context.popSubqueryInitiator();
}
initiator.end();
}
}
}
b.end();
}
} else {
renderSelection(cb, context, selection, treatedSelections);
}
return treatedSelections;
}
private void renderSelection(SelectBuilder cb, RenderContextImpl context, Selection s, List> treatedSelections) {
if (s instanceof Subquery) {
if (s.getAlias() != null) {
context.pushSubqueryInitiator(cb.selectSubquery(s.getAlias()));
} else {
context.pushSubqueryInitiator(cb.selectSubquery());
}
((SubqueryExpression) s).renderSubquery(context);
context.popSubqueryInitiator();
} else {
if (s instanceof TreatedPath) {
TreatedPath treatedPath = (TreatedPath) s;
treatedSelections.add(treatedPath);
treatedPath.getTreatedPath().render(context);
} else {
((AbstractSelection) s).render(context);
}
String expr = context.takeBuffer();
Map> aliasToSubqueries = context.takeAliasToSubqueryMap();
if (aliasToSubqueries.isEmpty()) {
if (s.getAlias() != null && !(s instanceof AbstractFrom)) {
cb.select(expr, s.getAlias());
} else {
cb.select(expr);
}
} else {
MultipleSubqueryInitiator initiator;
if (s.getAlias() != null) {
initiator = cb.selectSubqueries(expr, s.getAlias());
} else {
initiator = cb.selectSubqueries(expr);
}
for (Map.Entry> subqueryEntry : aliasToSubqueries.entrySet()) {
context.pushSubqueryInitiator(initiator.with(subqueryEntry.getKey()));
subqueryEntry.getValue().renderSubquery(context);
context.popSubqueryInitiator();
}
initiator.end();
}
}
}
private void renderFrom(FromBuilder cb, RenderContextImpl context) {
context.setClauseType(ClauseType.FROM);
for (BlazeRoot r : roots) {
((AbstractFrom) r).prepareAlias(context);
if (r.getAlias() != null) {
cb.from(r.getModel(), r.getAlias());
} else {
cb.from(r.getModel());
}
}
for (RootImpl r : roots) {
renderJoins(cb, context, r, true);
}
}
@SuppressWarnings("unchecked")
private void renderJoins(FromBuilder cb, RenderContextImpl context, AbstractFrom r, boolean fetching) {
String path;
if (r.getAlias() != null) {
path = r.getAlias();
} else {
path = "";
}
renderJoins(cb, null, true, context, path, (Set>) (Set) r.getBlazeJoins());
Collection> treatedPaths = (Collection>) (Collection) r.getTreatedPaths();
if (treatedPaths != null && treatedPaths.size() > 0) {
for (TreatedPath treatedPath : treatedPaths) {
RootImpl treatedRoot = (RootImpl) treatedPath;
String treatedParentPath = "TREAT(" + path + " AS " + treatedPath.getTreatType().getName() + ')';
renderJoins(cb, null, fetching, context, treatedParentPath, (Set>) (Set) treatedRoot.getBlazeJoins());
}
}
}
@SuppressWarnings({"rawtypes", "unchecked"})
private SubqueryBuilder renderSubqueryFrom(SubqueryInitiator initiator, RenderContextImpl context) {
SubqueryBuilder cb = null;
context.setClauseType(ClauseType.FROM);
for (RootImpl r : roots) {
r.prepareAlias(context);
if (cb == null) {
if (r.getAlias() != null) {
cb = initiator.from(r.getJavaType(), r.getAlias());
} else {
cb = initiator.from(r.getJavaType());
}
} else {
if (r.getAlias() != null) {
cb.from(r.getJavaType(), r.getAlias());
} else {
cb.from(r.getJavaType());
}
}
}
if (correlationRoots != null) {
for (AbstractFrom r : correlationRoots) {
r.prepareAlias(context);
Set> joins = (Set>) (Set) r.getBlazeJoins();
for (BlazeJoin j : joins) {
AbstractJoin join = (AbstractJoin) j;
join.prepareAlias(context);
EntityType treatJoinType = join.getTreatJoinType();
String path = getPath(r.getAlias(), j, treatJoinType);
if (j.getAttribute() != null && j.getAttribute().getPersistentAttributeType() == Attribute.PersistentAttributeType.EMBEDDED) {
cb = (SubqueryBuilder) renderJoins(cb, initiator, false, context, path, (Set>) (Set) j.getBlazeJoins());
} else {
if (cb == null) {
if (j.getAlias() != null) {
cb = initiator.from(path, j.getAlias());
} else {
cb = initiator.from(path);
}
} else {
if (j.getAlias() != null) {
cb.from(path, j.getAlias());
} else {
cb.from(path);
}
}
}
}
}
}
for (RootImpl r : roots) {
renderJoins(cb, context, r, false);
}
if (correlationRoots != null) {
for (AbstractFrom r : correlationRoots) {
Set> joins = (Set>) (Set) r.getBlazeJoins();
for (BlazeJoin j : joins) {
// We already rendered correlation joins for embedded paths
if (j.getAttribute() != null && j.getAttribute().getPersistentAttributeType() != Attribute.PersistentAttributeType.EMBEDDED) {
renderJoins(cb, context, (AbstractFrom) j, false);
}
}
}
}
return cb;
}
@SuppressWarnings({"rawtypes", "unchecked"})
private FromBuilder renderJoins(FromBuilder cb, SubqueryInitiator subqueryInitiator, boolean fetching, RenderContextImpl context, String parentPath, Set> joins) {
if (joins.isEmpty()) {
return cb;
}
for (BlazeJoin j : joins) {
AbstractJoin join = (AbstractJoin) j;
EntityType treatJoinType = join.getTreatJoinType();
join.prepareAlias(context);
// TODO: implicit joins?
String path = getPath(parentPath, j, treatJoinType);
String alias = j.getAlias();
JoinOnBuilder onBuilder = null;
// "Join" relations in embeddables
if (j.getAttribute() != null && j.getAttribute().getPersistentAttributeType() == Attribute.PersistentAttributeType.EMBEDDED) {
alias = path;
} else {
if (j.getOn() != null) {
if (fetching && j.isFetch()) {
throw new IllegalArgumentException("Fetch joining with on-condition is not allowed!" + j);
} else if (j instanceof EntityJoin) {
onBuilder = cb.joinOn(path, (EntityType) j.getModel(), alias, getJoinType(j.getJoinType()));
} else {
onBuilder = cb.joinOn(path, alias, getJoinType(j.getJoinType()));
}
} else {
if (fetching && j.isFetch()) {
((FullQueryBuilder) cb).join(path, alias, getJoinType(j.getJoinType()), true);
} else if (j instanceof EntityJoin) {
throw new IllegalArgumentException("Entity join without on-condition is not allowed! " + j);
} else if (cb == null) {
cb = subqueryInitiator.from(path, alias);
} else if (cb instanceof BaseSubqueryBuilder && j.isCorrelated()) {
((SubqueryBuilder) cb).from(path, alias);
} else {
cb.join(path, alias, getJoinType(j.getJoinType()));
}
}
}
if (onBuilder != null) {
context.setClauseType(ClauseType.ON);
context.getBuffer().setLength(0);
((AbstractSelection) j.getOn()).render(context);
String expression = context.takeBuffer();
Map> aliasToSubqueries = context.takeAliasToSubqueryMap();
if (aliasToSubqueries.isEmpty()) {
onBuilder.setOnExpression(expression);
} else {
MultipleSubqueryInitiator initiator = onBuilder.setOnExpressionSubqueries(expression);
for (Map.Entry> subqueryEntry : aliasToSubqueries.entrySet()) {
context.pushSubqueryInitiator(initiator.with(subqueryEntry.getKey()));
subqueryEntry.getValue().renderSubquery(context);
context.popSubqueryInitiator();
}
initiator.end();
}
}
renderJoins(cb, null, fetching, context, alias, (Set>) (Set) j.getBlazeJoins());
Collection> treatedPaths = (Collection>) (Collection) join.getTreatedPaths();
if (treatedPaths != null && treatedPaths.size() > 0) {
for (TreatedPath treatedPath : treatedPaths) {
AbstractJoin treatedJoin = (AbstractJoin) treatedPath;
String treatedParentPath = "TREAT(" + alias + " AS " + treatedPath.getTreatType().getName() + ')';
renderJoins(cb, null, fetching, context, treatedParentPath, (Set>) (Set) treatedJoin.getBlazeJoins());
}
}
}
return cb;
}
private String getPath(String parentPath, BlazeJoin j, EntityType treatJoinType) {
if (j.getAttribute() == null) {
return parentPath;
}
String path = j.getAttribute().getName();
if (parentPath == null || parentPath.isEmpty()) {
if (treatJoinType != null) {
return "TREAT(" + path + " AS " + treatJoinType.getName() + ')';
} else {
return path;
}
}
if (treatJoinType != null) {
return "TREAT(" + parentPath + "." + path + " AS " + treatJoinType.getName() + ')';
} else {
return parentPath + "." + path;
}
}
private JoinType getJoinType(javax.persistence.criteria.JoinType joinType) {
switch (joinType) {
case INNER:
return JoinType.INNER;
case LEFT:
return JoinType.LEFT;
case RIGHT:
return JoinType.RIGHT;
default:
throw new IllegalArgumentException("Unsupported join type: " + joinType);
}
}
private void renderWhere(WhereBuilder wb, RenderContextImpl context, List> treatedSelections) {
if (restriction == null) {
if (!treatedSelections.isEmpty()) {
renderTreatTypeRestrictions(context, treatedSelections);
String expression = context.takeBuffer();
wb.setWhereExpression(expression);
}
return;
}
context.setClauseType(ClauseType.WHERE);
context.getBuffer().setLength(0);
((AbstractSelection) restriction).render(context);
renderTreatTypeRestrictions(context, treatedSelections);
String expression = context.takeBuffer();
Map> aliasToSubqueries = context.takeAliasToSubqueryMap();
if (aliasToSubqueries.isEmpty()) {
wb.setWhereExpression(expression);
} else {
MultipleSubqueryInitiator initiator = wb.setWhereExpressionSubqueries(expression);
for (Map.Entry> subqueryEntry : aliasToSubqueries.entrySet()) {
context.pushSubqueryInitiator(initiator.with(subqueryEntry.getKey()));
subqueryEntry.getValue().renderSubquery(context);
context.popSubqueryInitiator();
}
initiator.end();
}
}
private void renderTreatTypeRestrictions(RenderContextImpl context, List> treatedSelections) {
final StringBuilder buffer = context.getBuffer();
boolean first = buffer.length() == 0;
for (TreatedPath p : treatedSelections) {
if (first) {
first = false;
} else {
buffer.append(" AND ");
}
buffer.append("TYPE(")
.append(p.getAlias())
.append(") = ")
.append(p.getTreatType().getName());
}
}
private void renderGroupBy(GroupByBuilder gb, RenderContextImpl context) {
if (groupList == null) {
return;
}
context.setClauseType(ClauseType.GROUP_BY);
for (Expression expr : groupList) {
context.getBuffer().setLength(0);
((AbstractSelection) expr).render(context);
String expression = context.takeBuffer();
Map> aliasToSubqueries = context.takeAliasToSubqueryMap();
if (aliasToSubqueries.isEmpty()) {
gb.groupBy(expression);
} else {
throw new IllegalArgumentException("Subqueries are not supported in the group by clause!");
// MultipleSubqueryInitiator initiator = gb.groupBySubqueries(expression);
//
// for (Map.Entry> subqueryEntry : aliasToSubqueries.entrySet()) {
// context.pushSubqueryInitiator(initiator.with(subqueryEntry.getKey()));
// subqueryEntry.getValue().renderSubquery(context);
// context.popSubqueryInitiator();
// }
//
// initiator.end();
}
}
}
private void renderHaving(HavingBuilder hb, RenderContextImpl context) {
if (having == null) {
return;
}
context.setClauseType(ClauseType.HAVING);
context.getBuffer().setLength(0);
((AbstractSelection) having).render(context);
String expression = context.takeBuffer();
Map> aliasToSubqueries = context.takeAliasToSubqueryMap();
if (aliasToSubqueries.isEmpty()) {
hb.setHavingExpression(expression);
} else {
MultipleSubqueryInitiator initiator = hb.setHavingExpressionSubqueries(expression);
for (Map.Entry> subqueryEntry : aliasToSubqueries.entrySet()) {
context.pushSubqueryInitiator(initiator.with(subqueryEntry.getKey()));
subqueryEntry.getValue().renderSubquery(context);
context.popSubqueryInitiator();
}
initiator.end();
}
}
private void renderOrderBy(OrderByBuilder ob, RenderContextImpl context) {
if (orderList == null) {
return;
}
context.setClauseType(ClauseType.ORDER_BY);
for (Order order : orderList) {
context.getBuffer().setLength(0);
((AbstractSelection) order.getExpression()).render(context);
String expression = context.takeBuffer();
Map> aliasToSubqueries = context.takeAliasToSubqueryMap();
if (aliasToSubqueries.isEmpty()) {
boolean nullsFirst = false;
if (order instanceof BlazeOrder) {
nullsFirst = ((BlazeOrder) order).isNullsFirst();
}
ob.orderBy(expression, order.isAscending(), nullsFirst);
} else {
throw new IllegalArgumentException("Subqueries are not supported in the order by clause!");
// MultipleSubqueryInitiator initiator = ob.groupBySubqueries(expression);
//
// for (Map.Entry> subqueryEntry : aliasToSubqueries.entrySet()) {
// context.pushSubqueryInitiator(initiator.with(subqueryEntry.getKey()));
// subqueryEntry.getValue().renderSubquery(context);
// context.popSubqueryInitiator();
// }
//
// initiator.end();
}
}
}
}