12d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert// © 2016 and later: Unicode, Inc. and others. 22d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert// License & terms of use: http://www.unicode.org/copyright.html#License 37935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert/* 47935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert ******************************************************************************* 5145e2c500373e5096c471c25be1dcfd3585e1b46Markus Scherer * Copyright (C) 2007-2015, International Business Machines Corporation and 6145e2c500373e5096c471c25be1dcfd3585e1b46Markus Scherer * others. All Rights Reserved. 77935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert ******************************************************************************* 87935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert */ 97935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertpackage com.ibm.icu.dev.test.duration; 107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 11145e2c500373e5096c471c25be1dcfd3585e1b46Markus Schererimport java.math.BigDecimal; 127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.text.FieldPosition; 137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.util.Date; 14145e2c500373e5096c471c25be1dcfd3585e1b46Markus Schererimport java.util.HashMap; 15145e2c500373e5096c471c25be1dcfd3585e1b46Markus Schererimport java.util.Map; 167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.util.MissingResourceException; 177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 18145e2c500373e5096c471c25be1dcfd3585e1b46Markus Schererimport javax.xml.datatype.DatatypeConstants; 19f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubertimport javax.xml.datatype.DatatypeConstants.Field; 207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport javax.xml.datatype.Duration; 217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 222d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubertimport org.junit.Test; 23fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubertimport org.junit.runner.RunWith; 24fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubertimport org.junit.runners.JUnit4; 252d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert 267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport com.ibm.icu.dev.test.TestFmwk; 277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport com.ibm.icu.text.DurationFormat; 287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport com.ibm.icu.util.Calendar; 297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport com.ibm.icu.util.ULocale; 307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert/** 327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @author srl 337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * 347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert */ 35fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert@RunWith(JUnit4.class) 367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertpublic class ICUDurationTest extends TestFmwk { 37145e2c500373e5096c471c25be1dcfd3585e1b46Markus Scherer /** 38145e2c500373e5096c471c25be1dcfd3585e1b46Markus Scherer * Allows us to not depend on javax.xml.datatype.DatatypeFactory. 39145e2c500373e5096c471c25be1dcfd3585e1b46Markus Scherer * We need just a tiny subset of the Duration API: 40145e2c500373e5096c471c25be1dcfd3585e1b46Markus Scherer * The ICU DurationFormat just extracts the field values, 41145e2c500373e5096c471c25be1dcfd3585e1b46Markus Scherer * to convert the Duration into an internal Period type. 42145e2c500373e5096c471c25be1dcfd3585e1b46Markus Scherer */ 43145e2c500373e5096c471c25be1dcfd3585e1b46Markus Scherer private static final class ICUTestDuration extends javax.xml.datatype.Duration { 44145e2c500373e5096c471c25be1dcfd3585e1b46Markus Scherer private final int sign; 45145e2c500373e5096c471c25be1dcfd3585e1b46Markus Scherer // Duration docs say BigInteger/BigDecimal but 46145e2c500373e5096c471c25be1dcfd3585e1b46Markus Scherer // ICU only cares about intValue() and floatValue(). 47145e2c500373e5096c471c25be1dcfd3585e1b46Markus Scherer private final Map<Field, Number> fields; 48145e2c500373e5096c471c25be1dcfd3585e1b46Markus Scherer 49145e2c500373e5096c471c25be1dcfd3585e1b46Markus Scherer ICUTestDuration(long millis) { 50145e2c500373e5096c471c25be1dcfd3585e1b46Markus Scherer fields = new HashMap<Field, Number>(); 51145e2c500373e5096c471c25be1dcfd3585e1b46Markus Scherer if (millis > 0) { 52145e2c500373e5096c471c25be1dcfd3585e1b46Markus Scherer sign = 1; 53145e2c500373e5096c471c25be1dcfd3585e1b46Markus Scherer } else if (millis == 0) { 54145e2c500373e5096c471c25be1dcfd3585e1b46Markus Scherer sign = 0; 55145e2c500373e5096c471c25be1dcfd3585e1b46Markus Scherer return; 56145e2c500373e5096c471c25be1dcfd3585e1b46Markus Scherer } else { 57145e2c500373e5096c471c25be1dcfd3585e1b46Markus Scherer sign = -1; 58145e2c500373e5096c471c25be1dcfd3585e1b46Markus Scherer millis = -millis; 59145e2c500373e5096c471c25be1dcfd3585e1b46Markus Scherer } 60145e2c500373e5096c471c25be1dcfd3585e1b46Markus Scherer long d = millis / 86400000L; 61145e2c500373e5096c471c25be1dcfd3585e1b46Markus Scherer millis %= 86400000L; 62145e2c500373e5096c471c25be1dcfd3585e1b46Markus Scherer if (d > 0) { 63145e2c500373e5096c471c25be1dcfd3585e1b46Markus Scherer fields.put(DatatypeConstants.DAYS, d); 64145e2c500373e5096c471c25be1dcfd3585e1b46Markus Scherer } 65145e2c500373e5096c471c25be1dcfd3585e1b46Markus Scherer long h = millis / 3600000L; 66145e2c500373e5096c471c25be1dcfd3585e1b46Markus Scherer millis %= 3600000L; 67145e2c500373e5096c471c25be1dcfd3585e1b46Markus Scherer if (h > 0) { 68145e2c500373e5096c471c25be1dcfd3585e1b46Markus Scherer fields.put(DatatypeConstants.HOURS, h); 69145e2c500373e5096c471c25be1dcfd3585e1b46Markus Scherer } 70145e2c500373e5096c471c25be1dcfd3585e1b46Markus Scherer long m = millis / 60000L; 71145e2c500373e5096c471c25be1dcfd3585e1b46Markus Scherer millis %= 60000L; 72145e2c500373e5096c471c25be1dcfd3585e1b46Markus Scherer if (m > 0) { 73145e2c500373e5096c471c25be1dcfd3585e1b46Markus Scherer fields.put(DatatypeConstants.MINUTES, m); 74145e2c500373e5096c471c25be1dcfd3585e1b46Markus Scherer } 75145e2c500373e5096c471c25be1dcfd3585e1b46Markus Scherer fields.put(DatatypeConstants.SECONDS, (float)millis / 1000); 76145e2c500373e5096c471c25be1dcfd3585e1b46Markus Scherer } 77145e2c500373e5096c471c25be1dcfd3585e1b46Markus Scherer 78145e2c500373e5096c471c25be1dcfd3585e1b46Markus Scherer /** 79145e2c500373e5096c471c25be1dcfd3585e1b46Markus Scherer * Pass in negative values for fields not to be set. 80145e2c500373e5096c471c25be1dcfd3585e1b46Markus Scherer */ 81145e2c500373e5096c471c25be1dcfd3585e1b46Markus Scherer ICUTestDuration(int sgn, int y, int months, int d, int h, int m, float s) { 82145e2c500373e5096c471c25be1dcfd3585e1b46Markus Scherer sign = sgn; 83145e2c500373e5096c471c25be1dcfd3585e1b46Markus Scherer fields = new HashMap<Field, Number>(); 84145e2c500373e5096c471c25be1dcfd3585e1b46Markus Scherer if (y >= 0) { fields.put(DatatypeConstants.YEARS, y); } 85145e2c500373e5096c471c25be1dcfd3585e1b46Markus Scherer if (months >= 0) { fields.put(DatatypeConstants.MONTHS, months); } 86145e2c500373e5096c471c25be1dcfd3585e1b46Markus Scherer if (d >= 0) { fields.put(DatatypeConstants.DAYS, d); } 87145e2c500373e5096c471c25be1dcfd3585e1b46Markus Scherer if (h >= 0) { fields.put(DatatypeConstants.HOURS, h); } 88145e2c500373e5096c471c25be1dcfd3585e1b46Markus Scherer if (m >= 0) { fields.put(DatatypeConstants.MINUTES, m); } 89145e2c500373e5096c471c25be1dcfd3585e1b46Markus Scherer if (s >= 0) { fields.put(DatatypeConstants.SECONDS, s); } 90145e2c500373e5096c471c25be1dcfd3585e1b46Markus Scherer } 91145e2c500373e5096c471c25be1dcfd3585e1b46Markus Scherer 92145e2c500373e5096c471c25be1dcfd3585e1b46Markus Scherer private ICUTestDuration(int sgn, Map<Field, Number> f) { 93145e2c500373e5096c471c25be1dcfd3585e1b46Markus Scherer sign = sgn; 94145e2c500373e5096c471c25be1dcfd3585e1b46Markus Scherer fields = f; 95145e2c500373e5096c471c25be1dcfd3585e1b46Markus Scherer } 96145e2c500373e5096c471c25be1dcfd3585e1b46Markus Scherer 97145e2c500373e5096c471c25be1dcfd3585e1b46Markus Scherer @Override 98145e2c500373e5096c471c25be1dcfd3585e1b46Markus Scherer public Duration add(Duration rhs) { 99145e2c500373e5096c471c25be1dcfd3585e1b46Markus Scherer throw new UnsupportedOperationException(); 100145e2c500373e5096c471c25be1dcfd3585e1b46Markus Scherer } 101145e2c500373e5096c471c25be1dcfd3585e1b46Markus Scherer 102145e2c500373e5096c471c25be1dcfd3585e1b46Markus Scherer @Override 103145e2c500373e5096c471c25be1dcfd3585e1b46Markus Scherer public void addTo(java.util.Calendar calendar) { 104145e2c500373e5096c471c25be1dcfd3585e1b46Markus Scherer throw new UnsupportedOperationException(); 105145e2c500373e5096c471c25be1dcfd3585e1b46Markus Scherer } 106145e2c500373e5096c471c25be1dcfd3585e1b46Markus Scherer 107145e2c500373e5096c471c25be1dcfd3585e1b46Markus Scherer @Override 108145e2c500373e5096c471c25be1dcfd3585e1b46Markus Scherer public int compare(Duration duration) { 109145e2c500373e5096c471c25be1dcfd3585e1b46Markus Scherer throw new UnsupportedOperationException(); 110145e2c500373e5096c471c25be1dcfd3585e1b46Markus Scherer } 111145e2c500373e5096c471c25be1dcfd3585e1b46Markus Scherer 112145e2c500373e5096c471c25be1dcfd3585e1b46Markus Scherer @Override 113145e2c500373e5096c471c25be1dcfd3585e1b46Markus Scherer public Number getField(Field field) { 114145e2c500373e5096c471c25be1dcfd3585e1b46Markus Scherer return fields.get(field); 115145e2c500373e5096c471c25be1dcfd3585e1b46Markus Scherer } 116145e2c500373e5096c471c25be1dcfd3585e1b46Markus Scherer 117145e2c500373e5096c471c25be1dcfd3585e1b46Markus Scherer @Override 118145e2c500373e5096c471c25be1dcfd3585e1b46Markus Scherer public int getSign() { 119145e2c500373e5096c471c25be1dcfd3585e1b46Markus Scherer return sign; 120145e2c500373e5096c471c25be1dcfd3585e1b46Markus Scherer } 121145e2c500373e5096c471c25be1dcfd3585e1b46Markus Scherer 122145e2c500373e5096c471c25be1dcfd3585e1b46Markus Scherer @Override 123145e2c500373e5096c471c25be1dcfd3585e1b46Markus Scherer public int hashCode() { 124145e2c500373e5096c471c25be1dcfd3585e1b46Markus Scherer throw new UnsupportedOperationException(); 125145e2c500373e5096c471c25be1dcfd3585e1b46Markus Scherer } 126145e2c500373e5096c471c25be1dcfd3585e1b46Markus Scherer 127145e2c500373e5096c471c25be1dcfd3585e1b46Markus Scherer @Override 128145e2c500373e5096c471c25be1dcfd3585e1b46Markus Scherer public boolean isSet(Field field) { 129145e2c500373e5096c471c25be1dcfd3585e1b46Markus Scherer return fields.containsKey(field); 130145e2c500373e5096c471c25be1dcfd3585e1b46Markus Scherer } 131145e2c500373e5096c471c25be1dcfd3585e1b46Markus Scherer 132145e2c500373e5096c471c25be1dcfd3585e1b46Markus Scherer @Override 133145e2c500373e5096c471c25be1dcfd3585e1b46Markus Scherer public Duration multiply(BigDecimal factor) { 134145e2c500373e5096c471c25be1dcfd3585e1b46Markus Scherer throw new UnsupportedOperationException(); 135145e2c500373e5096c471c25be1dcfd3585e1b46Markus Scherer } 136145e2c500373e5096c471c25be1dcfd3585e1b46Markus Scherer 137145e2c500373e5096c471c25be1dcfd3585e1b46Markus Scherer @Override 138145e2c500373e5096c471c25be1dcfd3585e1b46Markus Scherer public Duration negate() { 139145e2c500373e5096c471c25be1dcfd3585e1b46Markus Scherer return new ICUTestDuration(-sign, fields); 140145e2c500373e5096c471c25be1dcfd3585e1b46Markus Scherer } 141145e2c500373e5096c471c25be1dcfd3585e1b46Markus Scherer 142145e2c500373e5096c471c25be1dcfd3585e1b46Markus Scherer @Override 143145e2c500373e5096c471c25be1dcfd3585e1b46Markus Scherer public Duration normalizeWith(java.util.Calendar startTimeInstant) { 144145e2c500373e5096c471c25be1dcfd3585e1b46Markus Scherer throw new UnsupportedOperationException(); 145145e2c500373e5096c471c25be1dcfd3585e1b46Markus Scherer } 146145e2c500373e5096c471c25be1dcfd3585e1b46Markus Scherer 147145e2c500373e5096c471c25be1dcfd3585e1b46Markus Scherer @Override 148145e2c500373e5096c471c25be1dcfd3585e1b46Markus Scherer public String toString() { 149145e2c500373e5096c471c25be1dcfd3585e1b46Markus Scherer String signString = sign > 0 ? "positive" : sign == 0 ? "zero" : "negative"; 150145e2c500373e5096c471c25be1dcfd3585e1b46Markus Scherer return signString + " fields=" + fields; 151145e2c500373e5096c471c25be1dcfd3585e1b46Markus Scherer } 152145e2c500373e5096c471c25be1dcfd3585e1b46Markus Scherer } 153145e2c500373e5096c471c25be1dcfd3585e1b46Markus Scherer private static final ICUTestDuration newDuration(long millis) { 154145e2c500373e5096c471c25be1dcfd3585e1b46Markus Scherer return new ICUTestDuration(millis); 155145e2c500373e5096c471c25be1dcfd3585e1b46Markus Scherer } 156145e2c500373e5096c471c25be1dcfd3585e1b46Markus Scherer private static final ICUTestDuration newDuration(int sgn, int d, int h, int m, float s) { 157145e2c500373e5096c471c25be1dcfd3585e1b46Markus Scherer return new ICUTestDuration(sgn, -1, -1, d, h, m, s); 158145e2c500373e5096c471c25be1dcfd3585e1b46Markus Scherer } 159145e2c500373e5096c471c25be1dcfd3585e1b46Markus Scherer private static final ICUTestDuration newDuration(int sgn, int h, int m, float s) { 160145e2c500373e5096c471c25be1dcfd3585e1b46Markus Scherer return new ICUTestDuration(sgn, -1, -1, -1, h, m, s); 161145e2c500373e5096c471c25be1dcfd3585e1b46Markus Scherer } 162145e2c500373e5096c471c25be1dcfd3585e1b46Markus Scherer private static final ICUTestDuration newDuration(int sgn, float s) { 163145e2c500373e5096c471c25be1dcfd3585e1b46Markus Scherer return new ICUTestDuration(sgn, -1, -1, -1, -1, -1, s); 164145e2c500373e5096c471c25be1dcfd3585e1b46Markus Scherer } 1657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 1667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert /** 167fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert * 1687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert */ 1697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert public ICUDurationTest() { 1707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 1717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 1727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert /** 1737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Basic test 1747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert */ 1752d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert @Test 1767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert public void TestBasics() { 1777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert DurationFormat df; 1787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert String expect; 1797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert String formatted; 180fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert 1817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert df = DurationFormat.getInstance(new ULocale("it")); 1827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert formatted = df.formatDurationFromNow(4096); 1837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert expect = "fra quattro secondi"; 1847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if(!expect.equals(formatted)) { 1857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert errln("Expected " + expect + " but got " + formatted); 1867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } else { 1877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert logln("format duration -> " + formatted); 1887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 189fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert 1907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert formatted = df.formatDurationFromNowTo(new Date(0)); 1917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert Calendar cal = Calendar.getInstance(); 1927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert int years = cal.get(Calendar.YEAR) - 1970; // year of Date(0) 1937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert expect = years + " anni fa"; 1947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if(!expect.equals(formatted)) { 1957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert errln("Expected " + expect + " but got " + formatted); 1967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } else { 1977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert logln("format date -> " + formatted); 1987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 199fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert 2007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert formatted = df.formatDurationFrom(1000*3600*24, new Date(0).getTime()); 2017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert expect = "fra un giorno"; 2027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if(!expect.equals(formatted)) { 2037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert errln("Expected " + expect + " but got " + formatted); 2047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } else { 2057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert logln("format date from -> " + formatted); 2067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 2077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 2087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert formatted = df.format(new Long(1000*3600*24*2)); 2097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert expect = "fra due giorni"; 2107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if(!expect.equals(formatted)) { 2117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert errln("Expected " + expect + " but got " + formatted); 2127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } else { 2137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert logln("format long obj -> " + formatted); 2147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 2157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 2167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 2172d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert @Test 2187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert public void TestSimpleXMLDuration() { 2197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert Duration d; 2207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert DurationFormat df; 2217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert String out; 2227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert String expected; 2237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert String expected2; 224fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert 2257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // test 1 226145e2c500373e5096c471c25be1dcfd3585e1b46Markus Scherer d = newDuration(1, 2, 46, 40); // "PT2H46M40S" 2277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert df = DurationFormat.getInstance(new ULocale("en")); 2287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert expected = "2 hours, 46 minutes, and 40 seconds"; 2297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert out = df.format(d); 2307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if(out.equals(expected)) { 2317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert logln("out=expected: " + expected + " from " + d); 2327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } else { 2337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert errln("FAIL: got " + out + " wanted " + expected + " from " + d); 2347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 235fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert 2367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // test 2 237145e2c500373e5096c471c25be1dcfd3585e1b46Markus Scherer d = newDuration(10000); 2387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert df = DurationFormat.getInstance(new ULocale("en")); 2397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert expected = "10 seconds"; 2407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert out = df.format(d); 2417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if(out.equals(expected)) { 2427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert logln("out=expected: " + expected + " from " + d); 2437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } else { 2447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert errln("FAIL: got " + out + " wanted " + expected + " from " + d); 2457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 2467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // test 3 247145e2c500373e5096c471c25be1dcfd3585e1b46Markus Scherer d = newDuration(1, 0, 0, 0, 10); // "P0DT0H0M10.0S" 2487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert df = DurationFormat.getInstance(new ULocale("en")); 2497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert expected = "10 seconds"; 2507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert out = df.format(d); 2517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if(out.equals(expected)) { 2527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert logln("out=expected: " + expected + " from " + d); 2537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } else { 2547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert errln("FAIL: got " + out + " wanted " + expected + " from " + d); 2557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 2567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // test 4 257145e2c500373e5096c471c25be1dcfd3585e1b46Markus Scherer d = newDuration(86400000); 2587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert df = DurationFormat.getInstance(new ULocale("en")); 2597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert expected = "1 day, 0 hours, 0 minutes, and 0 seconds"; 2607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert expected2 = "1 day and 0 seconds"; // This is the expected result for Windows with IBM JRE6 2617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert out = df.format(d); 2627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if(out.equals(expected)) { 2637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert logln("out=expected: " + expected + " from " + d); 2647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } else { 2657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if(out.equals(expected2)){ 2667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert logln("WARNING: got " + out + " wanted " + expected + " from " + d); 2677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } else{ 2687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert errln("FAIL: got " + out + " wanted " + expected + " from " + d); 2697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 2707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 2717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 2727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 2732d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert @Test 2747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert public void TestXMLDuration() { 275145e2c500373e5096c471c25be1dcfd3585e1b46Markus Scherer final class TestCase { 276145e2c500373e5096c471c25be1dcfd3585e1b46Markus Scherer final String localeString; 277145e2c500373e5096c471c25be1dcfd3585e1b46Markus Scherer final ULocale locale; 278145e2c500373e5096c471c25be1dcfd3585e1b46Markus Scherer final String durationString; 279145e2c500373e5096c471c25be1dcfd3585e1b46Markus Scherer final Duration duration; 280145e2c500373e5096c471c25be1dcfd3585e1b46Markus Scherer final String expected; 281145e2c500373e5096c471c25be1dcfd3585e1b46Markus Scherer 282145e2c500373e5096c471c25be1dcfd3585e1b46Markus Scherer TestCase(String loc, String ds, Duration d, String exp) { 283145e2c500373e5096c471c25be1dcfd3585e1b46Markus Scherer localeString = loc; 284145e2c500373e5096c471c25be1dcfd3585e1b46Markus Scherer locale = new ULocale(loc); 285145e2c500373e5096c471c25be1dcfd3585e1b46Markus Scherer durationString = ds; 286145e2c500373e5096c471c25be1dcfd3585e1b46Markus Scherer duration = d; 287145e2c500373e5096c471c25be1dcfd3585e1b46Markus Scherer expected = exp; 288145e2c500373e5096c471c25be1dcfd3585e1b46Markus Scherer } 2897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 290145e2c500373e5096c471c25be1dcfd3585e1b46Markus Scherer 291145e2c500373e5096c471c25be1dcfd3585e1b46Markus Scherer TestCase cases[] = { 292145e2c500373e5096c471c25be1dcfd3585e1b46Markus Scherer new TestCase("en", "PT10.00099S", newDuration(1, 10.00099F), "10 seconds"), 293145e2c500373e5096c471c25be1dcfd3585e1b46Markus Scherer new TestCase("en", "#10000", newDuration(10000), "10 seconds"), 294145e2c500373e5096c471c25be1dcfd3585e1b46Markus Scherer new TestCase("en", "-PT10.00099S", newDuration(-1, 10.00099F), "10 seconds"), 295145e2c500373e5096c471c25be1dcfd3585e1b46Markus Scherer new TestCase("en", "#-10000", newDuration(-10000), "10 seconds"), 296145e2c500373e5096c471c25be1dcfd3585e1b46Markus Scherer 297145e2c500373e5096c471c25be1dcfd3585e1b46Markus Scherer // from BD req's 298145e2c500373e5096c471c25be1dcfd3585e1b46Markus Scherer new TestCase("en", "PT2H46M40S", newDuration(1, 2, 46, 40), "2 hours, 46 minutes, and 40 seconds"), 299145e2c500373e5096c471c25be1dcfd3585e1b46Markus Scherer new TestCase("it", "PT2H46M40S", newDuration(1, 2, 46, 40), "due ore, 46 minuti e 40 secondi"), 300145e2c500373e5096c471c25be1dcfd3585e1b46Markus Scherer 301145e2c500373e5096c471c25be1dcfd3585e1b46Markus Scherer // more cases 302145e2c500373e5096c471c25be1dcfd3585e1b46Markus Scherer new TestCase("en", "PT10S", newDuration(1, 10), "10 seconds"), 303145e2c500373e5096c471c25be1dcfd3585e1b46Markus Scherer new TestCase("en", "PT88M70S", newDuration(1, -1, 88, 70), "88 minutes and 70 seconds"), 304145e2c500373e5096c471c25be1dcfd3585e1b46Markus Scherer new TestCase("en", "PT10.100S", newDuration(1, 10.100F), "10 seconds and 100 milliseconds"), 305145e2c500373e5096c471c25be1dcfd3585e1b46Markus Scherer new TestCase("en", "-PT10S", newDuration(-1, 10), "10 seconds"), 306145e2c500373e5096c471c25be1dcfd3585e1b46Markus Scherer new TestCase("en", "PT0H5M0S", newDuration(1, 0, 5, 0), "5 minutes and 0 seconds") 3077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert }; 308145e2c500373e5096c471c25be1dcfd3585e1b46Markus Scherer 309145e2c500373e5096c471c25be1dcfd3585e1b46Markus Scherer for (TestCase tc : cases) { 310145e2c500373e5096c471c25be1dcfd3585e1b46Markus Scherer String loc = tc.localeString; 311145e2c500373e5096c471c25be1dcfd3585e1b46Markus Scherer String from = tc.durationString; 312145e2c500373e5096c471c25be1dcfd3585e1b46Markus Scherer String to = tc.expected; 313145e2c500373e5096c471c25be1dcfd3585e1b46Markus Scherer 314145e2c500373e5096c471c25be1dcfd3585e1b46Markus Scherer ULocale locale = tc.locale; 315145e2c500373e5096c471c25be1dcfd3585e1b46Markus Scherer Duration d = tc.duration; 316145e2c500373e5096c471c25be1dcfd3585e1b46Markus Scherer 3177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert DurationFormat df = DurationFormat.getInstance(locale); 3187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert String output = df.format(d); 319fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert 3207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if(output.equals(to)) { 3217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert logln("SUCCESS: locale: " + loc + ", from " + from + " ["+d.toString()+"] " +" to " + to + "= " + output); 3227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } else { 3237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert logln("FAIL: locale: " + loc + ", from " + from + " ["+d.toString()+"] " +": expected " + to + " got " + output); 3247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 3257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 3267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 3277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 3287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 3292d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert @Test 3307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert public void TestBadObjectError() { 3317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert Runtime r = Runtime.getRuntime(); 3327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert DurationFormat df = DurationFormat.getInstance(new ULocale("en")); 3337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert String output = null; 3347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert try { 3357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert output = df.format(r); 3367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert errln("FAIL: did NOT get IllegalArgumentException! Should have. Formatted Runtime as " + output + " ???"); 3377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } catch (IllegalArgumentException iae) { 3387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert logln("PASS: expected: Caught iae: " + iae.toString() ); 3397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 3407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // try a second time, because it is a different code path for java < 1.5 3417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert try { 3427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert output = df.format(r); 3437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert errln("FAIL: [#2] did NOT get IllegalArgumentException! Should have. Formatted Runtime as " + output + " ???"); 3447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } catch (IllegalArgumentException iae) { 3457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert logln("PASS: [#2] expected: Caught iae: " + iae.toString() ); 3467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 3477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 3487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 3492d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert @Test 3507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert public void TestBadLocaleError() { 3517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert try { 3527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert DurationFormat df = DurationFormat.getInstance(new ULocale("und")); 3537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert df.format(new Date()); 3547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert logln("Should have thrown err."); 3557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert errln("failed, should have thrown err."); 3567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } catch(MissingResourceException mre) { 3577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert logln("PASS: caught missing resource exception on locale 'und'"); 3587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert logln(mre.toString()); 3597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 3607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 3617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 3622d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert @Test 3637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert public void TestResourceWithCalendar() { 3647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert DurationFormat df = DurationFormat.getInstance(new ULocale("th@calendar=buddhist")); 3657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // should pass, but return a default formatter for th. 3667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (df == null) { 3677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert errln("FAIL: null DurationFormat returned."); 3687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 3697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 370fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert 3717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert /* Tests the class 3727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * DurationFormat 3737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert */ 3742d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert @Test 3757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert public void TestDurationFormat(){ 3767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert @SuppressWarnings("serial") 3777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert class TestDurationFormat extends DurationFormat { 378fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert @Override 3797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert public StringBuffer format(Object object, StringBuffer toAppend, FieldPosition pos) {return null;} 380fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert @Override 3817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert public String formatDurationFrom(long duration, long referenceDate) {return null;} 382fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert @Override 3837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert public String formatDurationFromNow(long duration) {return null;} 384fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert @Override 3857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert public String formatDurationFromNowTo(Date targetDate) {return null;} 3867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert public TestDurationFormat() {super();} 387fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert 3887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 389fe77e7203e518f62b5bd8e8c603bca361e9cf47bFredrik Roubert 3907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // Tests the constructor and the following method 3917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // public Object parseObject(String source, ParsePosition pos) 3927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert try{ 3937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert TestDurationFormat tdf = new TestDurationFormat(); 3947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert tdf.parseObject("",null); 3957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert errln("DurationFormat.parseObjet(String,ParsePosition) was " + 3967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert "to return an exception for an unsupported operation."); 3977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } catch(Exception e){} 3987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 3997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 4002d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert @Test 4017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert public void TestFromNowTo() { 4027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert class TestCase { 4037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert ULocale locale; 4047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert int diffInSeconds; 4057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert String expected; 4067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert TestCase(ULocale locale, int diffInSeconds, String expected) { 4077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert this.locale = locale; 4087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert this.diffInSeconds = diffInSeconds; 4097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert this.expected = expected; 4107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 4117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 4127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert TestCase[] testCases = { 4137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert new TestCase(ULocale.US, 10, "10 seconds from now"), 4147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert new TestCase(ULocale.US, -10, "10 seconds ago"), 4157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert new TestCase(ULocale.US, -1800, "30 minutes ago"), 4167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert new TestCase(ULocale.US, 3600, "1 hour from now"), 4177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert new TestCase(ULocale.US, 10000, "2 hours from now"), 4187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert new TestCase(ULocale.US, -20000, "5 hours ago"), 4197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert new TestCase(ULocale.FRANCE, -1800, "il y a 30 minutes"), 4207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert new TestCase(ULocale.ITALY, 10000, "fra due ore"), 4217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert }; 4227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 4237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert final long delayMS = 10; // Safe margin - 10 milliseconds 4247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // See the comments below 4257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert for (TestCase test : testCases) { 4267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert DurationFormat df = DurationFormat.getInstance(test.locale); 4277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert long target = System.currentTimeMillis() + test.diffInSeconds * 1000; 4287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // Need some adjustment because time difference is recalculated in 4297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // formatDurationFromNowTo method. 4307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert target = test.diffInSeconds > 0 ? target + delayMS : target - delayMS; 4317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert Date d = new Date(target); 4327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert String result = df.formatDurationFromNowTo(d); 4337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert assertEquals("TestFromNowTo (" + test.locale + ", " + test.diffInSeconds + "sec)", 4347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert test.expected, result); 4357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 4367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 4377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert} 438