/*
* Copyright 2014 - 2017 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.view.impl.objectbuilder.mapper;
import com.blazebit.persistence.CaseWhenStarterBuilder;
import com.blazebit.persistence.CommonQueryBuilder;
import com.blazebit.persistence.FullQueryBuilder;
import com.blazebit.persistence.MultipleSubqueryInitiator;
import com.blazebit.persistence.SelectBuilder;
import com.blazebit.persistence.SimpleCaseWhenStarterBuilder;
import com.blazebit.persistence.SubqueryBuilder;
import com.blazebit.persistence.SubqueryInitiator;
import com.blazebit.persistence.view.impl.objectbuilder.transformator.TupleTransformatorFactory;
import java.util.AbstractMap;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
*
* @author Christian Beikov
* @since 1.2.0
*/
public class ConstrainedTupleElementMapper implements TupleElementMapper {
private final Map.Entry<String, TupleElementMapper>[] mappers;
private final Map.Entry<String, TupleElementMapper>[] subqueryMappers;
private final String alias;
@SuppressWarnings("unchecked")
private ConstrainedTupleElementMapper(List<Map.Entry<String, TupleElementMapper>> mappers, List<Map.Entry<String, TupleElementMapper>> subqueryMappers, String alias) {
this.mappers = mappers.toArray(new Map.Entry[mappers.size()]);
this.subqueryMappers = subqueryMappers.toArray(new Map.Entry[subqueryMappers.size()]);
this.alias = alias;
}
@Override
public void applyMapping(SelectBuilder<?> queryBuilder, CommonQueryBuilder<?> parameterSource, Map<String, Object> optionalParameters) {
StringBuilder sb = new StringBuilder();
StringBuilderSelectBuilder selectBuilder = new StringBuilderSelectBuilder(sb);
sb.append("CASE");
for (Map.Entry<String, TupleElementMapper> entry : mappers) {
if (entry.getKey() != null) {
sb.append(" WHEN ");
sb.append(entry.getKey());
sb.append(" THEN ");
} else {
sb.append(" ELSE ");
}
entry.getValue().applyMapping(selectBuilder, parameterSource, optionalParameters);
}
sb.append(" END");
if (subqueryMappers.length == 0) {
if (alias != null) {
queryBuilder.select(sb.toString(), alias);
} else {
queryBuilder.select(sb.toString());
}
} else {
MultipleSubqueryInitiator<?> initiator;
if (alias != null) {
initiator = queryBuilder.selectSubqueries(sb.toString(), alias);
} else {
initiator = queryBuilder.selectSubqueries(sb.toString());
}
for (Map.Entry<String, TupleElementMapper> entry : subqueryMappers) {
selectBuilder.setInitiator(initiator.with(entry.getKey()));
entry.getValue().applyMapping(selectBuilder, parameterSource, optionalParameters);
}
initiator.end();
}
}
public static void addMappers(List<TupleElementMapper> mappingList, List<String> parameterMappingList, TupleTransformatorFactory tupleTransformatorFactory, List<Map.Entry<String, TupleElementMapperBuilder>> builders) {
List<List<Map.Entry<String, TupleElementMapper>>> subtypeMappersPerAttribute = new ArrayList<>();
boolean first = true;
// Transpose the subtype grouped attribute lists to attribute grouped subtype lists
for (Map.Entry<String, TupleElementMapperBuilder> builderEntry : builders) {
String constraint = builderEntry.getKey();
TupleElementMapperBuilder mapperBuilder = builderEntry.getValue();
// NOTE: we assume that constrained attributes have the same attribute types so we only use the transformators of the first mapper builder
if (first) {
tupleTransformatorFactory.add(mapperBuilder.getTupleTransformatorFactory());
first = false;
}
int attributeIndex = 0;
for (TupleElementMapper mapper : mapperBuilder.getMappers()) {
List<Map.Entry<String, TupleElementMapper>> list = subtypeMappersPerAttribute.size() > attributeIndex ? subtypeMappersPerAttribute.get(attributeIndex) : null;
if (list == null) {
list = new ArrayList<>();
subtypeMappersPerAttribute.add(attributeIndex, list);
}
list.add(new AbstractMap.SimpleEntry<>(constraint, mapper));
attributeIndex++;
}
}
// Split up subquery mappers from the rest since they need special handling
for (List<Map.Entry<String, TupleElementMapper>> attributeEntry : subtypeMappersPerAttribute) {
List<Map.Entry<String, TupleElementMapper>> mappers = new ArrayList<>();
List<Map.Entry<String, TupleElementMapper>> subqueryMappers = new ArrayList<>();
for (Map.Entry<String, TupleElementMapper> subtypeEntry : attributeEntry) {
String constraint = subtypeEntry.getKey();
TupleElementMapper mapper = subtypeEntry.getValue();
if (mapper instanceof SubqueryTupleElementMapper) {
SubqueryTupleElementMapper subqueryMapper = (SubqueryTupleElementMapper) mapper;
String subqueryAlias = "_inheritance_subquery_" + subqueryMappers.size();
String subqueryExpression;
if (subqueryMapper.getSubqueryAlias() == null) {
subqueryExpression = subqueryAlias;
} else {
subqueryExpression = subqueryMapper.getSubqueryExpression().replaceAll(subqueryMapper.getSubqueryAlias(), subqueryAlias);
}
mappers.add(new AbstractMap.SimpleEntry<String, TupleElementMapper>(constraint, new ExpressionTupleElementMapper(subqueryExpression)));
subqueryMappers.add(new AbstractMap.SimpleEntry<>(subqueryAlias, mapper));
} else {
mappers.add(new AbstractMap.SimpleEntry<>(constraint, mapper));
}
}
// TODO: determine alias
String alias = null;
mappingList.add(new ConstrainedTupleElementMapper(mappers, subqueryMappers, alias));
parameterMappingList.add(null);
}
}
private static class StringBuilderSelectBuilder implements SelectBuilder<Object> {
private final StringBuilder sb;
private SubqueryInitiator<Object> initiator;
public StringBuilderSelectBuilder(StringBuilder sb) {
this.sb = sb;
}
@SuppressWarnings("unchecked")
public void setInitiator(SubqueryInitiator<?> initiator) {
this.initiator = (SubqueryInitiator<Object>) initiator;
}
@Override
public CaseWhenStarterBuilder<Object> selectCase() {
throw new UnsupportedOperationException();
}
@Override
public CaseWhenStarterBuilder<Object> selectCase(String alias) {
throw new UnsupportedOperationException();
}
@Override
public SimpleCaseWhenStarterBuilder<Object> selectSimpleCase(String caseOperand) {
throw new UnsupportedOperationException();
}
@Override
public SimpleCaseWhenStarterBuilder<Object> selectSimpleCase(String caseOperand, String alias) {
throw new UnsupportedOperationException();
}
@Override
public SubqueryInitiator<Object> selectSubquery() {
return initiator;
}
@Override
public SubqueryInitiator<Object> selectSubquery(String alias) {
return initiator;
}
@Override
public SubqueryInitiator<Object> selectSubquery(String subqueryAlias, String expression, String selectAlias) {
return initiator;
}
@Override
public SubqueryInitiator<Object> selectSubquery(String subqueryAlias, String expression) {
return initiator;
}
@Override
public MultipleSubqueryInitiator<Object> selectSubqueries(String expression, String selectAlias) {
throw new UnsupportedOperationException();
}
@Override
public MultipleSubqueryInitiator<Object> selectSubqueries(String expression) {
throw new UnsupportedOperationException();
}
@Override
public SubqueryBuilder<Object> selectSubquery(FullQueryBuilder<?, ?> criteriaBuilder) {
throw new UnsupportedOperationException();
}
@Override
public SubqueryBuilder<Object> selectSubquery(String alias, FullQueryBuilder<?, ?> criteriaBuilder) {
throw new UnsupportedOperationException();
}
@Override
public SubqueryBuilder<Object> selectSubquery(String subqueryAlias, String expression, String selectAlias, FullQueryBuilder<?, ?> criteriaBuilder) {
throw new UnsupportedOperationException();
}
@Override
public SubqueryBuilder<Object> selectSubquery(String subqueryAlias, String expression, FullQueryBuilder<?, ?> criteriaBuilder) {
throw new UnsupportedOperationException();
}
@Override
public Object select(String expression) {
sb.append(expression);
return this;
}
@Override
public Object select(String expression, String alias) {
sb.append(expression);
return this;
}
}
}