Skip to content

Commit

Permalink
Work in progress to have single ObjectComparator calling back to new …
Browse files Browse the repository at this point in the history
…APIs.
  • Loading branch information
jdunkerley committed Mar 23, 2023
1 parent 314b152 commit 410cbdc
Show file tree
Hide file tree
Showing 20 changed files with 221 additions and 296 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -119,8 +119,10 @@ type Comparable

## PRIVATE
A callback allowing to compare two atoms with a custom comparator.
are_equal : Atom -> Atom -> Boolean
are_equal atom that = (Comparable.from atom).compare atom that == Ordering.Equal
compare_callback : Atom -> Atom -> Integer | Nothing
compare_callback atom that =
ordering = (Comparable.from atom).compare atom that
if ordering.is_nothing then Nothing else ordering.to_sign

## PRIVATE
A custom comparator is any comparator that is different than the
Expand Down
Original file line number Diff line number Diff line change
@@ -1,31 +1,11 @@
import project.Any.Any
import project.Data.Ordering.Natural_Order
import project.Data.Ordering.Ordering
import project.Data.Ordering.Comparable
import project.Data.Text.Case_Sensitivity.Case_Sensitivity
import project.Data.Text.Text_Ordering.Text_Ordering
import project.Nothing.Nothing

from project.Data.Boolean import True, False

polyglot java import org.enso.base.ObjectComparator

## PRIVATE
Creates a Java Comparator object which can call back to Enso for comparison
of non-primitive types.

Arguments:
- custom_comparator:
If `Nothing` will get an ordered comparator from each element.
Otherwise can support a custom fallback compare function.
new : Nothing | (Any -> Any -> Ordering) -> ObjectComparator
new custom_comparator=Nothing =
comparator_to_java cmp x y = cmp x y . to_sign

case custom_comparator of
Nothing -> ObjectComparator.getInstance (comparator_to_java Ordering.compare)
_ -> ObjectComparator.new (comparator_to_java custom_comparator)

## PRIVATE
Create a Java Comparator with the specified Text_Ordering

Expand All @@ -36,9 +16,8 @@ for_text_ordering : Text_Ordering -> ObjectComparator
for_text_ordering text_ordering = case text_ordering.sort_digits_as_numbers of
True ->
txt_cmp a b = Natural_Order.compare a b text_ordering.case_sensitivity . to_sign
new.withCustomTextComparator txt_cmp
ObjectComparator.new txt_cmp
False -> case text_ordering.case_sensitivity of
Case_Sensitivity.Default -> new
Case_Sensitivity.Sensitive -> new
Case_Sensitivity.Insensitive locale ->
new.withCaseInsensitivity locale.java_locale
Case_Sensitivity.Default -> ObjectComparator.DEFAULT
Case_Sensitivity.Sensitive -> ObjectComparator.DEFAULT
Case_Sensitivity.Insensitive locale -> ObjectComparator.new True locale.java_locale
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ type Rank_Method
Error.throw (Incomparable_Values.Error exc.payload.getLeftOperand exc.payload.getRightOperand)

handle_cmp_exc <| handle_nullpointer <|
java_ranks = Rank.rank input.to_array Comparator.new java_method
java_ranks = Rank.rank input.to_array java_method
Vector.from_polyglot_array java_ranks

type Statistic
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import project.Data.Numbers.Integer

from project.Data.Ordering import all
from project.Data.Boolean import Boolean, True, False


polyglot java import java.time.DayOfWeek

type Day_Of_Week
Expand Down Expand Up @@ -34,8 +37,9 @@ type Day_Of_Week
Day_Of_Week.Friday -> 5
Day_Of_Week.Saturday -> 6

shifted = if first_day == Day_Of_Week.Sunday then day_number else
(day_number + 7 - (first_day.to_integer start_at_zero=True)) % 7
shifted = case first_day of
Day_Of_Week.Sunday -> day_number
_ -> (day_number + 7 - (first_day.to_integer start_at_zero=True)) % 7

