Skip to content

Commit

Permalink
Refactored use of deep-equals to better align with the Metapath speci…
Browse files Browse the repository at this point in the history
…fication around fn:same-key. Also refactored the IMapKey implementations to align with the specification. Fixed a variety of Javadoc errors. (#370)
  • Loading branch information
david-waltermire authored Feb 27, 2025
1 parent ff1a35b commit 78a1b1c
Show file tree
Hide file tree
Showing 49 changed files with 305 additions and 260 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -187,11 +187,11 @@ public void setImplicitTimeZone(@NonNull ZoneId timezone) {
* Once the context is used, this value is expected to be stable.
*
* @param offset
* the offset which must be >= -PT14H and <= PT13H
* the offset which must be &gt;= -PT14H and &lt;= PT13H
* @throws DateTimeFunctionException
* with the code
* {@link DateTimeFunctionException#INVALID_TIME_ZONE_VALUE_ERROR} if
* the offset is < -PT14H or > PT14H
* the offset is &lt; -PT14H or &gt; PT14H
*/
public void setImplicitTimeZone(@NonNull IDayTimeDurationItem offset) {
setImplicitTimeZone(offset.asZoneOffset());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -252,7 +252,6 @@ private String getDefaultFunctionNamespace() {

/**
* Parse the name of an atomic type.
*
* <p>
* This method will attempt to identify the namespace corresponding to a given
* prefix.
Expand Down Expand Up @@ -345,6 +344,8 @@ public static IAtomicOrUnionType<?> lookupAtomicType(@NonNull IEnhancedQName qna
/**
* Lookup a known Metapath atomic type based on the type's item class.
*
* @param <T>
* the Java type of the item to get the type information for
* @param clazz
* the item class associated with the atomic type
* @return the type
Expand Down Expand Up @@ -574,7 +575,6 @@ private String resolveModelReferencePrefix(@NonNull String prefix) {
* <p>
* The prefix will be resolved using the following lookup order, advancing to
* the next when a {@code null} value is returned:
*
* <ol>
* <li>Lookup the prefix using the namespaces registered with the static
* context.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,9 @@ protected ISequence<? extends IAnyAtomicItem> evaluate(DynamicContext dynamicCon
* the first item
* @param rightItem
* the second item
* @param dynamicContext
* used to provide evaluation information, including the implicit
* timezone
* @return the result of the operation or an empty {@link ISequence} if either
* item is {@code null}
*/
Expand All @@ -93,6 +96,9 @@ protected ISequence<? extends IAnyAtomicItem> resultOrEmpty(
* the first item
* @param right
* the second item
* @param dynamicContext
* used to provide evaluation information, including the implicit
* timezone
* @return the result of the operation
*/
@SuppressWarnings("PMD.OnlyOneReturn")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import java.util.Locale;

import edu.umd.cs.findbugs.annotations.NonNull;
import edu.umd.cs.findbugs.annotations.Nullable;

/**
* A collection of comparison functions supporting value and general
Expand Down Expand Up @@ -91,7 +92,7 @@ public static IBooleanItem valueCompairison(
@NonNull IAnyAtomicItem leftItem,
@NonNull Operator operator,
@NonNull IAnyAtomicItem rightItem,
@NonNull DynamicContext dynamicContext) {
@Nullable DynamicContext dynamicContext) {
return compare(leftItem, operator, rightItem, dynamicContext);
}

Expand Down Expand Up @@ -194,7 +195,7 @@ public static IBooleanItem compare( // NOPMD - unavoidable
@NonNull IAnyAtomicItem left,
@NonNull Operator operator,
@NonNull IAnyAtomicItem right,
@NonNull DynamicContext dynamicContext) {
@Nullable DynamicContext dynamicContext) {
@NonNull
IBooleanItem retval;
if (left instanceof IStringItem || right instanceof IStringItem) {
Expand Down Expand Up @@ -390,29 +391,31 @@ public static IBooleanItem dateTimeCompare(
@NonNull IDateTimeItem left,
@NonNull Operator operator,
@NonNull IDateTimeItem right,
@NonNull DynamicContext dynamicContext) {
@Nullable DynamicContext dynamicContext) {
IBooleanItem retval;
switch (operator) {
case EQ:
retval = OperationFunctions.opDateTimeEqual(left, right, dynamicContext);
break;
case GE: {
IDateTimeItem leftNormalized = left.normalize(dynamicContext);
IDateTimeItem rightNormalized = right.normalize(dynamicContext);
// pre-normalize for efficiency
IDateTimeItem leftNormalized = dynamicContext == null ? left : left.normalize(dynamicContext);
IDateTimeItem rightNormalized = dynamicContext == null ? right : right.normalize(dynamicContext);
retval = IBooleanItem.valueOf(
OperationFunctions.opDateTimeGreaterThan(leftNormalized, rightNormalized, dynamicContext).toBoolean()
|| OperationFunctions.opDateTimeEqual(leftNormalized, rightNormalized, dynamicContext).toBoolean());
OperationFunctions.opDateTimeGreaterThan(leftNormalized, rightNormalized, null).toBoolean()
|| OperationFunctions.opDateTimeEqual(leftNormalized, rightNormalized, null).toBoolean());
break;
}
case GT:
retval = OperationFunctions.opDateTimeGreaterThan(left, right, dynamicContext);
break;
case LE: {
IDateTimeItem leftNormalized = left.normalize(dynamicContext);
IDateTimeItem rightNormalized = right.normalize(dynamicContext);
// pre-normalize for efficiency
IDateTimeItem leftNormalized = dynamicContext == null ? left : left.normalize(dynamicContext);
IDateTimeItem rightNormalized = dynamicContext == null ? right : right.normalize(dynamicContext);
retval = IBooleanItem.valueOf(
OperationFunctions.opDateTimeLessThan(leftNormalized, rightNormalized, dynamicContext).toBoolean()
|| OperationFunctions.opDateTimeEqual(leftNormalized, rightNormalized, dynamicContext).toBoolean());
OperationFunctions.opDateTimeLessThan(leftNormalized, rightNormalized, null).toBoolean()
|| OperationFunctions.opDateTimeEqual(leftNormalized, rightNormalized, null).toBoolean());
break;
}
case LT:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@ default boolean isArityUnbounded() {

@Override
default boolean deepEquals(ICollectionValue other, DynamicContext dynamicContext) {
// this is the expected result
// this is always the expected result
return false;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -472,7 +472,7 @@ public static IDateTimeItem opSubtractDayTimeDurationFromDateTime(
public static IYearMonthDurationItem opMultiplyYearMonthDuration(
@NonNull IYearMonthDurationItem arg1,
@NonNull INumericItem arg2) {
IDecimalItem months = IDecimalItem.valueOf(arg1.asMonths());
IDecimalItem months = IDecimalItem.valueOf(arg1.asTotalMonths());
INumericItem result = months.multiply(arg2);
Period period;
try {
Expand Down Expand Up @@ -522,7 +522,7 @@ public static IDayTimeDurationItem opMultiplyDayTimeDuration(
public static IYearMonthDurationItem opDivideYearMonthDuration(
@NonNull IYearMonthDurationItem arg1,
@NonNull INumericItem arg2) {
IDecimalItem months = IDecimalItem.valueOf(arg1.asMonths());
IDecimalItem months = IDecimalItem.valueOf(arg1.asTotalMonths());
INumericItem result = months.divide(arg2);

Period period = Period.ofMonths(result.round().toIntValueExact());
Expand All @@ -544,8 +544,8 @@ public static IYearMonthDurationItem opDivideYearMonthDuration(
public static IDecimalItem opDivideYearMonthDurationByYearMonthDuration(
@NonNull IYearMonthDurationItem arg1,
@NonNull IYearMonthDurationItem arg2) {
IIntegerItem totalMonths1 = IIntegerItem.valueOf(arg1.asMonths());
IIntegerItem totalMonths2 = IIntegerItem.valueOf(arg2.asMonths());
IIntegerItem totalMonths1 = IIntegerItem.valueOf(arg1.asTotalMonths());
IIntegerItem totalMonths2 = IIntegerItem.valueOf(arg2.asTotalMonths());

return totalMonths1.divide(totalMonths2);
}
Expand Down Expand Up @@ -630,9 +630,9 @@ public static IBooleanItem opDateEqual(
public static IBooleanItem opDateTimeEqual(
@NonNull IDateTimeItem arg1,
@NonNull IDateTimeItem arg2,
@NonNull DynamicContext dynamicContext) {
IDateTimeItem arg1Normalized = arg1.normalize(dynamicContext);
IDateTimeItem arg2Normalized = arg2.normalize(dynamicContext);
@Nullable DynamicContext dynamicContext) {
IDateTimeItem arg1Normalized = dynamicContext == null ? arg1 : arg1.normalize(dynamicContext);
IDateTimeItem arg2Normalized = dynamicContext == null ? arg2 : arg2.normalize(dynamicContext);
return IBooleanItem.valueOf(arg1Normalized.asZonedDateTime().isEqual(arg2Normalized.asZonedDateTime()));
}

Expand Down Expand Up @@ -729,7 +729,7 @@ public static IBooleanItem opDateGreaterThan(
public static IBooleanItem opDateTimeGreaterThan(
@NonNull IDateTimeItem arg1,
@NonNull IDateTimeItem arg2,
@NonNull DynamicContext dynamicContext) {
@Nullable DynamicContext dynamicContext) {
return opDateTimeLessThan(arg2, arg1, dynamicContext);
}

Expand Down Expand Up @@ -870,9 +870,9 @@ public static IBooleanItem opTimeLessThan(
public static IBooleanItem opDateTimeLessThan(
@NonNull IDateTimeItem arg1,
@NonNull IDateTimeItem arg2,
@NonNull DynamicContext dynamicContext) {
IDateTimeItem arg1Normalized = arg1.normalize(dynamicContext);
IDateTimeItem arg2Normalized = arg2.normalize(dynamicContext);
@Nullable DynamicContext dynamicContext) {
IDateTimeItem arg1Normalized = dynamicContext == null ? arg1 : arg1.normalize(dynamicContext);
IDateTimeItem arg2Normalized = dynamicContext == null ? arg2 : arg2.normalize(dynamicContext);
return IBooleanItem.valueOf(arg1Normalized.asZonedDateTime().isBefore(arg2Normalized.asZonedDateTime()));
}

Expand All @@ -891,7 +891,7 @@ public static IBooleanItem opDateTimeLessThan(
public static IBooleanItem opYearMonthDurationLessThan(
@NonNull IYearMonthDurationItem arg1,
@NonNull IYearMonthDurationItem arg2) {
return IBooleanItem.valueOf(arg1.asMonths() < arg2.asMonths());
return IBooleanItem.valueOf(arg1.asTotalMonths() < arg2.asTotalMonths());
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,9 @@ private static ISequence<IAnyAtomicItem> executeOneArg(@NonNull IFunction functi
*
* @param values
* the items to get destinct values for
* @param dynamicContext
* used to provide evaluation information, including the implicit
* timezone
* @return a the list of distinct values
*/
@NonNull
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,9 @@ private static ISequence<IIntegerItem> executeTwoArg(@NonNull IFunction function
* the items to match against
* @param search
* the item to match
* @param dynamicContext
* used to provide evaluation information, including the implicit
* timezone
* @return a list of index numbers indicating the position of matches in the
* sequence
*/
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -8,25 +8,21 @@
import gov.nist.secauto.metaschema.core.metapath.item.function.IDecimalMapKey;
import gov.nist.secauto.metaschema.core.metapath.item.function.IMapKey;

import nl.talsmasoftware.lazy4j.Lazy;

/**
* An implementation of a {@link IMapKey} that uses a string-based value.
*/
public abstract class AbstractDecimalMapKey
extends AbstractMapKey
implements IDecimalMapKey {
private final Lazy<Integer> hashCode = Lazy.lazy(() -> asDecimal().stripTrailingZeros().hashCode());

@Override
public int hashCode() {
return hashCode.get();
}

/**
* {@inheritDoc}
* <p>
* The hash code is based on the least precision to ensure this matches the
* logic in {@link #isSameKey(IMapKey)}.
*/
@Override
public boolean equals(Object obj) {
return this == obj
// TODO: implement fn:codepoint-equal per spec
|| obj instanceof IDecimalMapKey && asDecimal().compareTo(((IDecimalMapKey) obj).asDecimal()) == 0;
protected int generateHashCode() {
return asDecimal().stripTrailingZeros().hashCode();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ public boolean deepEquals(ICollectionValue other, DynamicContext dynamicContext)
Map.Entry<IMapKey, ? extends ICollectionValue> i1 = thisIterator.next();
Map.Entry<IMapKey, ? extends ICollectionValue> i2 = otherIterator.next();

retval = i1.getKey().equals(i2.getKey())
retval = i1.getKey().isSameKey(ObjectUtils.notNull(i2.getKey()))
&& i1.getValue().deepEquals(i2.getValue(), dynamicContext);
if (!retval) {
break;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,30 @@

import gov.nist.secauto.metaschema.core.metapath.item.function.IMapKey;

import nl.talsmasoftware.lazy4j.Lazy;

public abstract class AbstractMapKey implements IMapKey {
private final Lazy<Integer> hashCode = Lazy.lazy(() -> generateHashCode());

@Override
public abstract int hashCode();
public int hashCode() {
return hashCode.get();
}

/**
* Generate the hash code for the key.
*
* @return the hash code
*/
protected int generateHashCode() {
return getKey().hashCode();
}

@Override
public abstract boolean equals(Object obj);
public boolean equals(Object obj) {
return this == obj
|| (obj instanceof IMapKey && isSameKey((IMapKey) obj));
}

@Override
public String toString() {
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,4 @@ public abstract class AbstractStringMapKey
public int hashCode() {
return asString().hashCode();
}

@Override
public boolean equals(Object obj) {
return this == obj
// TODO: implement fn:codepoint-equal per spec
|| obj instanceof IStringMapKey && asString().equals(((IStringMapKey) obj).asString());
}
}
Loading

0 comments on commit 78a1b1c

Please sign in to comment.