package com.revolsys.record;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.TreeSet;
import java.util.function.Predicate;
import com.revolsys.collection.list.Lists;
import com.revolsys.comparator.StringNumberComparator;
import com.revolsys.datatype.DataType;
import com.revolsys.datatype.DataTypes;
import com.revolsys.geometry.model.BoundingBox;
import com.revolsys.geometry.model.Geometry;
import com.revolsys.geometry.model.GeometryFactory;
import com.revolsys.geometry.model.TopologyException;
import com.revolsys.identifier.Identifier;
import com.revolsys.io.PathName;
import com.revolsys.predicate.Predicates;
import com.revolsys.record.code.CodeTable;
import com.revolsys.record.schema.FieldDefinition;
import com.revolsys.record.schema.RecordDefinition;
import com.revolsys.record.schema.RecordDefinitionImpl;
import com.revolsys.util.CompareUtil;
import com.revolsys.util.JavaBeanUtil;
import com.revolsys.util.Property;
import com.revolsys.util.Strings;
public interface Records {
static BoundingBox boundingBox(final Iterable<Record> records) {
BoundingBox boundingBox = BoundingBox.empty();
for (final Record record : records) {
boundingBox = boundingBox.expandToInclude(boundingBox(record));
}
return boundingBox;
}
static BoundingBox boundingBox(final Record record) {
if (record != null) {
final Geometry geometry = record.getGeometry();
if (geometry != null) {
return geometry.getBoundingBox();
}
}
return BoundingBox.empty();
}
static int compareNullFirst(final Record record1, final Record record2, final String fieldName) {
final Object value1 = getValue(record1, fieldName);
final Object value2 = getValue(record2, fieldName);
if (value1 == value2) {
return 0;
} else {
if (value1 == null) {
return -1;
} else if (value2 == null) {
return 1;
} else {
return CompareUtil.compare(value1, value2);
}
}
}
static int compareNullFirst(final Record record1, final Record record2,
final String... fieldNames) {
for (final String fieldName : fieldNames) {
final Object value1 = getValue(record1, fieldName);
final Object value2 = getValue(record2, fieldName);
if (value1 != value2) {
if (value1 == null) {
return -1;
} else if (value2 == null) {
return 1;
} else {
final int compare = CompareUtil.compare(value1, value2);
if (compare != 0) {
return compare;
}
}
}
}
return 0;
}
static int compareNullLast(final Record record1, final Record record2, final String fieldName) {
final Object value1 = getValue(record1, fieldName);
final Object value2 = getValue(record2, fieldName);
if (value1 == value2) {
return 0;
} else {
if (value1 == null) {
return 1;
} else if (value2 == null) {
return -1;
} else {
return CompareUtil.compare(value1, value2);
}
}
}
static int compareNullLast(final Record record1, final Record record2,
final String... fieldNames) {
for (final String fieldName : fieldNames) {
final Object value1 = getValue(record1, fieldName);
final Object value2 = getValue(record2, fieldName);
if (value1 != value2) {
if (value1 == null) {
return 1;
} else if (value2 == null) {
return -1;
} else {
final int compare = CompareUtil.compare(value1, value2);
if (compare != 0) {
return compare;
}
}
}
}
return 0;
}
static Record copy(final RecordDefinition recordDefinition, final Record record) {
final Record copy = new ArrayRecord(recordDefinition);
copy.setValuesClone(record);
return copy;
}
/**
* Construct a new copy of the data record replacing the geometry with the new
* geometry. If the existing geometry on the record has user data it will be
* cloned to the new geometry.
*
* @param record The record to copy.
* @param geometry The new geometry.
* @return The copied record.
*/
@SuppressWarnings("unchecked")
static <T extends Record> T copy(final T record, final Geometry geometry) {
final T newObject = (T)record.clone();
newObject.setGeometryValue(geometry);
return newObject;
}
static double distance(final Record record1, final Record record2) {
if (record1 == null || record2 == null) {
return Double.MAX_VALUE;
} else {
final Geometry geometry1 = record1.getGeometry();
final Geometry geometry2 = record2.getGeometry();
if (geometry1 == null || geometry2 == null) {
return Double.MAX_VALUE;
} else {
return geometry1.distance(geometry2);
}
}
}
static <D extends Record> List<D> filter(final Collection<D> records, final Geometry geometry,
final double maxDistance) {
final List<D> results = new ArrayList<>();
for (final D record : records) {
final Geometry recordGeometry = record.getGeometry();
final double distance = recordGeometry.distance(geometry);
if (distance < maxDistance) {
results.add(record);
}
}
return results;
}
/**
* Filter and sort the records. The list will be overwritten by the result.
*
* @param records
* @param filter
* @param orderBy
*/
static <V extends Record> void filterAndSort(final List<V> records,
final Predicate<? super V> filter, final Map<String, Boolean> orderBy) {
// Filter records
if (!Property.isEmpty(filter)) {
Predicates.retain(records, filter);
}
// Sort records
if (Property.hasValue(orderBy)) {
final Comparator<Record> comparator = newComparatorOrderBy(orderBy);
Collections.sort(records, comparator);
}
}
static boolean getBoolean(final Record record, final String fieldName) {
if (record == null) {
return false;
} else {
final Object value = getValue(record, fieldName);
if (value == null) {
return false;
} else if (value instanceof Boolean) {
final Boolean booleanValue = (Boolean)value;
return booleanValue;
} else if (value instanceof Number) {
final Number number = (Number)value;
return number.intValue() == 1;
} else {
final String stringValue = value.toString();
if (stringValue.equals("Y") || stringValue.equals("1")
|| Boolean.parseBoolean(stringValue)) {
return true;
} else {
return false;
}
}
}
}
static Double getDouble(final Record record, final int fieldIndex) {
final Number value = record.getValue(fieldIndex);
if (value == null) {
return null;
} else if (value instanceof Double) {
return (Double)value;
} else {
return value.doubleValue();
}
}
static Double getDouble(final Record record, final String fieldName) {
final Number value = record.getValue(fieldName);
if (value == null) {
return null;
} else if (value instanceof Double) {
return (Double)value;
} else {
return value.doubleValue();
}
}
@SuppressWarnings("unchecked")
static <T> T getFieldByPath(final Record record, final String path) {
if (path == null) {
return null;
} else {
final RecordDefinition recordDefinition = record.getRecordDefinition();
final String[] propertyPath = path.split("\\.");
Object propertyValue = record;
for (int i = 0; i < propertyPath.length && propertyValue != null; i++) {
final String propertyName = propertyPath[i];
if (propertyValue instanceof Record) {
final Record recordValue = (Record)propertyValue;
if (recordValue.hasField(propertyName)) {
propertyValue = getValue(recordValue, propertyName);
if (propertyValue == null) {
return null;
} else if (i + 1 < propertyPath.length) {
final CodeTable codeTable = recordDefinition.getCodeTableByFieldName(propertyName);
if (codeTable != null) {
propertyValue = codeTable.getMap(Identifier.newIdentifier(propertyValue));
}
}
} else {
return null;
}
} else if (propertyValue instanceof Map) {
final Map<String, Object> map = (Map<String, Object>)propertyValue;
propertyValue = map.get(propertyName);
if (propertyValue == null) {
return null;
} else if (i + 1 < propertyPath.length) {
final CodeTable codeTable = recordDefinition.getCodeTableByFieldName(propertyName);
if (codeTable != null) {
propertyValue = codeTable.getMap(Identifier.newIdentifier(propertyValue));
}
}
} else {
try {
final Object object = propertyValue;
propertyValue = Property.getSimple(object, propertyName);
} catch (final IllegalArgumentException e) {
throw new IllegalArgumentException("Path does not exist " + path, e);
}
}
}
return (T)propertyValue;
}
}
static List<Geometry> getGeometries(final Collection<?> records) {
final List<Geometry> geometries = new ArrayList<>();
for (final Object record : records) {
final Geometry geometry = unionGeometry(record);
if (geometry != null) {
geometries.add(geometry);
}
}
return geometries;
}
static Geometry getGeometry(final Collection<?> records) {
final List<Geometry> geometries = getGeometries(records);
if (geometries.isEmpty()) {
return GeometryFactory.DEFAULT_3D.geometry();
} else {
final GeometryFactory geometryFactory = geometries.get(0).getGeometryFactory();
return geometryFactory.geometry(geometries);
}
}
static <G extends Geometry> G getGeometry(final Record record) {
if (record == null) {
return null;
} else {
return record.getGeometry();
}
}
static Set<Identifier> getIdentifiers(final Collection<? extends Record> records) {
final Set<Identifier> identifiers = Identifier.newTreeSet();
for (final Record record : records) {
final Identifier identifier = record.getIdentifier();
if (identifier != null) {
identifiers.add(identifier);
}
}
return identifiers;
}
static List<Identifier> getIdentifiers(final Record record, final Collection<String> fieldNames) {
final List<Identifier> identifiers = new ArrayList<>();
for (final String fieldName : fieldNames) {
final Identifier identifier = record.getIdentifier(fieldName);
if (Property.hasValue(identifier)) {
identifiers.add(identifier);
}
}
return identifiers;
}
static List<Identifier> getIdentifiers(final Record record, final String... fieldNames) {
return getIdentifiers(record, Arrays.asList(fieldNames));
}
static Integer getInteger(final Record record, final String fieldName,
final Integer defaultValue) {
if (record == null) {
return null;
} else {
final Number value = record.getValue(fieldName);
if (value == null) {
return defaultValue;
} else if (value instanceof Integer) {
return (Integer)value;
} else {
return value.intValue();
}
}
}
static Long getLong(final Record record, final String fieldName) {
final Number value = record.getValue(fieldName);
if (value == null) {
return null;
} else if (value instanceof Long) {
return (Long)value;
} else {
return value.longValue();
}
}
static int getMax(final int max, final Record record, final String fieldName) {
if (record != null) {
final Integer value = record.getInteger(fieldName);
if (value != null) {
if (value > max) {
return value;
}
}
}
return max;
}
static int getMin(final int min, final Record record, final String fieldName) {
if (record != null) {
final Integer value = record.getInteger(fieldName);
if (value != null) {
if (value < min) {
return value;
}
}
}
return min;
}
static Object getValue(final Record record, final String fieldName) {
if (record == null || !Property.hasValue(fieldName)) {
return null;
} else {
return record.getValue(fieldName);
}
}
static void mergeStringListValue(final Map<String, Object> record, final Record record1,
final Record record2, final String fieldName, final String separator) {
final String value1 = record1.getString(fieldName);
final String value2 = record2.getString(fieldName);
Object value;
if (!Property.hasValue(value1)) {
value = value2;
} else if (!Property.hasValue(value2)) {
value = value1;
} else if (DataType.equal(value1, value2)) {
value = value1;
} else {
final Set<String> values = new TreeSet<>(new StringNumberComparator());
values.addAll(Lists.split(value1, ","));
values.addAll(Lists.split(value2, ","));
value = Strings.toString(values);
}
record.put(fieldName, value);
}
static void mergeValue(final Map<String, Object> record, final Record record1,
final Record record2, final String fieldName, final String separator) {
final String value1 = record1.getString(fieldName);
final String value2 = record2.getString(fieldName);
Object value;
if (!Property.hasValue(value1)) {
value = value2;
} else if (!Property.hasValue(value2)) {
value = value1;
} else if (DataType.equal(value1, value2)) {
value = value1;
} else {
value = value1 + separator + value2;
}
record.put(fieldName, value);
}
static Comparator<Record> newComparatorDistance(final Geometry geometry) {
return (record1, record2) -> {
if (record1 == record2) {
return 0;
} else {
final double distance1 = record1.distance(geometry);
final double distance2 = record2.distance(geometry);
int compare = Double.compare(distance1, distance2);
if (compare == 0) {
compare = record1.compareTo(record2);
}
return compare;
}
};
}
static <R extends Record> Comparator<R> newComparatorOrderBy(final Map<String, Boolean> orderBy) {
return (record1, record2) -> {
if (record1 == record2) {
return 0;
} else {
if (Property.hasValue(orderBy)) {
for (final Entry<String, Boolean> entry : orderBy.entrySet()) {
final String fieldName = entry.getKey();
final Boolean ascending = entry.getValue();
final Object value1 = record1.getValue(fieldName);
final Object value2 = record2.getValue(fieldName);
final int compare = CompareUtil.compare(value1, value2);
if (compare != 0) {
if (ascending) {
return compare;
} else {
return -compare;
}
}
}
return -1;
} else {
return -1;
}
}
};
}
static <R extends Record> Comparator<R> newComparatorOrderByIdentifier(
final Map<String, Boolean> orderBy) {
return (record1, record2) -> {
if (record1 == record2) {
return 0;
} else {
if (Property.hasValue(orderBy)) {
for (final Entry<String, Boolean> entry : orderBy.entrySet()) {
final String fieldName = entry.getKey();
final Boolean ascending = entry.getValue();
final Object value1 = record1.getValue(fieldName);
final Object value2 = record2.getValue(fieldName);
final int compare = CompareUtil.compare(value1, value2);
if (compare != 0) {
if (ascending) {
return compare;
} else {
return -compare;
}
}
}
final Identifier identifier1 = record1.getIdentifier();
final Identifier identifier2 = record2.getIdentifier();
return CompareUtil.compare(identifier1, identifier2);
} else {
return -1;
}
}
};
}
static <V extends Record> Predicate<V> newFilter(final BoundingBox boundingBox) {
return (record) -> {
if (record != null) {
try {
final Geometry geometry = record.getGeometry();
if (geometry != null) {
return geometry.intersects(boundingBox);
}
} catch (final Throwable t) {
}
}
return false;
};
}
static <V extends Record> Predicate<V> newFilter(final Geometry geometry,
final double maxDistance) {
return (record) -> {
if (record != null) {
final Geometry recordGeometry = record.getGeometry();
if (recordGeometry != null) {
final double distance = recordGeometry.distance(geometry, maxDistance);
if (distance <= maxDistance) {
return true;
}
}
}
return false;
};
}
static <V extends Record> Predicate<V> newFilter(final String fieldName, final Object value) {
return (record) -> {
if (record != null) {
final Object fieldValue = record.getValue(fieldName);
return DataType.equal(fieldValue, value);
}
return false;
};
}
static <R extends Record> Predicate<R> newFilterGeometryIntersects(final Geometry geometry) {
if (Property.hasValue(geometry)) {
final GeometryFactory geometryFactory = geometry.getGeometryFactory();
return (record) -> {
if (record != null) {
try {
final Geometry geometry2 = record.getGeometry();
final Geometry convertedGeometry2 = geometry2.convertGeometry(geometryFactory);
if (convertedGeometry2 != null) {
try {
return geometry.intersects(convertedGeometry2);
} catch (final TopologyException e) {
return true;
}
}
} catch (final Throwable t) {
}
}
return false;
};
} else {
return Predicates.none();
}
}
static RecordDefinition newGeometryRecordDefinition() {
final FieldDefinition geometryField = new FieldDefinition("geometry", DataTypes.GEOMETRY, true);
return new RecordDefinitionImpl(PathName.newPathName("/Feature"), geometryField);
}
static Record newRecord(final RecordDefinition recordDefinition,
final Map<String, Object> values) {
return new ArrayRecord(recordDefinition, values);
}
static List<Record> newRecords(final RecordDefinition recordDefinition,
final Collection<? extends Map<String, Object>> list) {
final List<Record> records = new ArrayList<>();
for (final Map<String, Object> map : list) {
final Record record = newRecord(recordDefinition, map);
records.add(record);
}
return records;
}
static void removeDeleted(final Collection<? extends Record> records) {
for (final Iterator<? extends Record> iterator = records.iterator(); iterator.hasNext();) {
final Record record = iterator.next();
if (record == null || record.getState() == RecordState.DELETED) {
iterator.remove();
}
}
}
static void setValues(final Record target, final Record source,
final Collection<String> fieldNames, final Collection<String> ignoreFieldNames) {
for (final String fieldName : fieldNames) {
if (!ignoreFieldNames.contains(fieldName)) {
final Object oldValue = getValue(target, fieldName);
Object newValue = getValue(source, fieldName);
if (!DataType.equal(oldValue, newValue)) {
newValue = JavaBeanUtil.clone(newValue);
target.setValue(fieldName, newValue);
}
}
}
}
static Geometry unionGeometry(final Collection<?> records) {
final Geometry geometry = getGeometry(records);
return geometry.union();
}
static Geometry unionGeometry(final Map<?, ?> map) {
Geometry union = null;
for (final Entry<?, ?> entry : map.entrySet()) {
final Object key = entry.getKey();
final Geometry keyGeometry = unionGeometry(key);
if (keyGeometry != null) {
union = keyGeometry.union(union);
}
final Object value = entry.getValue();
final Geometry valueGeometry = unionGeometry(value);
if (valueGeometry != null) {
union = valueGeometry.union(union);
}
}
return union;
}
static Geometry unionGeometry(final Object object) {
if (object instanceof Geometry) {
final Geometry geometry = (Geometry)object;
return geometry;
} else if (object instanceof Record) {
final Record record = (Record)object;
return record.getGeometry();
} else if (object instanceof Collection) {
final Collection<?> objects = (Collection<?>)object;
return unionGeometry(objects);
} else if (object instanceof Map) {
final Map<?, ?> map = (Map<?, ?>)object;
return unionGeometry(map);
} else {
return null;
}
}
}