shifted + if start_at_zero then 0 else 1

Expand All @@ -49,3 +53,14 @@ type Day_Of_Week
Day_Of_Week.Thursday -> DayOfWeek.THURSDAY
Day_Of_Week.Friday -> DayOfWeek.FRIDAY
Day_Of_Week.Saturday -> DayOfWeek.SATURDAY

## PRIVATE
type Day_Of_Week_Comparator
compare x y =
x_int = x.to_integer
y_int = y.to_integer
Comparable.from x_int . compare x_int y_int

hash x = x.to_integer

Comparable.from (_:Day_Of_Week) = Day_Of_Week_Comparator
21 changes: 12 additions & 9 deletions distribution/lib/Standard/Table/0.0.0-dev/src/Data/Column.enso
Original file line number Diff line number Diff line change
Expand Up @@ -1258,15 +1258,18 @@ type Column
my_compare a b = Ordering.compare a.abs b.abs
Examples.decimal_column.sort by=my_compare
sort : Sort_Direction -> Boolean -> (Any -> Any -> Ordering) | Nothing -> Column
sort self order=Sort_Direction.Ascending missing_last=True by=Nothing =
order_bool = case order of
Sort_Direction.Ascending -> True
Sort_Direction.Descending -> False
java_cmp = Comparator.new by
rule = OrderBuilder.OrderRule.new self.java_column java_cmp order_bool missing_last
mask = OrderBuilder.buildOrderMask [rule].to_array
new_col = self.java_column.applyMask mask
Column.Value new_col
sort self order=Sort_Direction.Ascending missing_last=True by=Nothing = case by of
Nothing ->
order_bool = case order of
Sort_Direction.Ascending -> True
Sort_Direction.Descending -> False
rule = OrderBuilder.OrderRule.new self.java_column java_cmp order_bool missing_last else
mask = OrderBuilder.buildOrderMask [rule].to_array
new_col = self.java_column.applyMask mask
Column.Value new_col
_ ->
sorted = self.to_vector.sort order by=by
Column.from_vector self.name sorted

## Creates a new Column with the specified range of rows from the input
Column.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1280,11 +1280,8 @@ type Table
join_resolution = make_join_helpers self right . resolve on on_problems
right_columns_to_drop = join_resolution.redundant_column_names

object_comparator = Comparator.new
equality_fallback = .==

java_conditions = join_resolution.conditions
new_java_table = self.java_table.join right.java_table java_conditions (rows_to_keep.at 0) (rows_to_keep.at 1) (rows_to_keep.at 2) (columns_to_keep.at 0) (columns_to_keep.at 1) right_columns_to_drop right_prefix object_comparator equality_fallback
new_java_table = self.java_table.join right.java_table java_conditions (rows_to_keep.at 0) (rows_to_keep.at 1) (rows_to_keep.at 2) (columns_to_keep.at 0) (columns_to_keep.at 1) right_columns_to_drop right_prefix

on_problems.attach_problems_after (Table.Value new_java_table) <|
problems = new_java_table.getProblems
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -222,14 +222,14 @@ java_aggregator name column =
if ordering.is_nothing then FirstAggregator.new name c.java_column ignore_nothing else
order_columns = ordering.map c->c.column.java_column
order_directions = ordering.map c->c.direction.to_sign
FirstAggregator.new name c.java_column ignore_nothing order_columns.to_array order_directions.to_array Comparator.new
FirstAggregator.new name c.java_column ignore_nothing order_columns.to_array order_directions.to_array
Last c _ ignore_nothing ordering ->
if ordering.is_nothing then LastAggregator.new name c.java_column ignore_nothing else
order_columns = ordering.map c->c.column.java_column
order_direction = ordering.map c->c.direction.to_sign
LastAggregator.new name c.java_column ignore_nothing order_columns.to_array order_direction.to_array Comparator.new
Maximum c _ -> MinOrMaxAggregator.new name c.java_column 1 Comparator.new
Minimum c _ -> MinOrMaxAggregator.new name c.java_column -1 Comparator.new
LastAggregator.new name c.java_column ignore_nothing order_columns.to_array order_direction.to_array
Maximum c _ -> MinOrMaxAggregator.new name c.java_column 1
Minimum c _ -> MinOrMaxAggregator.new name c.java_column -1
Shortest c _ -> ShortestOrLongestAggregator.new name c.java_column -1
Longest c _ -> ShortestOrLongestAggregator.new name c.java_column 1
Concatenate c _ join prefix suffix quote -> ConcatenateAggregator.new name c.java_column join prefix suffix quote
Expand Down
98 changes: 43 additions & 55 deletions std-bits/base/src/main/java/org/enso/base/ObjectComparator.java
Original file line number Diff line number Diff line change
@@ -1,73 +1,69 @@
package org.enso.base;

