package com.revolsys.record.query;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.function.Consumer;
import java.util.function.Predicate;
import com.revolsys.geometry.model.BoundingBox;
import com.revolsys.io.PathName;
import com.revolsys.jdbc.JdbcUtils;
import com.revolsys.predicate.Predicates;
import com.revolsys.properties.BaseObjectWithProperties;
import com.revolsys.record.Record;
import com.revolsys.record.RecordFactory;
import com.revolsys.record.Records;
import com.revolsys.record.query.functions.EnvelopeIntersects;
import com.revolsys.record.query.functions.F;
import com.revolsys.record.schema.FieldDefinition;
import com.revolsys.record.schema.RecordDefinition;
import com.revolsys.record.schema.RecordDefinitionProxy;
import com.revolsys.record.schema.RecordStore;
import com.revolsys.util.Cancellable;
import com.revolsys.util.CancellableProxy;
import com.revolsys.util.Property;
import com.revolsys.util.count.LabelCountMap;
public class Query extends BaseObjectWithProperties implements Cloneable, CancellableProxy {
private static void addFilter(final Query query, final RecordDefinition recordDefinition,
final Map<String, ?> filter, final AbstractMultiCondition multipleCondition) {
if (filter != null && !filter.isEmpty()) {
for (final Entry<String, ?> entry : filter.entrySet()) {
final String name = entry.getKey();
final FieldDefinition fieldDefinition = recordDefinition.getField(name);
if (fieldDefinition == null) {
final Object value = entry.getValue();
if (value == null) {
multipleCondition.addCondition(Q.isNull(name));
} else if (value instanceof Collection) {
final Collection<?> values = (Collection<?>)value;
multipleCondition.addCondition(new In(name, values));
} else {
multipleCondition.addCondition(Q.equal(name, value));
}
} else {
final Object value = entry.getValue();
if (value == null) {
multipleCondition.addCondition(Q.isNull(name));
} else if (value instanceof Collection) {
final Collection<?> values = (Collection<?>)value;
multipleCondition.addCondition(new In(fieldDefinition, values));
} else {
multipleCondition.addCondition(Q.equal(fieldDefinition, value));
}
}
}
query.setWhereCondition(multipleCondition);
}
}
public static Query and(final RecordDefinition recordDefinition, final Map<String, ?> filter) {
final Query query = new Query(recordDefinition);
final And and = new And();
addFilter(query, recordDefinition, filter, and);
return query;
}
public static Query equal(final RecordDefinitionProxy recordDefinition, final String name,
final Object value) {
final FieldDefinition fieldDefinition = recordDefinition.getFieldDefinition(name);
if (fieldDefinition == null) {
return null;
} else {
final Query query = new Query(recordDefinition);
final Value valueCondition = new Value(fieldDefinition, value);
final BinaryCondition equal = Q.equal(name, valueCondition);
query.setWhereCondition(equal);
return query;
}
}
public static Query intersects(final RecordDefinition recordDefinition,
final BoundingBox boundingBox) {
final FieldDefinition geometryField = recordDefinition.getGeometryField();
if (geometryField == null) {
return null;
} else {
final EnvelopeIntersects intersects = F.envelopeIntersects(geometryField, boundingBox);
final Query query = new Query(recordDefinition, intersects);
return query;
}
}
public static Query intersects(final RecordStore recordStore, final PathName path,
final BoundingBox boundingBox) {
final RecordDefinition recordDefinition = recordStore.getRecordDefinition(path);
final FieldDefinition geometryField = recordDefinition.getGeometryField();
if (geometryField == null) {
return null;
} else {
final EnvelopeIntersects intersects = F.envelopeIntersects(geometryField, boundingBox);
final Query query = new Query(recordDefinition, intersects);
return query;
}
}
public static Query or(final RecordDefinition recordDefinition, final Map<String, ?> filter) {
final Query query = new Query(recordDefinition);
final Or or = new Or();
addFilter(query, recordDefinition, filter, or);
return query;
}
public static Query orderBy(final PathName pathName, final String... orderBy) {
final Query query = new Query(pathName);
query.setOrderByFieldNames(orderBy);
return query;
}
private Cancellable cancellable;
private RecordFactory<Record> recordFactory;
private List<String> fieldNames = Collections.emptyList();
private String fromClause;
private int limit = Integer.MAX_VALUE;
private boolean lockResults = false;
private int offset = 0;
private Map<String, Boolean> orderBy = new HashMap<>();
private List<Object> parameters = new ArrayList<>();
private RecordDefinition recordDefinition;
private String sql;
private LabelCountMap labelCountMap;
private PathName typeName;
private String typePathAlias;
private Condition whereCondition = Condition.ALL;
public Query() {
}
public Query(final PathName typePath) {
this(typePath, null);
}
public Query(final PathName typePath, final Condition whereCondition) {
this.typeName = typePath;
setWhereCondition(whereCondition);
}
public Query(final RecordDefinitionProxy recordDefinition) {
this(recordDefinition, null);
}
public Query(final RecordDefinitionProxy recordDefinition, final Condition whereCondition) {
this(recordDefinition.getPathName());
this.recordDefinition = recordDefinition.getRecordDefinition();
setWhereCondition(whereCondition);
}
public Query(final String typePath) {
this(PathName.newPathName(typePath));
}
public Query(final String typePath, final Condition whereCondition) {
this(PathName.newPathName(typePath), whereCondition);
}
public Query addOrderBy(final String column) {
return addOrderBy(column, true);
}
public Query addOrderBy(final String column, final boolean ascending) {
this.orderBy.put(column, ascending);
return this;
}
@Deprecated
public void addParameter(final Object value) {
this.parameters.add(value);
}
public Query and(final Condition condition) {
if (!Property.isEmpty(condition)) {
Condition whereCondition = getWhereCondition();
whereCondition = whereCondition.and(condition);
setWhereCondition(whereCondition);
}
return this;
}
@Override
public Query clone() {
final Query clone = (Query)super.clone();
clone.fieldNames = new ArrayList<>(clone.fieldNames);
clone.parameters = new ArrayList<>(this.parameters);
clone.orderBy = new HashMap<>(this.orderBy);
if (this.whereCondition != null) {
clone.whereCondition = this.whereCondition.clone();
}
if (!clone.getFieldNames().isEmpty() || clone.whereCondition != null) {
clone.sql = null;
}
return clone;
}
@SuppressWarnings("unchecked")
public <R extends Record> void forEachRecord(final Iterable<R> records,
final Consumer<? super R> consumer) {
final Map<String, Boolean> orderBy = getOrderBy();
final Predicate<R> filter = (Predicate<R>)getWhereCondition();
if (orderBy == null) {
if (filter == null) {
records.forEach(consumer);
} else {
records.forEach((record) -> {
if (filter.test(record)) {
consumer.accept(record);
}
});
}
} else {
final Comparator<R> comparator = Records.newComparatorOrderBy(orderBy);
final List<R> results = Predicates.filter(records, filter);
results.sort(comparator);
results.forEach(consumer);
}
}
@Override
public Cancellable getCancellable() {
return this.cancellable;
}
public List<String> getFieldNames() {
return this.fieldNames;
}
public String getFromClause() {
return this.fromClause;
}
public FieldDefinition getGeometryField() {
return getRecordDefinition().getGeometryField();
}
public int getLimit() {
return this.limit;
}
public int getOffset() {
return this.offset;
}
public Map<String, Boolean> getOrderBy() {
return this.orderBy;
}
public List<Object> getParameters() {
return this.parameters;
}
public RecordDefinition getRecordDefinition() {
return this.recordDefinition;
}
@SuppressWarnings("unchecked")
public <V extends Record> RecordFactory<V> getRecordFactory() {
return (RecordFactory<V>)this.recordFactory;
}
public String getSql() {
return this.sql;
}
public LabelCountMap getStatistics() {
return this.labelCountMap;
}
public String getTypeName() {
if (this.typeName == null) {
return null;
} else {
return this.typeName.getPath();
}
}
public String getTypeNameAlias() {
return this.typePathAlias;
}
public PathName getTypePath() {
return this.typeName;
}
public String getWhere() {
return this.whereCondition.toFormattedString();
}
public Condition getWhereCondition() {
return this.whereCondition;
}
public boolean isLockResults() {
return this.lockResults;
}
public Query newQuery(final RecordDefinition recordDefinition) {
final Query query = clone();
query.setRecordDefinition(recordDefinition);
return query;
}
public void or(final Condition condition) {
final Condition whereCondition = getWhereCondition();
if (whereCondition.isEmpty()) {
setWhereCondition(condition);
} else if (whereCondition instanceof Or) {
final Or or = (Or)whereCondition;
or.or(condition);
} else {
setWhereCondition(new Or(whereCondition, condition));
}
}
public void setCancellable(final Cancellable cancellable) {
this.cancellable = cancellable;
}
public Query setFieldNames(final List<String> fieldNames) {
this.fieldNames = fieldNames;
return this;
}
public Query setFieldNames(final String... fieldNames) {
setFieldNames(Arrays.asList(fieldNames));
return this;
}
public Query setFromClause(final String fromClause) {
this.fromClause = fromClause;
return this;
}
public Query setLimit(final int limit) {
if (limit < 0) {
this.limit = Integer.MAX_VALUE;
} else {
this.limit = limit;
}
return this;
}
public void setLockResults(final boolean lockResults) {
this.lockResults = lockResults;
}
public Query setOffset(final int offset) {
this.offset = offset;
return this;
}
public Query setOrderBy(final Map<String, Boolean> orderBy) {
if (orderBy != this.orderBy) {
this.orderBy.clear();
if (orderBy != null) {
this.orderBy.putAll(orderBy);
}
}
return this;
}
public Query setOrderByFieldNames(final List<String> orderBy) {
this.orderBy.clear();
for (final String column : orderBy) {
this.orderBy.put(column, Boolean.TRUE);
}
return this;
}
public Query setOrderByFieldNames(final String... orderBy) {
return setOrderByFieldNames(Arrays.asList(orderBy));
}
public void setRecordDefinition(final RecordDefinition recordDefinition) {
this.recordDefinition = recordDefinition;
if (this.whereCondition != null) {
this.whereCondition.setRecordDefinition(recordDefinition);
}
}
@SuppressWarnings("unchecked")
public void setRecordFactory(final RecordFactory<?> recordFactory) {
this.recordFactory = (RecordFactory<Record>)recordFactory;
}
public void setSql(final String sql) {
this.sql = sql;
}
public void setStatistics(final LabelCountMap labelCountMap) {
this.labelCountMap = labelCountMap;
}
public void setTypeName(final String typeName) {
this.typeName = PathName.newPathName(typeName);
}
public void setTypeNameAlias(final String typePathAlias) {
this.typePathAlias = typePathAlias;
}
public Query setWhere(final String where) {
final Condition whereCondition = QueryValue.parseWhere(this.recordDefinition, where);
return setWhereCondition(whereCondition);
}
public Query setWhereCondition(final Condition whereCondition) {
if (whereCondition == null) {
this.whereCondition = Condition.ALL;
} else {
this.whereCondition = whereCondition;
final RecordDefinition recordDefinition = getRecordDefinition();
if (recordDefinition != null) {
whereCondition.setRecordDefinition(recordDefinition);
}
}
return this;
}
public <V extends Record> void sort(final List<V> records) {
final Map<String, Boolean> orderBy = getOrderBy();
if (Property.hasValue(orderBy)) {
final Comparator<Record> comparator = Records.newComparatorOrderBy(orderBy);
records.sort(comparator);
}
}
@Override
public String toString() {
try {
final StringBuilder string = new StringBuilder();
if (this.sql == null) {
string.append(JdbcUtils.getSelectSql(this));
} else {
string.append(this.sql);
}
if (!this.parameters.isEmpty()) {
string.append(" ");
string.append(this.parameters);
}
return string.toString();
} catch (final Throwable t) {
t.printStackTrace();
return "";
}
}
}