/**
*
* Copyright (c) 2006-2017, Speedment, Inc. All Rights Reserved.
*
* 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.speedment.runtime.core.internal.stream.builder.streamterminator;
import com.speedment.runtime.core.component.sql.SqlStreamOptimizerInfo;
import com.speedment.runtime.core.db.AsynchronousQueryResult;
import com.speedment.runtime.core.db.DbmsType;
import com.speedment.runtime.core.db.FieldPredicateView;
import com.speedment.runtime.core.db.SqlPredicateFragment;
import com.speedment.runtime.core.internal.stream.builder.action.reference.FilterAction;
import com.speedment.runtime.core.internal.stream.builder.action.reference.SortedComparatorAction;
import com.speedment.runtime.core.internal.util.Cast;
import com.speedment.runtime.core.stream.Pipeline;
import com.speedment.runtime.core.stream.action.Action;
import com.speedment.runtime.field.Field;
import com.speedment.runtime.field.comparator.FieldComparator;
import com.speedment.runtime.field.internal.predicate.AbstractCombinedPredicate;
import com.speedment.runtime.field.predicate.CombinedPredicate;
import com.speedment.runtime.field.predicate.FieldPredicate;
import com.speedment.runtime.typemapper.TypeMapper;
import java.util.ArrayList;
import java.util.List;
import static java.util.Objects.requireNonNull;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Function;
import java.util.function.Predicate;
import static java.util.stream.Collectors.joining;
import static java.util.stream.Collectors.toList;
/**
*
* @author pemi
*/
public final class StreamTerminatorUtil {
public static <T extends Pipeline, ENTITY> List<FieldPredicate<ENTITY>> topLevelAndPredicates(T initialPipeline) {
final List<FieldPredicate<ENTITY>> andPredicateBuilders = new ArrayList<>();
for (final Action<?, ?> action : initialPipeline) {
@SuppressWarnings("rawtypes")
final Optional<FilterAction> oFilterAction = Cast.cast(action, FilterAction.class);
if (oFilterAction.isPresent()) {
@SuppressWarnings("unchecked")
final List<FieldPredicate<ENTITY>> newAndPredicates = andPredicates(oFilterAction.get());
andPredicateBuilders.addAll(newAndPredicates);
} else {
break; // We can only do initial consecutive FilterAction(s)
}
}
return andPredicateBuilders;
}
@SuppressWarnings({"unchecked", "rawtypes"})
public static <ENTITY> List<FieldPredicate<?>> andPredicates(FilterAction<ENTITY> action) {
requireNonNull(action);
final List<FieldPredicate<?>> andPredicateBuilders = new ArrayList<>();
final Predicate<? super ENTITY> predicate = action.getPredicate();
final Optional<FieldPredicate> oPredicateBuilder = Cast.cast(predicate, FieldPredicate.class);
if (oPredicateBuilder.isPresent()) {
andPredicateBuilders.add(oPredicateBuilder.get()); // Just a top level predicate builder
} else {
final Optional<AbstractCombinedPredicate.AndCombinedBasePredicate> oAndCombinedBasePredicate = Cast.cast(predicate, AbstractCombinedPredicate.AndCombinedBasePredicate.class);
if (oAndCombinedBasePredicate.isPresent()) {
final AbstractCombinedPredicate.AndCombinedBasePredicate<ENTITY> andCombinedBasePredicate = (AbstractCombinedPredicate.AndCombinedBasePredicate<ENTITY>) oAndCombinedBasePredicate.get();
andCombinedBasePredicate.stream()
.map(p -> Cast.cast(p, FieldPredicate.class))
.filter(p -> p.isPresent())
.map(Optional::get)
.forEachOrdered(andPredicateBuilders::add);
}
}
return andPredicateBuilders;
}
public static <ENTITY> boolean isContainingOnlyFieldPredicate(Predicate<ENTITY> predicate) {
if (predicate instanceof FieldPredicate) {
return true;
} else if (predicate instanceof CombinedPredicate) {
return ((CombinedPredicate<ENTITY>) predicate).stream().allMatch(StreamTerminatorUtil::isContainingOnlyFieldPredicate);
}
return false;
}
public static boolean isSortedActionWithFieldPredicate(Action<?, ?> action) {
if (action instanceof SortedComparatorAction) {
if (((SortedComparatorAction) action).getComparator() instanceof FieldComparator) {
return true;
}
}
return false;
}
public static <ENTITY> void modifySource(
final List<FieldPredicate<ENTITY>> predicateBuilders,
final SqlStreamOptimizerInfo<ENTITY> info,
final AsynchronousQueryResult<ENTITY> query
) {
requireNonNull(predicateBuilders);
requireNonNull(info);
requireNonNull(query);
final FieldPredicateView spv = info.getDbmsType().getFieldPredicateView();
final List<SqlPredicateFragment> fragments = predicateBuilders.stream()
.map(sp -> spv.transform(info.getSqlColumnNamer(), info.getSqlDatabaseTypeFunction(), sp))
.collect(toList());
final String sql = info.getSqlSelect() + " WHERE "
+ fragments.stream()
.map(SqlPredicateFragment::getSql)
.collect(joining(" AND "));
final List<Object> values = new ArrayList<>();
for (int i = 0; i < fragments.size(); i++) {
final FieldPredicate<ENTITY> p = predicateBuilders.get(i);
final Field<ENTITY> referenceFieldTrait = p.getField();
@SuppressWarnings("unchecked")
final TypeMapper<Object, Object> tm = (TypeMapper<Object, Object>) referenceFieldTrait.typeMapper();
fragments.get(i).objects()
.map(tm::toDatabaseType)
.forEach(values::add);
}
query.setSql(sql);
query.setValues(values);
}
public interface RenderResult {
String getSql();
List<Object> getValues();
//Pipeline getPipeline();
}
private static final class RenderResultImpl implements RenderResult {
private final String sql;
private final List<Object> values;
public RenderResultImpl(String sql, List<Object> values /*, Pipeline pipeline*/) {
this.sql = sql;
this.values = values;
}
@Override
public String getSql() {
return sql;
}
@Override
public List<Object> getValues() {
return values;
}
@Override
public String toString() {
return String.format("RenderResultImpl {sql=%s, values=%s}", sql, values);
}
}
public static <T extends Pipeline, ENTITY> RenderResult renderSqlWhere(
final DbmsType dbmsType,
final Function<Field<ENTITY>, String> columnNamer,
final Function<Field<ENTITY>, Class<?>> columnDbTypeFunction,
final List<Predicate<ENTITY>> predicates
) {
final FieldPredicateView predicateView = dbmsType.getFieldPredicateView();
final StringBuilder sql = new StringBuilder();
final List<Object> values = new ArrayList<>();
final AtomicInteger cnt = new AtomicInteger();
predicates.forEach(predicate -> {
if (cnt.getAndIncrement() != 0) {
sql.append(" AND ");
}
renderSqlWhileHelper(predicateView, columnNamer, columnDbTypeFunction, sql, values, predicate);
});
return new RenderResultImpl(sql.toString(), values);
}
private static <T extends Pipeline, ENTITY> void renderSqlWhileHelper(
final FieldPredicateView spv,
final Function<Field<ENTITY>, String> columnNamer,
final Function<Field<ENTITY>, Class<?>> columnDbTypeFunction,
final StringBuilder sql,
final List<Object> values,
final Predicate<ENTITY> predicate
) {
if (predicate instanceof FieldPredicate) {
final FieldPredicate<ENTITY> fieldPredicate = (FieldPredicate<ENTITY>) predicate;
final SqlPredicateFragment fragment = spv.transform(columnNamer, columnDbTypeFunction, fieldPredicate);
final Field<ENTITY> referenceFieldTrait = fieldPredicate.getField();
@SuppressWarnings("unchecked")
final TypeMapper<Object, Object> tm = (TypeMapper<Object, Object>) referenceFieldTrait.typeMapper();
sql.append(fragment.getSql());
fragment.objects().map(tm::toDatabaseType).forEachOrdered(values::add);
} else if (predicate instanceof CombinedPredicate) {
final CombinedPredicate<ENTITY> combinedPredicate = (CombinedPredicate<ENTITY>) predicate;
final StringBuilder internalSql = new StringBuilder();
final List<Object> internalValues = new ArrayList<>();
final AtomicInteger cnt = new AtomicInteger();
combinedPredicate.stream().forEachOrdered(internalPredicate -> {
if (cnt.getAndIncrement() != 0) {
internalSql.append(" ").append(combinedPredicate.getType().toString()).append(" ");
}
@SuppressWarnings("unchecked")
final Predicate<ENTITY> castedInternalPredicate = (Predicate<ENTITY>) internalPredicate;
renderSqlWhileHelper(
spv,
columnNamer,
columnDbTypeFunction,
internalSql,
internalValues,
castedInternalPredicate
);
});
if (combinedPredicate.isNegated()) {
sql.append("(NOT (").append(internalSql).append("))");
} else {
sql.append("(").append(internalSql).append(")");
}
values.addAll(internalValues);
} else {
throw new IllegalArgumentException("A predicate that is not instanceof FieldPredicate was given:" + predicate.toString());
}
}
private StreamTerminatorUtil() {
throw new UnsupportedOperationException();
}
}