Skip to content

Commit

Permalink
Aligned atomic item key equality with XPath 3.1 specifications.
Browse files Browse the repository at this point in the history
  • Loading branch information
david-waltermire committed Jan 15, 2025
1 parent 3b4ac40 commit 4818859
Show file tree
Hide file tree
Showing 23 changed files with 783 additions and 381 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -362,7 +362,9 @@ public static IBooleanItem booleanCompare(@NonNull IBooleanItem left, @NonNull O
* @return the comparison result
*/
@NonNull
public static IBooleanItem dateTimeCompare(@NonNull IDateTimeItem left, @NonNull Operator operator,
public static IBooleanItem dateTimeCompare(
@NonNull IDateTimeItem left,
@NonNull Operator operator,
@NonNull IDateTimeItem right) {
IBooleanItem retval;
switch (operator) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@
public final class OperationFunctions {
@NonNull
private static final IDateItem DATE_1972_12_31 = IDateItem.valueOf(ObjectUtils.notNull(LocalDate.of(1972, 12, 31)));
@NonNull
private static final ITimeItem TIME_00_00_00 = ITimeItem.valueOf(OffsetTime.of(0, 0, 0, 0, ZoneOffset.UTC), false);

/**
* Identifies the types and substypes that support aggregation.
Expand Down Expand Up @@ -602,7 +604,9 @@ public static IDecimalItem opDivideDayTimeDurationByDayTimeDuration(
*/
@NonNull
public static IBooleanItem opDateEqual(@NonNull IDateItem arg1, @NonNull IDateItem arg2) {
return IBooleanItem.valueOf(arg1.asZonedDateTime().equals(arg2.asZonedDateTime()));
IDateTimeItem time1 = IDateTimeItem.valueOf(arg1, TIME_00_00_00);
IDateTimeItem time2 = IDateTimeItem.valueOf(arg2, TIME_00_00_00);
return opDateTimeEqual(time1, time2);
}

/**
Expand All @@ -618,7 +622,7 @@ public static IBooleanItem opDateEqual(@NonNull IDateItem arg1, @NonNull IDateIt
*/
@NonNull
public static IBooleanItem opDateTimeEqual(@NonNull IDateTimeItem arg1, @NonNull IDateTimeItem arg2) {
return IBooleanItem.valueOf(arg1.asZonedDateTime().equals(arg2.asZonedDateTime()));
return IBooleanItem.valueOf(arg1.asZonedDateTime().isEqual(arg2.asZonedDateTime()));
}

/**
Expand Down Expand Up @@ -700,7 +704,7 @@ public static IBooleanItem opDateGreaterThan(@NonNull IDateItem arg1, @NonNull I
*/
@NonNull
public static IBooleanItem opDateTimeGreaterThan(@NonNull IDateTimeItem arg1, @NonNull IDateTimeItem arg2) {
return IBooleanItem.valueOf(arg1.asZonedDateTime().compareTo(arg2.asZonedDateTime()) > 0);
return IBooleanItem.valueOf(arg1.asZonedDateTime().isAfter(arg2.asZonedDateTime()));
}

/**
Expand Down Expand Up @@ -828,7 +832,7 @@ public static IBooleanItem opTimeLessThan(
public static IBooleanItem opDateTimeLessThan(
@NonNull IDateTimeItem arg1,
@NonNull IDateTimeItem arg2) {
return IBooleanItem.valueOf(arg1.asZonedDateTime().compareTo(arg2.asZonedDateTime()) < 0);
return IBooleanItem.valueOf(arg1.asZonedDateTime().isBefore(arg2.asZonedDateTime()));
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/*
* SPDX-FileCopyrightText: none
* SPDX-License-Identifier: CC0-1.0
*/

package gov.nist.secauto.metaschema.core.metapath.impl;

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

public abstract class AbstractCalendarMapKey
extends AbstractMapKey
implements ICalendarMapKey {

@Override
public boolean equals(Object obj) {
return this == obj
|| obj instanceof ICalendarMapKey
&& getKey().equals(((ICalendarMapKey) obj).getKey());
}

@Override
public int hashCode() {
return getKey().hashCode();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,22 +8,25 @@
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 asDecimal().hashCode();
return hashCode.get();
}

@Override
public boolean equals(Object obj) {
return this == obj
// TODO: implement fn:codepoint-equal per spec
|| obj instanceof IDecimalMapKey && asDecimal().equals(((IDecimalMapKey) obj).asDecimal());
|| obj instanceof IDecimalMapKey && asDecimal().compareTo(((IDecimalMapKey) obj).asDecimal()) == 0;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,12 @@
import gov.nist.secauto.metaschema.core.metapath.item.function.IMapKey;

public abstract class AbstractMapKey implements IMapKey {
@Override
public abstract int hashCode();

@Override
public abstract boolean equals(Object obj);

@Override
public String toString() {
return getKey().toSignature();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,6 @@ public int hashCode() {
public boolean equals(Object obj) {
return this == obj
// TODO: implement fn:codepoint-equal per spec
|| obj instanceof IStringMapKey && getKey().asString().equals(((IStringMapKey) obj).getKey().asString());
|| obj instanceof IStringMapKey && asString().equals(((IStringMapKey) obj).asString());
}
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,11 @@ default IAtomicOrUnionType<? extends IDateItem> getType() {
return type();
}

@Override
default Class<IDateItem> getItemBaseType() {
return IDateItem.class;
}

/**
* Construct a new date item using the provided string {@code value}.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.OffsetTime;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.time.temporal.ChronoUnit;
Expand Down Expand Up @@ -50,6 +51,11 @@ default IAtomicOrUnionType<? extends IDateTimeItem> getType() {
return type();
}

@Override
default Class<IDateTimeItem> getItemBaseType() {
return IDateTimeItem.class;
}

/**
* Construct a new date/time item using the provided string {@code value}.
*
Expand All @@ -73,12 +79,32 @@ static IDateTimeItem valueOf(@NonNull String value) {

@NonNull
static IDateTimeItem valueOf(@NonNull IDateItem date, @NonNull ITimeItem time) {
throw new UnsupportedOperationException();
ZonedDateTime zDate = ObjectUtils.notNull(date.asZonedDateTime());
ZoneId tzDate = date.hasTimezone() ? zDate.getZone() : null;
OffsetTime oTime = ObjectUtils.notNull(time.asOffsetTime());
ZoneId tzTime = time.hasTimezone() ? oTime.getOffset() : null;

if (tzDate != null && tzTime != null && !tzDate.equals(tzTime)) {
// exception

}

// either both have the same timezone, both are null, or only one has a timezone
ZoneId zone = tzDate == null
? tzTime == null ? null : tzTime
: tzDate;

return valueOf(
ObjectUtils.notNull(ZonedDateTime.of(
zDate.toLocalDate(),
oTime.toLocalTime(),
zone == null ? ZoneOffset.UTC : zone)),
zone != null);
}

@NonNull
static IDateTimeItem valueOf(@NonNull ICalendarTemporalItem date) {
return valueOf(date.asZonedDateTime(), date.hasTimezone());
static IDateTimeItem valueOf(@NonNull ICalendarTemporalItem item) {
return valueOf(item.asZonedDateTime(), item.hasTimezone());
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@
*/
public interface ITemporalItem extends IAnyAtomicItem {

@NonNull
Class<? extends ITemporalItem> getItemBaseType();

int getYear();

int getMonth();
Expand Down Expand Up @@ -113,14 +116,4 @@ default IDayTimeDurationItem getOffset() {
*/
@NonNull
ITemporalItem replaceTimezone(@Nullable IDayTimeDurationItem offset);

/**
* Compares this value with the argument.
*
* @param item
* the item to compare with this value
* @return a negative integer, zero, or a positive integer if this value is less
* than, equal to, or greater than the {@code item}.
*/
int compareTo(@NonNull ITemporalItem item);
}
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,11 @@ default IAtomicOrUnionType<? extends ITimeItem> getType() {
return type();
}

@Override
default Class<ITimeItem> getItemBaseType() {
return ITimeItem.class;
}

/**
* Construct a new date/time item using the provided string {@code value}.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,11 @@

package gov.nist.secauto.metaschema.core.metapath.item.atomic.impl;

import gov.nist.secauto.metaschema.core.metapath.impl.AbstractCalendarMapKey;
import gov.nist.secauto.metaschema.core.metapath.item.atomic.IDateItem;
import gov.nist.secauto.metaschema.core.metapath.item.function.IMapKey;

import java.time.ZoneOffset;

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

Expand All @@ -31,13 +35,36 @@ protected AbstractDateItem(@NonNull TYPE value) {

@Override
public int hashCode() {
return asZonedDateTime().hashCode();
int result = asZonedDateTime().withZoneSameInstant(ZoneOffset.UTC).hashCode();
result = hasTimezone() ? 31 * result * Boolean.hashCode(hasTimezone()) : result;
return 31 * result * getClass().hashCode();
}

@SuppressWarnings("PMD.OnlyOneReturn")
@Override
public boolean equals(Object obj) {
return this == obj
|| obj instanceof IDateItem && compareTo((IDateItem) obj) == 0;
if (this == obj) {
return true;
}

if (obj instanceof IDateItem) {
IDateItem that = (IDateItem) obj;
return hasTimezone() == that.hasTimezone()
&& deepEquals(that);
}
return false;
}

@Override
public IMapKey asMapKey() {
return new MapKey();
}

private final class MapKey
extends AbstractCalendarMapKey {
@Override
public IDateItem getKey() {
return AbstractDateItem.this;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,11 @@

package gov.nist.secauto.metaschema.core.metapath.item.atomic.impl;

import gov.nist.secauto.metaschema.core.metapath.impl.AbstractCalendarMapKey;
import gov.nist.secauto.metaschema.core.metapath.item.atomic.IDateTimeItem;
import gov.nist.secauto.metaschema.core.metapath.item.function.IMapKey;

import java.time.ZoneOffset;

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

Expand All @@ -31,13 +35,35 @@ protected AbstractDateTimeItem(@NonNull TYPE value) {

@Override
public int hashCode() {
return asZonedDateTime().hashCode();
int result = asZonedDateTime().withZoneSameInstant(ZoneOffset.UTC).hashCode();
result = hasTimezone() ? 31 * result * Boolean.hashCode(hasTimezone()) : result;
return 31 * result * getClass().hashCode();
}

@SuppressWarnings("PMD.OnlyOneReturn")
@Override
public boolean equals(Object obj) {
return this == obj
|| obj instanceof IDateTimeItem && compareTo((IDateTimeItem) obj) == 0;
if (this == obj) {
return true;
}

if (obj instanceof IDateTimeItem) {
IDateTimeItem that = (IDateTimeItem) obj;
return hasTimezone() == that.hasTimezone() && deepEquals(that);
}
return false;
}

@Override
public IMapKey asMapKey() {
return new MapKey();
}

private final class MapKey
extends AbstractCalendarMapKey {
@Override
public IDateTimeItem getKey() {
return AbstractDateTimeItem.this;
}
}
}
Loading

0 comments on commit 4818859

Please sign in to comment.