import org.graalvm.polyglot.Context;
import org.graalvm.polyglot.Value;

import java.time.LocalDate;
import java.time.LocalTime;
import java.time.ZonedDateTime;
import java.util.Comparator;
import java.util.Locale;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.BiFunction;

public class ObjectComparator implements Comparator<Object> {
private static ObjectComparator INSTANCE;

/**
* A singleton instance of an ObjectComparator.
*
* @param fallbackComparator this MUST be the default .compare_to function for Enso. Needs to be
* passed to allow calling back from Java.
* @return Comparator object.
*/
public static ObjectComparator getInstance(Function<Object, Function<Object, Value>> fallbackComparator) {
if (INSTANCE == null) {
INSTANCE = new ObjectComparator(fallbackComparator);
public static final ObjectComparator DEFAULT = new ObjectComparator();

private static BiFunction<Object, Object, Integer> EnsoCompareCallback = null;

private static void initCallbacks() {
if (EnsoCompareCallback == null) {
var module = Context.getCurrent().getBindings("enso").invokeMember("get_module", "Standard.Base.Data.Ordering");
var type = module.invokeMember("get_type", "Comparable");

var are_equal = module.invokeMember("get_method", type, "compare_callback");
EnsoCompareCallback = (v, u) -> {
var result = are_equal.execute(null, v, u);
if (result.isNull()) {
throw new ClassCastException( "Unable to compare " + v.toString() + " and " + u.toString());
} else {
return result.asInt();
}
};
}

return INSTANCE;
}

private final Function<Object, Function<Object, Value>> fallbackComparator;
private final Function<String, Function<String, Value>> textComparator;


public ObjectComparator() {
this(
(a) -> (b) -> {
throw new CompareException(a, b);
});
public static int ensoCompare(Object value, Object other) throws ClassCastException {
initCallbacks();
return EnsoCompareCallback.apply(value, other);
}

public ObjectComparator(Function<Object, Function<Object, Value>> fallbackComparator) {
this(fallbackComparator, (a) -> (b) -> Value.asValue(Text_Utils.compare_normalized(a, b)));
}
private static BiFunction<String, String, Integer> textComparator;

private ObjectComparator(Function<Object, Function<Object, Value>> fallbackComparator, Function<String, Function<String, Value>> textComparator) {
this.fallbackComparator = fallbackComparator;
this.textComparator = textComparator;
public ObjectComparator() {
this(false, Locale.ROOT);
}

/**
* Create a copy of the ObjectComparator with case-insensitive text comparisons.
* @param locale to use for case folding.
* @return Comparator object.
*/
public ObjectComparator withCaseInsensitivity(Locale locale) {
return new ObjectComparator(this.fallbackComparator, (a) -> (b) -> Value.asValue(Text_Utils.compare_normalized_ignoring_case(a, b, locale)));
public ObjectComparator(boolean caseSensitive, Locale locale) {
if (caseSensitive) {
textComparator = Text_Utils::compare_normalized;
} else {
textComparator = (a, b) -> Text_Utils.compare_normalized_ignoring_case(a, b, locale);
}
}

/**
* Create a copy of the ObjectComparator with case-insensitive text comparisons.
* @param textComparator custom comparator for Text.
* @return Comparator object.
*/
public ObjectComparator withCustomTextComparator(Function<String, Function<String, Value>> textComparator) {
return new ObjectComparator(this.fallbackComparator, textComparator);
public ObjectComparator(Function<Object, Function<Object, Value>> textComparator) {
this.textComparator = (a, b) -> {
var result = textComparator.apply(a).apply(b);
if (result.isNull()) {
throw new CompareException(a, b);
}
return result.asInt();
};
}

@Override
public int compare(Object thisValue, Object thatValue) throws ClassCastException {
public int compare(Object thisValue, Object thatValue) {
// NULLs
if (thisValue == null) {
if (thatValue != null) {
Expand Down Expand Up @@ -121,7 +117,7 @@ public int compare(Object thisValue, Object thatValue) throws ClassCastException

// Text
if (thisValue instanceof String thisString && thatValue instanceof String thatString) {
return convertComparatorResult(textComparator.apply(thisString).apply(thatString), thisString, thatString);
return this.textComparator.apply(thisString, thatString);
}

// DateTimes
Expand All @@ -145,14 +141,6 @@ public int compare(Object thisValue, Object thatValue) throws ClassCastException
}

// Fallback to Enso
return convertComparatorResult(fallbackComparator.apply(thisValue).apply(thatValue), thisValue, thatValue);
}

private static int convertComparatorResult(Value comparatorResult, Object leftOperand, Object rightOperand) {
if (comparatorResult.isNumber() && comparatorResult.fitsInInt()) {
return comparatorResult.asInt();
} else {
throw new CompareException(leftOperand, rightOperand);
}
return ensoCompare(thisValue, thatValue);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
package org.enso.base.polyglot;

import org.enso.base.ObjectComparator;
import org.graalvm.polyglot.Context;

import java.util.function.BiFunction;
import java.util.function.Function;

public class EnsoObjectWrapper implements Comparable<EnsoObjectWrapper> {
private static Function<Object, Integer> EnsoHashCodeCallback = null;
private static BiFunction<Object, Object, Boolean> EnsoAreEqualCallback = null;

private static void initCallbacks() {
if (EnsoHashCodeCallback == null) {
var module = Context.getCurrent().getBindings("enso").invokeMember("get_module", "Standard.Base.Data.Ordering");
var type = module.invokeMember("get_type", "Comparable");

var hash_callback = module.invokeMember("get_method", type, "hash_callback");
EnsoHashCodeCallback = v -> {
var result = hash_callback.execute(null, v);
if (result.isNull()) {
throw new IllegalStateException("Unable to object hash in EnsoObjectWrapper for " + v.toString());
} else {
return result.asInt();
}
};

var are_equal = module.invokeMember("get_method", type, "compare_callback");
EnsoAreEqualCallback = (v, u) -> {
var result = are_equal.execute(null, v, u);
return !result.isNull() && result.asInt() == 0;
};
}
}

private static int getEnsoHashCode(Object value) {
initCallbacks();
return EnsoHashCodeCallback.apply(value);
}

private static boolean areEqual(Object value, Object other) {
initCallbacks();
return EnsoAreEqualCallback.apply(value, other);
}

private final Object value;
private final int ensoHashCode;

public EnsoObjectWrapper(Object value) {
this.value = value;
this.ensoHashCode = getEnsoHashCode(value);
}

public Object getValue() {
return value;
}

@Override
public int hashCode() {
return ensoHashCode;
}

@Override
public boolean equals(Object obj) {
if (obj instanceof EnsoObjectWrapper that) {
return areEqual(this.value, that.value);
} else {
return false;
}
}

@Override
public int compareTo(EnsoObjectWrapper o) {
return ObjectComparator.ensoCompare(this.value, o.value);
}
}
Loading

0 comments on commit 410cbdc

Please sign in to comment.