--- old/jaxp/src/com/sun/org/apache/xerces/internal/jaxp/datatype/DurationImpl.java Fri Jun 20 16:59:56 2014 +++ new/jaxp/src/com/sun/org/apache/xerces/internal/jaxp/datatype/DurationImpl.java Fri Jun 20 16:59:56 2014 @@ -21,6 +21,7 @@ package com.sun.org.apache.xerces.internal.jaxp.datatype; +import com.sun.org.apache.xerces.internal.util.DatatypeMessageFormatter; import java.io.IOException; import java.io.ObjectStreamException; import java.io.Serializable; @@ -30,13 +31,10 @@ import java.util.Date; import java.util.GregorianCalendar; import java.util.TimeZone; - import javax.xml.datatype.DatatypeConstants; import javax.xml.datatype.Duration; import javax.xml.datatype.XMLGregorianCalendar; -import com.sun.org.apache.xerces.internal.util.DatatypeMessageFormatter; - /** *

Immutable representation of a time span as defined in * the W3C XML Schema 1.0 specification.

@@ -120,17 +118,6 @@ DatatypeConstants.SECONDS }; - /** - *

Internal array of value Field ids.

- */ - private static final int[] FIELD_IDS = { - DatatypeConstants.YEARS.getId(), - DatatypeConstants.MONTHS.getId(), - DatatypeConstants.DAYS.getId(), - DatatypeConstants.HOURS.getId(), - DatatypeConstants.MINUTES.getId(), - DatatypeConstants.SECONDS.getId() - }; /** * TimeZone for GMT. @@ -137,12 +124,18 @@ */ private static final TimeZone GMT = TimeZone.getTimeZone("GMT"); - /** + /** *

BigDecimal value of 0.

*/ private static final BigDecimal ZERO = BigDecimal.valueOf(0); /** + * BigInteger value of Integer's max value.

+ */ + private static final BigInteger MaxIntAsBigInt = + BigInteger.valueOf((long) Integer.MAX_VALUE); + + /** *

Indicates the sign. -1, 0 or 1 if the duration is negative, * zero, or positive.

*/ @@ -195,12 +188,12 @@ return signum; } - /** - * TODO: Javadoc - * @param isPositive Sign. - * - * @return 1 if positive, else -1. - */ + /** + * Determine the sign of the duration. + * + * @param isPositive Sign. + * @return 1 if positive, -1 negative, or 0 if all fields are zero. + */ protected int calcSignum(boolean isPositive) { if ((years == null || years.signum() == 0) && (months == null || months.signum() == 0) @@ -694,209 +687,71 @@ XMLGregorianCalendarImpl.parse("1903-07-01T00:00:00Z") }; - /** - *

Partial order relation comparison with this Duration instance.

- * - *

Comparison result must be in accordance with - * W3C XML Schema 1.0 Part 2, Section 3.2.7.6.2, - * Order relation on duration.

- * - *

Return:

- * - * - * @param duration to compare - * - * @return the relationship between this Durationand duration parameter as - * {@link DatatypeConstants#LESSER}, {@link DatatypeConstants#EQUAL}, {@link DatatypeConstants#GREATER} - * or {@link DatatypeConstants#INDETERMINATE}. - * - * @throws UnsupportedOperationException If the underlying implementation - * cannot reasonably process the request, e.g. W3C XML Schema allows for - * arbitrarily large/small/precise values, the request may be beyond the - * implementations capability. - * @throws NullPointerException if duration is null. - * - * @see #isShorterThan(Duration) - * @see #isLongerThan(Duration) - */ + /** + *

Partial order relation comparison with this Duration instance.

+ * + *

Comparison result must be in accordance with + * W3C XML Schema 1.0 Part 2, Section 3.2.7.6.2, + * Order relation on duration.

+ * + *

Return:

+ * + * + * @param duration to compare + * + * @return the relationship between this Durationand duration parameter as + * {@link DatatypeConstants#LESSER}, {@link DatatypeConstants#EQUAL}, {@link DatatypeConstants#GREATER} + * or {@link DatatypeConstants#INDETERMINATE}. + * + * @throws UnsupportedOperationException If the underlying implementation + * cannot reasonably process the request, e.g. W3C XML Schema allows for + * arbitrarily large/small/precise values, the request may be beyond the + * implementations capability. + * @throws NullPointerException if duration is null. + * + * @see #isShorterThan(Duration) + * @see #isLongerThan(Duration) + */ public int compare(Duration rhs) { - - BigInteger maxintAsBigInteger = BigInteger.valueOf(Integer.MAX_VALUE); - - // check for fields that are too large in this Duration - if (years != null && years.compareTo(maxintAsBigInteger) == 1) { - throw new UnsupportedOperationException( - DatatypeMessageFormatter.formatMessage(null, "TooLarge", - new Object[]{this.getClass().getName() + "#compare(Duration duration)" + DatatypeConstants.YEARS.toString(), years.toString()}) - //this.getClass().getName() + "#compare(Duration duration)" - //+ " years too large to be supported by this implementation " - //+ years.toString() - ); + /** check if any field in the Durations is too large for the operation + * that uses XMLGregorianCalendar for comparison + */ + for (DatatypeConstants.Field field : FIELDS) { + checkMaxValue(getField(field), field); + checkMaxValue(rhs.getField(field), field); } - if (months != null && months.compareTo(maxintAsBigInteger) == 1) { - throw new UnsupportedOperationException( - DatatypeMessageFormatter.formatMessage(null, "TooLarge", - new Object[]{this.getClass().getName() + "#compare(Duration duration)" + DatatypeConstants.MONTHS.toString(), months.toString()}) - //this.getClass().getName() + "#compare(Duration duration)" - //+ " months too large to be supported by this implementation " - //+ months.toString() - ); - } - if (days != null && days.compareTo(maxintAsBigInteger) == 1) { - throw new UnsupportedOperationException( - DatatypeMessageFormatter.formatMessage(null, "TooLarge", - new Object[]{this.getClass().getName() + "#compare(Duration duration)" + DatatypeConstants.DAYS.toString(), days.toString()}) + return compareDates(this, rhs); + } - //this.getClass().getName() + "#compare(Duration duration)" - //+ " days too large to be supported by this implementation " - //+ days.toString() - ); + /** + * Check if a field exceeds the maximum value + * @param field the value of a field + * @param fieldType type of the field, e.g. year, month, day, hour, minute or second. + */ + private void checkMaxValue(Number field, DatatypeConstants.Field fieldType) { + BigInteger fieldValue = null; + if (fieldType != DatatypeConstants.SECONDS) { + fieldValue = (BigInteger) field; + } else { + BigDecimal rhsSecondsAsBigDecimal = (BigDecimal) field; + if ( rhsSecondsAsBigDecimal != null ) { + fieldValue = rhsSecondsAsBigDecimal.toBigInteger(); + } } - if (hours != null && hours.compareTo(maxintAsBigInteger) == 1) { - throw new UnsupportedOperationException( - DatatypeMessageFormatter.formatMessage(null, "TooLarge", - new Object[]{this.getClass().getName() + "#compare(Duration duration)" + DatatypeConstants.HOURS.toString(), hours.toString()}) - - //this.getClass().getName() + "#compare(Duration duration)" - //+ " hours too large to be supported by this implementation " - //+ hours.toString() - ); + + if (fieldValue != null && fieldValue.compareTo(MaxIntAsBigInt) == 1) { + throw new UnsupportedOperationException( + DatatypeMessageFormatter.formatMessage(null, "TooLarge", + new Object[]{this.getClass().getName() + "#compare(Duration duration)" + + fieldType, field.toString()}) + ); } - if (minutes != null && minutes.compareTo(maxintAsBigInteger) == 1) { - throw new UnsupportedOperationException( - DatatypeMessageFormatter.formatMessage(null, "TooLarge", - new Object[]{this.getClass().getName() + "#compare(Duration duration)" + DatatypeConstants.MINUTES.toString(), minutes.toString()}) - - //this.getClass().getName() + "#compare(Duration duration)" - //+ " minutes too large to be supported by this implementation " - //+ minutes.toString() - ); - } - if (seconds != null && seconds.toBigInteger().compareTo(maxintAsBigInteger) == 1) { - throw new UnsupportedOperationException( - DatatypeMessageFormatter.formatMessage(null, "TooLarge", - new Object[]{this.getClass().getName() + "#compare(Duration duration)" + DatatypeConstants.SECONDS.toString(), toString(seconds)}) - - //this.getClass().getName() + "#compare(Duration duration)" - //+ " seconds too large to be supported by this implementation " - //+ seconds.toString() - ); - } - - // check for fields that are too large in rhs Duration - BigInteger rhsYears = (BigInteger) rhs.getField(DatatypeConstants.YEARS); - if (rhsYears != null && rhsYears.compareTo(maxintAsBigInteger) == 1) { - throw new UnsupportedOperationException( - DatatypeMessageFormatter.formatMessage(null, "TooLarge", - new Object[]{this.getClass().getName() + "#compare(Duration duration)" + DatatypeConstants.YEARS.toString(), rhsYears.toString()}) - - //this.getClass().getName() + "#compare(Duration duration)" - //+ " years too large to be supported by this implementation " - //+ rhsYears.toString() - ); - } - BigInteger rhsMonths = (BigInteger) rhs.getField(DatatypeConstants.MONTHS); - if (rhsMonths != null && rhsMonths.compareTo(maxintAsBigInteger) == 1) { - throw new UnsupportedOperationException( - DatatypeMessageFormatter.formatMessage(null, "TooLarge", - new Object[]{this.getClass().getName() + "#compare(Duration duration)" + DatatypeConstants.MONTHS.toString(), rhsMonths.toString()}) - - //this.getClass().getName() + "#compare(Duration duration)" - //+ " months too large to be supported by this implementation " - //+ rhsMonths.toString() - ); - } - BigInteger rhsDays = (BigInteger) rhs.getField(DatatypeConstants.DAYS); - if (rhsDays != null && rhsDays.compareTo(maxintAsBigInteger) == 1) { - throw new UnsupportedOperationException( - DatatypeMessageFormatter.formatMessage(null, "TooLarge", - new Object[]{this.getClass().getName() + "#compare(Duration duration)" + DatatypeConstants.DAYS.toString(), rhsDays.toString()}) - - //this.getClass().getName() + "#compare(Duration duration)" - //+ " days too large to be supported by this implementation " - //+ rhsDays.toString() - ); - } - BigInteger rhsHours = (BigInteger) rhs.getField(DatatypeConstants.HOURS); - if (rhsHours != null && rhsHours.compareTo(maxintAsBigInteger) == 1) { - throw new UnsupportedOperationException( - DatatypeMessageFormatter.formatMessage(null, "TooLarge", - new Object[]{this.getClass().getName() + "#compare(Duration duration)" + DatatypeConstants.HOURS.toString(), rhsHours.toString()}) - - //this.getClass().getName() + "#compare(Duration duration)" - //+ " hours too large to be supported by this implementation " - //+ rhsHours.toString() - ); - } - BigInteger rhsMinutes = (BigInteger) rhs.getField(DatatypeConstants.MINUTES); - if (rhsMinutes != null && rhsMinutes.compareTo(maxintAsBigInteger) == 1) { - throw new UnsupportedOperationException( - DatatypeMessageFormatter.formatMessage(null, "TooLarge", - new Object[]{this.getClass().getName() + "#compare(Duration duration)" + DatatypeConstants.MINUTES.toString(), rhsMinutes.toString()}) - - //this.getClass().getName() + "#compare(Duration duration)" - //+ " minutes too large to be supported by this implementation " - //+ rhsMinutes.toString() - ); - } - BigDecimal rhsSecondsAsBigDecimal = (BigDecimal) rhs.getField(DatatypeConstants.SECONDS); - BigInteger rhsSeconds = null; - if ( rhsSecondsAsBigDecimal != null ) { - rhsSeconds = rhsSecondsAsBigDecimal.toBigInteger(); - } - if (rhsSeconds != null && rhsSeconds.compareTo(maxintAsBigInteger) == 1) { - throw new UnsupportedOperationException( - DatatypeMessageFormatter.formatMessage(null, "TooLarge", - new Object[]{this.getClass().getName() + "#compare(Duration duration)" + DatatypeConstants.SECONDS.toString(), rhsSeconds.toString()}) - - //this.getClass().getName() + "#compare(Duration duration)" - //+ " seconds too large to be supported by this implementation " - //+ rhsSeconds.toString() - ); - } - - // turn this Duration into a GregorianCalendar - GregorianCalendar lhsCalendar = new GregorianCalendar( - 1970, - 1, - 1, - 0, - 0, - 0); - lhsCalendar.add(GregorianCalendar.YEAR, getYears() * getSign()); - lhsCalendar.add(GregorianCalendar.MONTH, getMonths() * getSign()); - lhsCalendar.add(GregorianCalendar.DAY_OF_YEAR, getDays() * getSign()); - lhsCalendar.add(GregorianCalendar.HOUR_OF_DAY, getHours() * getSign()); - lhsCalendar.add(GregorianCalendar.MINUTE, getMinutes() * getSign()); - lhsCalendar.add(GregorianCalendar.SECOND, getSeconds() * getSign()); - - // turn compare Duration into a GregorianCalendar - GregorianCalendar rhsCalendar = new GregorianCalendar( - 1970, - 1, - 1, - 0, - 0, - 0); - rhsCalendar.add(GregorianCalendar.YEAR, rhs.getYears() * rhs.getSign()); - rhsCalendar.add(GregorianCalendar.MONTH, rhs.getMonths() * rhs.getSign()); - rhsCalendar.add(GregorianCalendar.DAY_OF_YEAR, rhs.getDays() * rhs.getSign()); - rhsCalendar.add(GregorianCalendar.HOUR_OF_DAY, rhs.getHours() * rhs.getSign()); - rhsCalendar.add(GregorianCalendar.MINUTE, rhs.getMinutes() * rhs.getSign()); - rhsCalendar.add(GregorianCalendar.SECOND, rhs.getSeconds() * rhs.getSign()); - - - if (lhsCalendar.equals(rhsCalendar)) { - return DatatypeConstants.EQUAL; - } - - return compareDates(this, rhs); } /** --- /dev/null Fri Jun 20 16:59:58 2014 +++ new/jdk/test/javax/xml/jaxp/datatype/5077522/DurationComparison.java Fri Jun 20 16:59:57 2014 @@ -0,0 +1,201 @@ +/* + * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * @test + * @bug 5077522 + * @summary test INDETERMINATE relations + * @run main DurationComparison + */ +import javax.xml.datatype.DatatypeConfigurationException; +import javax.xml.datatype.DatatypeConstants; +import javax.xml.datatype.DatatypeFactory; +import javax.xml.datatype.Duration; + +/** + * + * This is a JCK failure. The implementation fails with a list of + * INDETERMINATE comparisons + * + * + * @author Joe Wang + */ +public class DurationComparison { + static String errMsg; + int passed = 0, failed = 0; + + public static void main(String[] args) { + DurationComparison test = new DurationComparison(); + test.testCompareWithInderterminateRelation(); + test.testVerifyOtherRelations(); + test.tearDown(); + } + + /** + * See JDK-5077522, Duration.compare returns equal for INDETERMINATE + * comparisons + */ + public void testCompareWithInderterminateRelation() { + + final String [][] partialOrder = { // partialOrder + {"P1Y", "<>", "P365D"}, + {"P1Y", "<>", "P366D"}, + {"P1M", "<>", "P28D"}, + {"P1M", "<>", "P29D"}, + {"P1M", "<>", "P30D"}, + {"P1M", "<>", "P31D"}, + {"P5M", "<>", "P150D"}, + {"P5M", "<>", "P151D"}, + {"P5M", "<>", "P152D"}, + {"P5M", "<>", "P153D"}, + {"PT2419200S", "<>", "P1M"}, + {"PT2678400S", "<>", "P1M"}, + {"PT31536000S", "<>", "P1Y"}, + {"PT31622400S", "<>", "P1Y"}, + {"PT525600M", "<>", "P1Y"}, + {"PT527040M", "<>", "P1Y"}, + {"PT8760H", "<>", "P1Y"}, + {"PT8784H", "<>", "P1Y"}, + {"P365D", "<>", "P1Y"}, + }; + + DatatypeFactory df = null; + try { + df = DatatypeFactory.newInstance(); + } catch (DatatypeConfigurationException ex) { + ex.printStackTrace(); + fail(ex.toString()); + } + + for (int valueIndex = 0; valueIndex < partialOrder.length; ++valueIndex) { + Duration duration1 = df.newDuration(partialOrder[valueIndex][0]); + Duration duration2 = df.newDuration(partialOrder[valueIndex][2]); + int cmp = duration1.compare(duration2); + int expected = ">".equals(partialOrder[valueIndex][1]) + ? DatatypeConstants.GREATER + : "<".equals(partialOrder[valueIndex][1]) + ? DatatypeConstants.LESSER + : "==".equals(partialOrder[valueIndex][1]) + ? DatatypeConstants.EQUAL + : DatatypeConstants.INDETERMINATE; + + if (expected != cmp) { + fail("returned " + cmp2str(cmp) + + " for durations \'" + duration1 + "\' and " + + duration2 + "\', but expected " + cmp2str(expected)); + } else { + success("Comparing " + duration1 + " and " + duration2 + + ": INDETERMINATE"); + } + } + } + + /** + * Verify cases around the INDETERMINATE relations + */ + public void testVerifyOtherRelations() { + + final String [][] partialOrder = { // partialOrder + {"P1Y", ">", "P364D"}, + {"P1Y", "<", "P367D"}, + {"P1Y2D", ">", "P366D"}, + {"P1M", ">", "P27D"}, + {"P1M", "<", "P32D"}, + {"P1M", "<", "P31DT1H"}, + {"P5M", ">", "P149D"}, + {"P5M", "<", "P154D"}, + {"P5M", "<", "P153DT1H"}, + {"PT2419199S", "<", "P1M"}, //PT2419200S -> P28D + {"PT2678401S", ">", "P1M"}, //PT2678400S -> P31D + {"PT31535999S", "<", "P1Y"}, //PT31536000S -> P365D + {"PT31622401S", ">", "P1Y"}, //PT31622400S -> P366D + {"PT525599M59S", "<", "P1Y"}, //PT525600M -> P365D + {"PT527040M1S", ">", "P1Y"}, //PT527040M -> P366D + {"PT8759H59M59S", "<", "P1Y"}, //PT8760H -> P365D + {"PT8784H1S", ">", "P1Y"}, //PT8784H -> P366D + }; + + DatatypeFactory df = null; + try { + df = DatatypeFactory.newInstance(); + } catch (DatatypeConfigurationException ex) { + ex.printStackTrace(); + fail(ex.toString()); + } + + for (int valueIndex = 0; valueIndex < partialOrder.length; ++valueIndex) { + Duration duration1 = df.newDuration(partialOrder[valueIndex][0]); + Duration duration2 = df.newDuration(partialOrder[valueIndex][2]); + int cmp = duration1.compare(duration2); + int expected = ">".equals(partialOrder[valueIndex][1]) + ? DatatypeConstants.GREATER + : "<".equals(partialOrder[valueIndex][1]) + ? DatatypeConstants.LESSER + : "==".equals(partialOrder[valueIndex][1]) + ? DatatypeConstants.EQUAL + : DatatypeConstants.INDETERMINATE; + + if (expected != cmp) { + fail("returned " + cmp2str(cmp) + + " for durations \'" + duration1 + "\' and " + + duration2 + "\', but expected " + cmp2str(expected)); + } else { + success("Comparing " + duration1 + " and " + duration2 + + ": expected: " + cmp2str(expected) + + " actual: " + cmp2str(cmp)); + } + } + } + public static String cmp2str(int cmp) { + return cmp == DatatypeConstants.LESSER ? "LESSER" + : cmp == DatatypeConstants.GREATER ? "GREATER" + : cmp == DatatypeConstants.EQUAL ? "EQUAL" + : cmp == DatatypeConstants.INDETERMINATE ? "INDETERMINATE" + : "UNDEFINED"; + } + + public void tearDown() { + + System.out.println("\nNumber of tests passed: " + passed); + System.out.println("Number of tests failed: " + failed + "\n"); + + if (errMsg != null ) { + throw new RuntimeException(errMsg); + } + } + void fail(String msg) { + if (errMsg == null) { + errMsg = msg; + } else { + errMsg = errMsg + "\n" + msg; + } + failed++; + } + + void success(String msg) { + passed++; + System.out.println(msg); + } + +} +