| | |
| | | * |
| | | * |
| | | * Copyright 2009-2010 Sun Microsystems, Inc. |
| | | * Portions copyright 2012 ForgeRock AS. |
| | | */ |
| | | |
| | | package com.forgerock.opendj.util; |
| | | |
| | | import java.util.Calendar; |
| | | |
| | | import org.forgerock.i18n.LocalizedIllegalArgumentException; |
| | | import org.forgerock.opendj.ldap.AttributeDescription; |
| | | import org.forgerock.opendj.ldap.ByteString; |
| | | import org.forgerock.opendj.ldap.DN; |
| | | import org.forgerock.opendj.ldap.DecodeException; |
| | | import org.forgerock.opendj.ldap.schema.Schema; |
| | | |
| | | /** |
| | |
| | | } |
| | | }; |
| | | |
| | | private static final Function<ByteString, String, Void> BYTESTRING_TO_BASE64 = |
| | | new Function<ByteString, String, Void>() { |
| | | |
| | | public String apply(final ByteString value, final Void p) { |
| | | return Base64.encode(value); |
| | | } |
| | | }; |
| | | |
| | | private static final Function<ByteString, Boolean, Void> BYTESTRING_TO_BOOLEAN = |
| | | new Function<ByteString, Boolean, Void>() { |
| | | |
| | |
| | | } |
| | | }; |
| | | |
| | | private static final Function<ByteString, Calendar, Void> BYTESTRING_TO_CALENDAR = |
| | | new Function<ByteString, Calendar, Void>() { |
| | | |
| | | public Calendar apply(final ByteString value, final Void p) { |
| | | try { |
| | | return GeneralizedTime.decode(value); |
| | | } catch (DecodeException e) { |
| | | throw new LocalizedIllegalArgumentException(e.getMessageObject(), e); |
| | | } |
| | | } |
| | | }; |
| | | |
| | | private static final Function<ByteString, DN, Schema> BYTESTRING_TO_DN = |
| | | new Function<ByteString, DN, Schema>() { |
| | | |
| | |
| | | } |
| | | |
| | | /** |
| | | * Returns a function which encodes a {@code ByteString} as {@code Base64}. |
| | | * |
| | | * @return A function which encodes a {@code ByteString} as {@code Base64}. |
| | | */ |
| | | public static Function<ByteString, String, Void> valueToBase64() { |
| | | return BYTESTRING_TO_BASE64; |
| | | } |
| | | |
| | | /** |
| | | * Returns a function which parses the string representation of a |
| | | * {@code ByteString} to a {@code Boolean}. The function will accept the |
| | | * values {@code 0}, {@code false}, {@code no}, {@code off}, {@code 1}, |
| | |
| | | |
| | | /** |
| | | * Returns a function which parses the string representation of a |
| | | * {@code ByteString} as a generalized time syntax. Invalid values will |
| | | * result in a {@code LocalizedIllegalArgumentException}. |
| | | * |
| | | * @return A function which parses the string representation of a |
| | | * {@code ByteString} as generalized time syntax. |
| | | */ |
| | | public static Function<ByteString, Calendar, Void> valueToCalendar() { |
| | | return BYTESTRING_TO_CALENDAR; |
| | | } |
| | | |
| | | /** |
| | | * Returns a function which parses the string representation of a |
| | | * {@code ByteString} as a {@code DN} using the default schema. Invalid |
| | | * values will result in a {@code LocalizedIllegalArgumentException}. |
| | | * |
| New file |
| | |
| | | /* |
| | | * CDDL HEADER START |
| | | * |
| | | * The contents of this file are subject to the terms of the |
| | | * Common Development and Distribution License, Version 1.0 only |
| | | * (the "License"). You may not use this file except in compliance |
| | | * with the License. |
| | | * |
| | | * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt |
| | | * or http://forgerock.org/license/CDDLv1.0.html. |
| | | * See the License for the specific language governing permissions |
| | | * and limitations under the License. |
| | | * |
| | | * When distributing Covered Code, include this CDDL HEADER in each |
| | | * file and include the License file at legal-notices/CDDLv1_0.txt. |
| | | * If applicable, add the following below this CDDL HEADER, with the |
| | | * fields enclosed by brackets "[]" replaced with your own identifying |
| | | * information: |
| | | * Portions Copyright [yyyy] [name of copyright owner] |
| | | * |
| | | * CDDL HEADER END |
| | | * |
| | | * |
| | | * Copyright 2012 ForgeRock AS. |
| | | */ |
| | | package com.forgerock.opendj.util; |
| | | |
| | | import static org.forgerock.opendj.ldap.CoreMessages.*; |
| | | |
| | | import java.util.Calendar; |
| | | import java.util.Date; |
| | | import java.util.GregorianCalendar; |
| | | import java.util.TimeZone; |
| | | |
| | | import org.forgerock.i18n.LocalizableMessage; |
| | | import org.forgerock.opendj.ldap.ByteSequence; |
| | | import org.forgerock.opendj.ldap.ByteString; |
| | | import org.forgerock.opendj.ldap.DecodeException; |
| | | |
| | | /** |
| | | * Utility class for encoding and decoding generalized time syntax values. |
| | | */ |
| | | public final class GeneralizedTime { |
| | | |
| | | // UTC TimeZone is assumed to never change over JVM lifetime |
| | | private static final TimeZone TIME_ZONE_UTC_OBJ = TimeZone.getTimeZone("UTC"); |
| | | |
| | | /** |
| | | * Returns the provided generalized time syntax value decoded as a |
| | | * {@code Calendar}. |
| | | * |
| | | * @param value |
| | | * The generalized time value to be decoded. |
| | | * @return The decoded {@code Calendar}. |
| | | * @throws DecodeException |
| | | * If the provided value cannot be parsed as a valid generalized |
| | | * time string. |
| | | */ |
| | | public static Calendar decode(final ByteSequence value) throws DecodeException { |
| | | int year = 0; |
| | | int month = 0; |
| | | int day = 0; |
| | | int hour = 0; |
| | | int minute = 0; |
| | | int second = 0; |
| | | |
| | | // Get the value as a string and verify that it is at least long |
| | | // enough for "YYYYMMDDhhZ", which is the shortest allowed value. |
| | | final String valueString = value.toString().toUpperCase(); |
| | | final int length = valueString.length(); |
| | | if (length < 11) { |
| | | final LocalizableMessage message = |
| | | WARN_ATTR_SYNTAX_GENERALIZED_TIME_TOO_SHORT.get(valueString); |
| | | final DecodeException e = DecodeException.error(message); |
| | | StaticUtils.DEBUG_LOG.throwing("GeneralizedTimeSyntax", "valueIsAcceptable", e); |
| | | throw e; |
| | | } |
| | | |
| | | // The first four characters are the century and year, and they must |
| | | // be numeric digits between 0 and 9. |
| | | for (int i = 0; i < 4; i++) { |
| | | switch (valueString.charAt(i)) { |
| | | case '0': |
| | | year = year * 10; |
| | | break; |
| | | |
| | | case '1': |
| | | year = (year * 10) + 1; |
| | | break; |
| | | |
| | | case '2': |
| | | year = (year * 10) + 2; |
| | | break; |
| | | |
| | | case '3': |
| | | year = (year * 10) + 3; |
| | | break; |
| | | |
| | | case '4': |
| | | year = (year * 10) + 4; |
| | | break; |
| | | |
| | | case '5': |
| | | year = (year * 10) + 5; |
| | | break; |
| | | |
| | | case '6': |
| | | year = (year * 10) + 6; |
| | | break; |
| | | |
| | | case '7': |
| | | year = (year * 10) + 7; |
| | | break; |
| | | |
| | | case '8': |
| | | year = (year * 10) + 8; |
| | | break; |
| | | |
| | | case '9': |
| | | year = (year * 10) + 9; |
| | | break; |
| | | |
| | | default: |
| | | final LocalizableMessage message = |
| | | WARN_ATTR_SYNTAX_GENERALIZED_TIME_INVALID_YEAR.get(valueString, String |
| | | .valueOf(valueString.charAt(i))); |
| | | final DecodeException e = DecodeException.error(message); |
| | | StaticUtils.DEBUG_LOG.throwing("GeneralizedTimeSyntax", "valueIsAcceptable", e); |
| | | throw e; |
| | | } |
| | | } |
| | | |
| | | // The next two characters are the month, and they must form the |
| | | // string representation of an integer between 01 and 12. |
| | | char m1 = valueString.charAt(4); |
| | | final char m2 = valueString.charAt(5); |
| | | switch (m1) { |
| | | case '0': |
| | | // m2 must be a digit between 1 and 9. |
| | | switch (m2) { |
| | | case '1': |
| | | month = Calendar.JANUARY; |
| | | break; |
| | | |
| | | case '2': |
| | | month = Calendar.FEBRUARY; |
| | | break; |
| | | |
| | | case '3': |
| | | month = Calendar.MARCH; |
| | | break; |
| | | |
| | | case '4': |
| | | month = Calendar.APRIL; |
| | | break; |
| | | |
| | | case '5': |
| | | month = Calendar.MAY; |
| | | break; |
| | | |
| | | case '6': |
| | | month = Calendar.JUNE; |
| | | break; |
| | | |
| | | case '7': |
| | | month = Calendar.JULY; |
| | | break; |
| | | |
| | | case '8': |
| | | month = Calendar.AUGUST; |
| | | break; |
| | | |
| | | case '9': |
| | | month = Calendar.SEPTEMBER; |
| | | break; |
| | | |
| | | default: |
| | | final LocalizableMessage message = |
| | | WARN_ATTR_SYNTAX_GENERALIZED_TIME_INVALID_MONTH.get(valueString, |
| | | valueString.substring(4, 6)); |
| | | final DecodeException e = DecodeException.error(message); |
| | | StaticUtils.DEBUG_LOG.throwing("GeneralizedTimeSyntax", "valueIsAcceptable", e); |
| | | throw e; |
| | | } |
| | | break; |
| | | case '1': |
| | | // m2 must be a digit between 0 and 2. |
| | | switch (m2) { |
| | | case '0': |
| | | month = Calendar.OCTOBER; |
| | | break; |
| | | |
| | | case '1': |
| | | month = Calendar.NOVEMBER; |
| | | break; |
| | | |
| | | case '2': |
| | | month = Calendar.DECEMBER; |
| | | break; |
| | | |
| | | default: |
| | | final LocalizableMessage message = |
| | | WARN_ATTR_SYNTAX_GENERALIZED_TIME_INVALID_MONTH.get(valueString, |
| | | valueString.substring(4, 6)); |
| | | final DecodeException e = DecodeException.error(message); |
| | | StaticUtils.DEBUG_LOG.throwing("GeneralizedTimeSyntax", "valueIsAcceptable", e); |
| | | throw e; |
| | | } |
| | | break; |
| | | default: |
| | | final LocalizableMessage message = |
| | | WARN_ATTR_SYNTAX_GENERALIZED_TIME_INVALID_MONTH.get(valueString, valueString |
| | | .substring(4, 6)); |
| | | final DecodeException e = DecodeException.error(message); |
| | | StaticUtils.DEBUG_LOG.throwing("GeneralizedTimeSyntax", "valueIsAcceptable", e); |
| | | throw e; |
| | | } |
| | | |
| | | // The next two characters should be the day of the month, and they |
| | | // must form the string representation of an integer between 01 and |
| | | // 31. This doesn't do any validation against the year or month, so |
| | | // it will allow dates like April 31, or February 29 in a non-leap |
| | | // year, but we'll let those slide. |
| | | final char d1 = valueString.charAt(6); |
| | | final char d2 = valueString.charAt(7); |
| | | switch (d1) { |
| | | case '0': |
| | | // d2 must be a digit between 1 and 9. |
| | | switch (d2) { |
| | | case '1': |
| | | day = 1; |
| | | break; |
| | | |
| | | case '2': |
| | | day = 2; |
| | | break; |
| | | |
| | | case '3': |
| | | day = 3; |
| | | break; |
| | | |
| | | case '4': |
| | | day = 4; |
| | | break; |
| | | |
| | | case '5': |
| | | day = 5; |
| | | break; |
| | | |
| | | case '6': |
| | | day = 6; |
| | | break; |
| | | |
| | | case '7': |
| | | day = 7; |
| | | break; |
| | | |
| | | case '8': |
| | | day = 8; |
| | | break; |
| | | |
| | | case '9': |
| | | day = 9; |
| | | break; |
| | | |
| | | default: |
| | | final LocalizableMessage message = |
| | | WARN_ATTR_SYNTAX_GENERALIZED_TIME_INVALID_DAY.get(valueString, valueString |
| | | .substring(6, 8)); |
| | | final DecodeException e = DecodeException.error(message); |
| | | StaticUtils.DEBUG_LOG.throwing("GeneralizedTimeSyntax", "valueIsAcceptable", e); |
| | | throw e; |
| | | } |
| | | break; |
| | | |
| | | case '1': |
| | | // d2 must be a digit between 0 and 9. |
| | | switch (d2) { |
| | | case '0': |
| | | day = 10; |
| | | break; |
| | | |
| | | case '1': |
| | | day = 11; |
| | | break; |
| | | |
| | | case '2': |
| | | day = 12; |
| | | break; |
| | | |
| | | case '3': |
| | | day = 13; |
| | | break; |
| | | |
| | | case '4': |
| | | day = 14; |
| | | break; |
| | | |
| | | case '5': |
| | | day = 15; |
| | | break; |
| | | |
| | | case '6': |
| | | day = 16; |
| | | break; |
| | | |
| | | case '7': |
| | | day = 17; |
| | | break; |
| | | |
| | | case '8': |
| | | day = 18; |
| | | break; |
| | | |
| | | case '9': |
| | | day = 19; |
| | | break; |
| | | |
| | | default: |
| | | final LocalizableMessage message = |
| | | WARN_ATTR_SYNTAX_GENERALIZED_TIME_INVALID_DAY.get(valueString, valueString |
| | | .substring(6, 8)); |
| | | final DecodeException e = DecodeException.error(message); |
| | | StaticUtils.DEBUG_LOG.throwing("GeneralizedTimeSyntax", "valueIsAcceptable", e); |
| | | throw e; |
| | | } |
| | | break; |
| | | |
| | | case '2': |
| | | // d2 must be a digit between 0 and 9. |
| | | switch (d2) { |
| | | case '0': |
| | | day = 20; |
| | | break; |
| | | |
| | | case '1': |
| | | day = 21; |
| | | break; |
| | | |
| | | case '2': |
| | | day = 22; |
| | | break; |
| | | |
| | | case '3': |
| | | day = 23; |
| | | break; |
| | | |
| | | case '4': |
| | | day = 24; |
| | | break; |
| | | |
| | | case '5': |
| | | day = 25; |
| | | break; |
| | | |
| | | case '6': |
| | | day = 26; |
| | | break; |
| | | |
| | | case '7': |
| | | day = 27; |
| | | break; |
| | | |
| | | case '8': |
| | | day = 28; |
| | | break; |
| | | |
| | | case '9': |
| | | day = 29; |
| | | break; |
| | | |
| | | default: |
| | | final LocalizableMessage message = |
| | | WARN_ATTR_SYNTAX_GENERALIZED_TIME_INVALID_DAY.get(valueString, valueString |
| | | .substring(6, 8)); |
| | | final DecodeException e = DecodeException.error(message); |
| | | StaticUtils.DEBUG_LOG.throwing("GeneralizedTimeSyntax", "valueIsAcceptable", e); |
| | | throw e; |
| | | } |
| | | break; |
| | | |
| | | case '3': |
| | | // d2 must be either 0 or 1. |
| | | switch (d2) { |
| | | case '0': |
| | | day = 30; |
| | | break; |
| | | |
| | | case '1': |
| | | day = 31; |
| | | break; |
| | | |
| | | default: |
| | | final LocalizableMessage message = |
| | | WARN_ATTR_SYNTAX_GENERALIZED_TIME_INVALID_DAY.get(valueString, valueString |
| | | .substring(6, 8)); |
| | | final DecodeException e = DecodeException.error(message); |
| | | StaticUtils.DEBUG_LOG.throwing("GeneralizedTimeSyntax", "valueIsAcceptable", e); |
| | | throw e; |
| | | } |
| | | break; |
| | | |
| | | default: |
| | | final LocalizableMessage message = |
| | | WARN_ATTR_SYNTAX_GENERALIZED_TIME_INVALID_DAY.get(valueString, valueString |
| | | .substring(6, 8)); |
| | | final DecodeException e = DecodeException.error(message); |
| | | StaticUtils.DEBUG_LOG.throwing("GeneralizedTimeSyntax", "valueIsAcceptable", e); |
| | | throw e; |
| | | } |
| | | |
| | | // The next two characters must be the hour, and they must form the |
| | | // string representation of an integer between 00 and 23. |
| | | final char h1 = valueString.charAt(8); |
| | | final char h2 = valueString.charAt(9); |
| | | switch (h1) { |
| | | case '0': |
| | | switch (h2) { |
| | | case '0': |
| | | hour = 0; |
| | | break; |
| | | |
| | | case '1': |
| | | hour = 1; |
| | | break; |
| | | |
| | | case '2': |
| | | hour = 2; |
| | | break; |
| | | |
| | | case '3': |
| | | hour = 3; |
| | | break; |
| | | |
| | | case '4': |
| | | hour = 4; |
| | | break; |
| | | |
| | | case '5': |
| | | hour = 5; |
| | | break; |
| | | |
| | | case '6': |
| | | hour = 6; |
| | | break; |
| | | |
| | | case '7': |
| | | hour = 7; |
| | | break; |
| | | |
| | | case '8': |
| | | hour = 8; |
| | | break; |
| | | |
| | | case '9': |
| | | hour = 9; |
| | | break; |
| | | |
| | | default: |
| | | final LocalizableMessage message = |
| | | WARN_ATTR_SYNTAX_GENERALIZED_TIME_INVALID_HOUR.get(valueString, valueString |
| | | .substring(8, 10)); |
| | | final DecodeException e = DecodeException.error(message); |
| | | StaticUtils.DEBUG_LOG.throwing("GeneralizedTimeSyntax", "valueIsAcceptable", e); |
| | | throw e; |
| | | } |
| | | break; |
| | | |
| | | case '1': |
| | | switch (h2) { |
| | | case '0': |
| | | hour = 10; |
| | | break; |
| | | |
| | | case '1': |
| | | hour = 11; |
| | | break; |
| | | |
| | | case '2': |
| | | hour = 12; |
| | | break; |
| | | |
| | | case '3': |
| | | hour = 13; |
| | | break; |
| | | |
| | | case '4': |
| | | hour = 14; |
| | | break; |
| | | |
| | | case '5': |
| | | hour = 15; |
| | | break; |
| | | |
| | | case '6': |
| | | hour = 16; |
| | | break; |
| | | |
| | | case '7': |
| | | hour = 17; |
| | | break; |
| | | |
| | | case '8': |
| | | hour = 18; |
| | | break; |
| | | |
| | | case '9': |
| | | hour = 19; |
| | | break; |
| | | |
| | | default: |
| | | final LocalizableMessage message = |
| | | WARN_ATTR_SYNTAX_GENERALIZED_TIME_INVALID_HOUR.get(valueString, valueString |
| | | .substring(8, 10)); |
| | | final DecodeException e = DecodeException.error(message); |
| | | StaticUtils.DEBUG_LOG.throwing("GeneralizedTimeSyntax", "valueIsAcceptable", e); |
| | | throw e; |
| | | } |
| | | break; |
| | | |
| | | case '2': |
| | | switch (h2) { |
| | | case '0': |
| | | hour = 20; |
| | | break; |
| | | |
| | | case '1': |
| | | hour = 21; |
| | | break; |
| | | |
| | | case '2': |
| | | hour = 22; |
| | | break; |
| | | |
| | | case '3': |
| | | hour = 23; |
| | | break; |
| | | |
| | | default: |
| | | final LocalizableMessage message = |
| | | WARN_ATTR_SYNTAX_GENERALIZED_TIME_INVALID_HOUR.get(valueString, valueString |
| | | .substring(8, 10)); |
| | | final DecodeException e = DecodeException.error(message); |
| | | StaticUtils.DEBUG_LOG.throwing("GeneralizedTimeSyntax", "valueIsAcceptable", e); |
| | | throw e; |
| | | } |
| | | break; |
| | | |
| | | default: |
| | | final LocalizableMessage message = |
| | | WARN_ATTR_SYNTAX_GENERALIZED_TIME_INVALID_HOUR.get(valueString, valueString |
| | | .substring(8, 10)); |
| | | final DecodeException e = DecodeException.error(message); |
| | | StaticUtils.DEBUG_LOG.throwing("GeneralizedTimeSyntax", "valueIsAcceptable", e); |
| | | throw e; |
| | | } |
| | | |
| | | // Next, there should be either two digits comprising an integer |
| | | // between 00 and 59 (for the minute), a letter 'Z' (for the UTC |
| | | // specifier), a plus or minus sign followed by two or four digits |
| | | // (for the UTC offset), or a period or comma representing the |
| | | // fraction. |
| | | m1 = valueString.charAt(10); |
| | | switch (m1) { |
| | | case '0': |
| | | case '1': |
| | | case '2': |
| | | case '3': |
| | | case '4': |
| | | case '5': |
| | | // There must be at least two more characters, and the next one |
| | | // must be a digit between 0 and 9. |
| | | if (length < 13) { |
| | | final LocalizableMessage message = |
| | | WARN_ATTR_SYNTAX_GENERALIZED_TIME_INVALID_CHAR.get(valueString, String |
| | | .valueOf(m1), 10); |
| | | final DecodeException e = DecodeException.error(message); |
| | | StaticUtils.DEBUG_LOG.throwing("GeneralizedTimeSyntax", "valueIsAcceptable", e); |
| | | throw e; |
| | | } |
| | | |
| | | minute = 10 * (m1 - '0'); |
| | | |
| | | switch (valueString.charAt(11)) { |
| | | case '0': |
| | | break; |
| | | |
| | | case '1': |
| | | minute += 1; |
| | | break; |
| | | |
| | | case '2': |
| | | minute += 2; |
| | | break; |
| | | |
| | | case '3': |
| | | minute += 3; |
| | | break; |
| | | |
| | | case '4': |
| | | minute += 4; |
| | | break; |
| | | |
| | | case '5': |
| | | minute += 5; |
| | | break; |
| | | |
| | | case '6': |
| | | minute += 6; |
| | | break; |
| | | |
| | | case '7': |
| | | minute += 7; |
| | | break; |
| | | |
| | | case '8': |
| | | minute += 8; |
| | | break; |
| | | |
| | | case '9': |
| | | minute += 9; |
| | | break; |
| | | |
| | | default: |
| | | final LocalizableMessage message = |
| | | WARN_ATTR_SYNTAX_GENERALIZED_TIME_INVALID_MINUTE.get(valueString, |
| | | valueString.substring(10, 12)); |
| | | final DecodeException e = DecodeException.error(message); |
| | | StaticUtils.DEBUG_LOG.throwing("GeneralizedTimeSyntax", "valueIsAcceptable", e); |
| | | throw e; |
| | | } |
| | | |
| | | break; |
| | | |
| | | case 'Z': |
| | | // This is fine only if we are at the end of the value. |
| | | if (length == 11) { |
| | | final TimeZone tz = TIME_ZONE_UTC_OBJ; |
| | | return createTime(valueString, year, month, day, hour, minute, second, tz); |
| | | } else { |
| | | final LocalizableMessage message = |
| | | WARN_ATTR_SYNTAX_GENERALIZED_TIME_INVALID_CHAR.get(valueString, String |
| | | .valueOf(m1), 10); |
| | | final DecodeException e = DecodeException.error(message); |
| | | StaticUtils.DEBUG_LOG.throwing("GeneralizedTimeSyntax", "valueIsAcceptable", e); |
| | | throw e; |
| | | } |
| | | |
| | | case '+': |
| | | case '-': |
| | | // These are fine only if there are exactly two or four more |
| | | // digits that specify a valid offset. |
| | | if ((length == 13) || (length == 15)) { |
| | | final TimeZone tz = getTimeZoneForOffset(valueString, 10); |
| | | return createTime(valueString, year, month, day, hour, minute, second, tz); |
| | | } else { |
| | | final LocalizableMessage message = |
| | | WARN_ATTR_SYNTAX_GENERALIZED_TIME_INVALID_CHAR.get(valueString, String |
| | | .valueOf(m1), 10); |
| | | final DecodeException e = DecodeException.error(message); |
| | | StaticUtils.DEBUG_LOG.throwing("GeneralizedTimeSyntax", "valueIsAcceptable", e); |
| | | throw e; |
| | | } |
| | | |
| | | case '.': |
| | | case ',': |
| | | return finishDecodingFraction(valueString, 11, year, month, day, hour, minute, second, |
| | | 3600000); |
| | | |
| | | default: |
| | | final LocalizableMessage message = |
| | | WARN_ATTR_SYNTAX_GENERALIZED_TIME_INVALID_CHAR.get(valueString, String |
| | | .valueOf(m1), 10); |
| | | final DecodeException e = DecodeException.error(message); |
| | | StaticUtils.DEBUG_LOG.throwing("GeneralizedTimeSyntax", "valueIsAcceptable", e); |
| | | throw e; |
| | | } |
| | | |
| | | // Next, there should be either two digits comprising an integer |
| | | // between 00 and 60 (for the second, including a possible leap |
| | | // second), a letter 'Z' (for the UTC specifier), a plus or minus |
| | | // sign followed by two or four digits (for the UTC offset), or a |
| | | // period or comma to start the fraction. |
| | | final char s1 = valueString.charAt(12); |
| | | switch (s1) { |
| | | case '0': |
| | | case '1': |
| | | case '2': |
| | | case '3': |
| | | case '4': |
| | | case '5': |
| | | // There must be at least two more characters, and the next one |
| | | // must be a digit between 0 and 9. |
| | | if (length < 15) { |
| | | final LocalizableMessage message = |
| | | WARN_ATTR_SYNTAX_GENERALIZED_TIME_INVALID_CHAR.get(valueString, String |
| | | .valueOf(s1), 12); |
| | | final DecodeException e = DecodeException.error(message); |
| | | StaticUtils.DEBUG_LOG.throwing("GeneralizedTimeSyntax", "valueIsAcceptable", e); |
| | | throw e; |
| | | } |
| | | |
| | | second = 10 * (s1 - '0'); |
| | | |
| | | switch (valueString.charAt(13)) { |
| | | case '0': |
| | | break; |
| | | |
| | | case '1': |
| | | second += 1; |
| | | break; |
| | | |
| | | case '2': |
| | | second += 2; |
| | | break; |
| | | |
| | | case '3': |
| | | second += 3; |
| | | break; |
| | | |
| | | case '4': |
| | | second += 4; |
| | | break; |
| | | |
| | | case '5': |
| | | second += 5; |
| | | break; |
| | | |
| | | case '6': |
| | | second += 6; |
| | | break; |
| | | |
| | | case '7': |
| | | second += 7; |
| | | break; |
| | | |
| | | case '8': |
| | | second += 8; |
| | | break; |
| | | |
| | | case '9': |
| | | second += 9; |
| | | break; |
| | | |
| | | default: |
| | | final LocalizableMessage message = |
| | | WARN_ATTR_SYNTAX_GENERALIZED_TIME_INVALID_MINUTE.get(valueString, |
| | | valueString.substring(12, 14)); |
| | | final DecodeException e = DecodeException.error(message); |
| | | StaticUtils.DEBUG_LOG.throwing("GeneralizedTimeSyntax", "valueIsAcceptable", e); |
| | | throw e; |
| | | } |
| | | |
| | | break; |
| | | |
| | | case '6': |
| | | // There must be at least two more characters and the next one |
| | | // must be a 0. |
| | | if (length < 15) { |
| | | final LocalizableMessage message = |
| | | WARN_ATTR_SYNTAX_GENERALIZED_TIME_INVALID_CHAR.get(valueString, String |
| | | .valueOf(s1), 12); |
| | | final DecodeException e = DecodeException.error(message); |
| | | StaticUtils.DEBUG_LOG.throwing("GeneralizedTimeSyntax", "valueIsAcceptable", e); |
| | | throw e; |
| | | } |
| | | |
| | | if (valueString.charAt(13) != '0') { |
| | | final LocalizableMessage message = |
| | | WARN_ATTR_SYNTAX_GENERALIZED_TIME_INVALID_SECOND.get(valueString, |
| | | valueString.substring(12, 14)); |
| | | final DecodeException e = DecodeException.error(message); |
| | | StaticUtils.DEBUG_LOG.throwing("GeneralizedTimeSyntax", "valueIsAcceptable", e); |
| | | throw e; |
| | | } |
| | | |
| | | second = 60; |
| | | break; |
| | | |
| | | case 'Z': |
| | | // This is fine only if we are at the end of the value. |
| | | if (length == 13) { |
| | | final TimeZone tz = TIME_ZONE_UTC_OBJ; |
| | | return createTime(valueString, year, month, day, hour, minute, second, tz); |
| | | } else { |
| | | final LocalizableMessage message = |
| | | WARN_ATTR_SYNTAX_GENERALIZED_TIME_INVALID_CHAR.get(valueString, String |
| | | .valueOf(s1), 12); |
| | | final DecodeException e = DecodeException.error(message); |
| | | StaticUtils.DEBUG_LOG.throwing("GeneralizedTimeSyntax", "valueIsAcceptable", e); |
| | | throw e; |
| | | } |
| | | |
| | | case '+': |
| | | case '-': |
| | | // These are fine only if there are exactly two or four more |
| | | // digits that specify a valid offset. |
| | | if ((length == 15) || (length == 17)) { |
| | | final TimeZone tz = getTimeZoneForOffset(valueString, 12); |
| | | return createTime(valueString, year, month, day, hour, minute, second, tz); |
| | | } else { |
| | | final LocalizableMessage message = |
| | | WARN_ATTR_SYNTAX_GENERALIZED_TIME_INVALID_CHAR.get(valueString, String |
| | | .valueOf(s1), 12); |
| | | final DecodeException e = DecodeException.error(message); |
| | | StaticUtils.DEBUG_LOG.throwing("GeneralizedTimeSyntax", "valueIsAcceptable", e); |
| | | throw e; |
| | | } |
| | | |
| | | case '.': |
| | | case ',': |
| | | return finishDecodingFraction(valueString, 13, year, month, day, hour, minute, second, |
| | | 60000); |
| | | |
| | | default: |
| | | final LocalizableMessage message = |
| | | WARN_ATTR_SYNTAX_GENERALIZED_TIME_INVALID_CHAR.get(valueString, String |
| | | .valueOf(s1), 12); |
| | | final DecodeException e = DecodeException.error(message); |
| | | StaticUtils.DEBUG_LOG.throwing("GeneralizedTimeSyntax", "valueIsAcceptable", e); |
| | | throw e; |
| | | } |
| | | |
| | | // Next, there should be either a period or comma followed by |
| | | // between one and three digits (to specify the sub-second), a |
| | | // letter 'Z' (for the UTC specifier), or a plus or minus sign |
| | | // followed by two our four digits (for the UTC offset). |
| | | switch (valueString.charAt(14)) { |
| | | case '.': |
| | | case ',': |
| | | return finishDecodingFraction(valueString, 15, year, month, day, hour, minute, second, |
| | | 1000); |
| | | |
| | | case 'Z': |
| | | // This is fine only if we are at the end of the value. |
| | | if (length == 15) { |
| | | final TimeZone tz = TIME_ZONE_UTC_OBJ; |
| | | return createTime(valueString, year, month, day, hour, minute, second, tz); |
| | | } else { |
| | | final LocalizableMessage message = |
| | | WARN_ATTR_SYNTAX_GENERALIZED_TIME_INVALID_CHAR.get(valueString, String |
| | | .valueOf(valueString.charAt(14)), 14); |
| | | final DecodeException e = DecodeException.error(message); |
| | | StaticUtils.DEBUG_LOG.throwing("GeneralizedTimeSyntax", "valueIsAcceptable", e); |
| | | throw e; |
| | | } |
| | | |
| | | case '+': |
| | | case '-': |
| | | // These are fine only if there are exactly two or four more |
| | | // digits that specify a valid offset. |
| | | if ((length == 17) || (length == 19)) { |
| | | final TimeZone tz = getTimeZoneForOffset(valueString, 14); |
| | | return createTime(valueString, year, month, day, hour, minute, second, tz); |
| | | } else { |
| | | final LocalizableMessage message = |
| | | WARN_ATTR_SYNTAX_GENERALIZED_TIME_INVALID_CHAR.get(valueString, String |
| | | .valueOf(valueString.charAt(14)), 14); |
| | | final DecodeException e = DecodeException.error(message); |
| | | StaticUtils.DEBUG_LOG.throwing("GeneralizedTimeSyntax", "valueIsAcceptable", e); |
| | | throw e; |
| | | } |
| | | |
| | | default: |
| | | final LocalizableMessage message = |
| | | WARN_ATTR_SYNTAX_GENERALIZED_TIME_INVALID_CHAR.get(valueString, String |
| | | .valueOf(valueString.charAt(14)), 14); |
| | | final DecodeException e = DecodeException.error(message); |
| | | StaticUtils.DEBUG_LOG.throwing("GeneralizedTimeSyntax", "valueIsAcceptable", e); |
| | | throw e; |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Returns the generalized time syntax encoding of the provided |
| | | * {@code Calendar}. |
| | | * |
| | | * @param value |
| | | * The calendar to be encoded. |
| | | * @return The generalized time syntax encoding. |
| | | */ |
| | | public static ByteString encode(final Calendar value) { |
| | | return encode(value.getTimeInMillis()); |
| | | } |
| | | |
| | | /** |
| | | * Returns the generalized time syntax encoding of the provided {@code Date} |
| | | * . |
| | | * |
| | | * @param value |
| | | * The date to be encoded. |
| | | * @return The generalized time syntax encoding. |
| | | */ |
| | | public static ByteString encode(final Date value) { |
| | | return encode(value.getTime()); |
| | | } |
| | | |
| | | /** |
| | | * Returns the generalized time syntax encoding of the provided date |
| | | * represented as milliseconds since the epoch. |
| | | * |
| | | * @param value |
| | | * The date in milli-seconds since the epoch. |
| | | * @return The generalized time syntax encoding. |
| | | */ |
| | | public static ByteString encode(final long value) { |
| | | // Generalized time has the format yyyyMMddHHmmss.SSS'Z' |
| | | |
| | | // Do this in a thread-safe non-synchronized fashion. |
| | | // (Simple)DateFormat is neither fast nor thread-safe. |
| | | final StringBuilder sb = new StringBuilder(19); |
| | | final GregorianCalendar calendar = new GregorianCalendar(TIME_ZONE_UTC_OBJ); |
| | | calendar.setLenient(false); |
| | | calendar.setTimeInMillis(value); |
| | | |
| | | // Format the year yyyy. |
| | | int n = calendar.get(Calendar.YEAR); |
| | | if (n < 0) { |
| | | throw new IllegalArgumentException("Year cannot be < 0:" + n); |
| | | } else if (n < 10) { |
| | | sb.append("000"); |
| | | } else if (n < 100) { |
| | | sb.append("00"); |
| | | } else if (n < 1000) { |
| | | sb.append("0"); |
| | | } |
| | | sb.append(n); |
| | | |
| | | // Format the month MM. |
| | | n = calendar.get(Calendar.MONTH) + 1; |
| | | if (n < 10) { |
| | | sb.append("0"); |
| | | } |
| | | sb.append(n); |
| | | |
| | | // Format the day dd. |
| | | n = calendar.get(Calendar.DAY_OF_MONTH); |
| | | if (n < 10) { |
| | | sb.append("0"); |
| | | } |
| | | sb.append(n); |
| | | |
| | | // Format the hour HH. |
| | | n = calendar.get(Calendar.HOUR_OF_DAY); |
| | | if (n < 10) { |
| | | sb.append("0"); |
| | | } |
| | | sb.append(n); |
| | | |
| | | // Format the minute mm. |
| | | n = calendar.get(Calendar.MINUTE); |
| | | if (n < 10) { |
| | | sb.append("0"); |
| | | } |
| | | sb.append(n); |
| | | |
| | | // Format the seconds ss. |
| | | n = calendar.get(Calendar.SECOND); |
| | | if (n < 10) { |
| | | sb.append("0"); |
| | | } |
| | | sb.append(n); |
| | | |
| | | // Format the milli-seconds. |
| | | sb.append('.'); |
| | | n = calendar.get(Calendar.MILLISECOND); |
| | | if (n < 10) { |
| | | sb.append("00"); |
| | | } else if (n < 100) { |
| | | sb.append("0"); |
| | | } |
| | | sb.append(n); |
| | | |
| | | // Format the timezone (always Z). |
| | | sb.append('Z'); |
| | | |
| | | return ByteString.valueOf(sb.toString()); |
| | | } |
| | | |
| | | /** |
| | | * Returns a Calendar object representing the provided date / time |
| | | * parameters. |
| | | * |
| | | * @param value |
| | | * The generalized time string representation. |
| | | * @param year |
| | | * The year. |
| | | * @param month |
| | | * The month. |
| | | * @param day |
| | | * The day. |
| | | * @param hour |
| | | * The hour. |
| | | * @param minute |
| | | * The minute. |
| | | * @param second |
| | | * The second. |
| | | * @param tz |
| | | * The timezone. |
| | | * @return A Calendar object representing the provided date / time |
| | | * parameters. |
| | | * @throws DecodeException |
| | | * If the calendar could not be created. |
| | | */ |
| | | private static Calendar createTime(final String value, final int year, final int month, |
| | | final int day, final int hour, final int minute, final int second, final TimeZone tz) |
| | | throws DecodeException { |
| | | try { |
| | | final GregorianCalendar calendar = new GregorianCalendar(); |
| | | calendar.setLenient(false); |
| | | calendar.setTimeZone(tz); |
| | | calendar.set(year, month, day, hour, minute, second); |
| | | calendar.set(Calendar.MILLISECOND, 0); |
| | | return calendar; |
| | | } catch (final Exception e) { |
| | | // This should only happen if the provided date wasn't legal |
| | | // (e.g., September 31). |
| | | final LocalizableMessage message = |
| | | WARN_ATTR_SYNTAX_GENERALIZED_TIME_ILLEGAL_TIME.get(value, String.valueOf(e)); |
| | | final DecodeException de = DecodeException.error(message, e); |
| | | StaticUtils.DEBUG_LOG.throwing("GeneralizedTimeSyntax", "valueIsAcceptable", de); |
| | | throw de; |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Completes decoding the generalized time value containing a fractional |
| | | * component. It will also decode the trailing 'Z' or offset. |
| | | * |
| | | * @param value |
| | | * The whole value, including the fractional component and time |
| | | * zone information. |
| | | * @param startPos |
| | | * The position of the first character after the period in the |
| | | * value string. |
| | | * @param year |
| | | * The year decoded from the provided value. |
| | | * @param month |
| | | * The month decoded from the provided value. |
| | | * @param day |
| | | * The day decoded from the provided value. |
| | | * @param hour |
| | | * The hour decoded from the provided value. |
| | | * @param minute |
| | | * The minute decoded from the provided value. |
| | | * @param second |
| | | * The second decoded from the provided value. |
| | | * @param multiplier |
| | | * The multiplier value that should be used to scale the fraction |
| | | * appropriately. If it's a fraction of an hour, then it should |
| | | * be 3600000 (60*60*1000). If it's a fraction of a minute, then |
| | | * it should be 60000. If it's a fraction of a second, then it |
| | | * should be 1000. |
| | | * @return The timestamp created from the provided generalized time value |
| | | * including the fractional element. |
| | | * @throws DecodeException |
| | | * If the provided value cannot be parsed as a valid generalized |
| | | * time string. |
| | | */ |
| | | private static Calendar finishDecodingFraction(final String value, final int startPos, |
| | | final int year, final int month, final int day, final int hour, final int minute, |
| | | final int second, final int multiplier) throws DecodeException { |
| | | final int length = value.length(); |
| | | final StringBuilder fractionBuffer = new StringBuilder((2 + length) - startPos); |
| | | fractionBuffer.append("0."); |
| | | |
| | | TimeZone timeZone = null; |
| | | |
| | | outerLoop: |
| | | for (int i = startPos; i < length; i++) { |
| | | final char c = value.charAt(i); |
| | | switch (c) { |
| | | case '0': |
| | | case '1': |
| | | case '2': |
| | | case '3': |
| | | case '4': |
| | | case '5': |
| | | case '6': |
| | | case '7': |
| | | case '8': |
| | | case '9': |
| | | fractionBuffer.append(c); |
| | | break; |
| | | |
| | | case 'Z': |
| | | // This is only acceptable if we're at the end of the value. |
| | | if (i != (value.length() - 1)) { |
| | | final LocalizableMessage message = |
| | | WARN_ATTR_SYNTAX_GENERALIZED_TIME_ILLEGAL_FRACTION_CHAR.get(value, |
| | | String.valueOf(c)); |
| | | final DecodeException e = DecodeException.error(message); |
| | | StaticUtils.DEBUG_LOG.throwing("GeneralizedTimeSyntax", |
| | | "finishDecodingFraction", e); |
| | | throw e; |
| | | } |
| | | |
| | | timeZone = TIME_ZONE_UTC_OBJ; |
| | | break outerLoop; |
| | | |
| | | case '+': |
| | | case '-': |
| | | timeZone = getTimeZoneForOffset(value, i); |
| | | break outerLoop; |
| | | |
| | | default: |
| | | final LocalizableMessage message = |
| | | WARN_ATTR_SYNTAX_GENERALIZED_TIME_ILLEGAL_FRACTION_CHAR.get(value, String |
| | | .valueOf(c)); |
| | | final DecodeException e = DecodeException.error(message); |
| | | StaticUtils.DEBUG_LOG |
| | | .throwing("GeneralizedTimeSyntax", "finishDecodingFraction", e); |
| | | throw e; |
| | | } |
| | | } |
| | | |
| | | if (fractionBuffer.length() == 2) { |
| | | final LocalizableMessage message = |
| | | WARN_ATTR_SYNTAX_GENERALIZED_TIME_EMPTY_FRACTION.get(value); |
| | | final DecodeException e = DecodeException.error(message); |
| | | StaticUtils.DEBUG_LOG.throwing("GeneralizedTimeSyntax", "finishDecodingFraction", e); |
| | | throw e; |
| | | } |
| | | |
| | | if (timeZone == null) { |
| | | final LocalizableMessage message = |
| | | WARN_ATTR_SYNTAX_GENERALIZED_TIME_NO_TIME_ZONE_INFO.get(value); |
| | | final DecodeException e = DecodeException.error(message); |
| | | StaticUtils.DEBUG_LOG.throwing("GeneralizedTimeSyntax", "finishDecodingFraction", e); |
| | | throw e; |
| | | } |
| | | |
| | | final Double fractionValue = Double.parseDouble(fractionBuffer.toString()); |
| | | final int additionalMilliseconds = (int) Math.round(fractionValue * multiplier); |
| | | |
| | | try { |
| | | final GregorianCalendar calendar = new GregorianCalendar(); |
| | | calendar.setLenient(false); |
| | | calendar.setTimeZone(timeZone); |
| | | calendar.set(year, month, day, hour, minute, second); |
| | | calendar.set(Calendar.MILLISECOND, additionalMilliseconds); |
| | | return calendar; |
| | | } catch (final Exception e) { |
| | | |
| | | // This should only happen if the provided date wasn't legal |
| | | // (e.g., September 31). |
| | | final LocalizableMessage message = |
| | | WARN_ATTR_SYNTAX_GENERALIZED_TIME_ILLEGAL_TIME.get(value, String.valueOf(e)); |
| | | final DecodeException de = DecodeException.error(message, e); |
| | | StaticUtils.DEBUG_LOG.throwing("GeneralizedTimeSyntax", "valueIsAcceptable", de); |
| | | throw de; |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Decodes a time zone offset from the provided value. |
| | | * |
| | | * @param value |
| | | * The whole value, including the offset. |
| | | * @param startPos |
| | | * The position of the first character that is contained in the |
| | | * offset. This should be the position of the plus or minus |
| | | * character. |
| | | * @return The {@code TimeZone} object representing the decoded time zone. |
| | | * @throws DecodeException |
| | | * If the provided value does not contain a valid offset. |
| | | */ |
| | | private static TimeZone getTimeZoneForOffset(final String value, final int startPos) |
| | | throws DecodeException { |
| | | final String offSetStr = value.substring(startPos); |
| | | if ((offSetStr.length() != 3) && (offSetStr.length() != 5)) { |
| | | final LocalizableMessage message = |
| | | WARN_ATTR_SYNTAX_GENERALIZED_TIME_INVALID_OFFSET.get(value, offSetStr); |
| | | final DecodeException e = DecodeException.error(message); |
| | | StaticUtils.DEBUG_LOG.throwing("GeneralizedTimeSyntax", "getTimeZoneForOffset", e); |
| | | throw e; |
| | | } |
| | | |
| | | // The first character must be either a plus or minus. |
| | | switch (offSetStr.charAt(0)) { |
| | | case '+': |
| | | case '-': |
| | | // These are OK. |
| | | break; |
| | | |
| | | default: |
| | | final LocalizableMessage message = |
| | | WARN_ATTR_SYNTAX_GENERALIZED_TIME_INVALID_OFFSET.get(value, offSetStr); |
| | | final DecodeException e = DecodeException.error(message); |
| | | StaticUtils.DEBUG_LOG.throwing("GeneralizedTimeSyntax", "getTimeZoneForOffset", e); |
| | | throw e; |
| | | } |
| | | |
| | | // The first two characters must be an integer between 00 and 23. |
| | | switch (offSetStr.charAt(1)) { |
| | | case '0': |
| | | case '1': |
| | | switch (offSetStr.charAt(2)) { |
| | | case '0': |
| | | case '1': |
| | | case '2': |
| | | case '3': |
| | | case '4': |
| | | case '5': |
| | | case '6': |
| | | case '7': |
| | | case '8': |
| | | case '9': |
| | | // These are all fine. |
| | | break; |
| | | |
| | | default: |
| | | final LocalizableMessage message = |
| | | WARN_ATTR_SYNTAX_GENERALIZED_TIME_INVALID_OFFSET.get(value, offSetStr); |
| | | final DecodeException e = DecodeException.error(message); |
| | | StaticUtils.DEBUG_LOG.throwing("GeneralizedTimeSyntax", "getTimeZoneForOffset", e); |
| | | throw e; |
| | | } |
| | | break; |
| | | |
| | | case '2': |
| | | switch (offSetStr.charAt(2)) { |
| | | case '0': |
| | | case '1': |
| | | case '2': |
| | | case '3': |
| | | // These are all fine. |
| | | break; |
| | | |
| | | default: |
| | | final LocalizableMessage message = |
| | | WARN_ATTR_SYNTAX_GENERALIZED_TIME_INVALID_OFFSET.get(value, offSetStr); |
| | | final DecodeException e = DecodeException.error(message); |
| | | StaticUtils.DEBUG_LOG.throwing("GeneralizedTimeSyntax", "getTimeZoneForOffset", e); |
| | | throw e; |
| | | } |
| | | break; |
| | | |
| | | default: |
| | | final LocalizableMessage message = |
| | | WARN_ATTR_SYNTAX_GENERALIZED_TIME_INVALID_OFFSET.get(value, offSetStr); |
| | | final DecodeException e = DecodeException.error(message); |
| | | StaticUtils.DEBUG_LOG.throwing("GeneralizedTimeSyntax", "getTimeZoneForOffset", e); |
| | | throw e; |
| | | } |
| | | |
| | | // If there are two more characters, then they must be an integer |
| | | // between 00 and 59. |
| | | if (offSetStr.length() == 5) { |
| | | switch (offSetStr.charAt(3)) { |
| | | case '0': |
| | | case '1': |
| | | case '2': |
| | | case '3': |
| | | case '4': |
| | | case '5': |
| | | switch (offSetStr.charAt(4)) { |
| | | case '0': |
| | | case '1': |
| | | case '2': |
| | | case '3': |
| | | case '4': |
| | | case '5': |
| | | case '6': |
| | | case '7': |
| | | case '8': |
| | | case '9': |
| | | // These are all fine. |
| | | break; |
| | | |
| | | default: |
| | | final LocalizableMessage message = |
| | | WARN_ATTR_SYNTAX_GENERALIZED_TIME_INVALID_OFFSET.get(value, offSetStr); |
| | | final DecodeException e = DecodeException.error(message); |
| | | StaticUtils.DEBUG_LOG.throwing("GeneralizedTimeSyntax", "getTimeZoneForOffset", |
| | | e); |
| | | throw e; |
| | | } |
| | | break; |
| | | |
| | | default: |
| | | final LocalizableMessage message = |
| | | WARN_ATTR_SYNTAX_GENERALIZED_TIME_INVALID_OFFSET.get(value, offSetStr); |
| | | final DecodeException e = DecodeException.error(message); |
| | | StaticUtils.DEBUG_LOG.throwing("GeneralizedTimeSyntax", "getTimeZoneForOffset", e); |
| | | throw e; |
| | | } |
| | | } |
| | | |
| | | // If we've gotten here, then it looks like a valid offset. We can |
| | | // create a time zone by using "GMT" followed by the offset. |
| | | return TimeZone.getTimeZone("GMT" + offSetStr); |
| | | } |
| | | |
| | | private GeneralizedTime() { |
| | | // Prevent instantiation. |
| | | } |
| | | |
| | | } |
| | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public AttributeParser parse() { |
| | | return AttributeParser.parseAttribute(this); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override |
| | | public abstract Iterator<ByteString> iterator(); |
| | | |
| | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public AttributeParser parseAttribute(AttributeDescription attributeDescription) { |
| | | return AttributeParser.parseAttribute(getAttribute(attributeDescription)); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public AttributeParser parseAttribute(String attributeDescription) { |
| | | return AttributeParser.parseAttribute(getAttribute(attributeDescription)); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public boolean removeAttribute(final AttributeDescription attributeDescription) { |
| | | return removeAttribute(Attributes.emptyAttribute(attributeDescription), null); |
| | | } |
| | |
| | | Iterator<ByteString> iterator(); |
| | | |
| | | /** |
| | | * Returns a parser for this attribute which can be used for decoding values |
| | | * as different types of object. |
| | | * |
| | | * @return A parser for this attribute. |
| | | */ |
| | | AttributeParser parse(); |
| | | |
| | | /** |
| | | * Removes {@code value} from this attribute if it is present (optional |
| | | * operation). If this attribute does not contain {@code value}, the call |
| | | * leaves the attribute unchanged and returns {@code false}. |
| New file |
| | |
| | | /* |
| | | * CDDL HEADER START |
| | | * |
| | | * The contents of this file are subject to the terms of the |
| | | * Common Development and Distribution License, Version 1.0 only |
| | | * (the "License"). You may not use this file except in compliance |
| | | * with the License. |
| | | * |
| | | * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt |
| | | * or http://forgerock.org/license/CDDLv1.0.html. |
| | | * See the License for the specific language governing permissions |
| | | * and limitations under the License. |
| | | * |
| | | * When distributing Covered Code, include this CDDL HEADER in each |
| | | * file and include the License file at legal-notices/CDDLv1_0.txt. |
| | | * If applicable, add the following below this CDDL HEADER, with the |
| | | * fields enclosed by brackets "[]" replaced with your own identifying |
| | | * information: |
| | | * Portions Copyright [yyyy] [name of copyright owner] |
| | | * |
| | | * CDDL HEADER END |
| | | * |
| | | * |
| | | * Copyright 2012 ForgeRock AS. |
| | | */ |
| | | |
| | | package org.forgerock.opendj.ldap; |
| | | |
| | | import java.util.Arrays; |
| | | import java.util.Calendar; |
| | | import java.util.Collection; |
| | | import java.util.LinkedHashSet; |
| | | import java.util.NoSuchElementException; |
| | | import java.util.Set; |
| | | |
| | | import org.forgerock.opendj.ldap.schema.Schema; |
| | | |
| | | import com.forgerock.opendj.util.Base64; |
| | | import com.forgerock.opendj.util.Collections2; |
| | | import com.forgerock.opendj.util.Function; |
| | | import com.forgerock.opendj.util.Functions; |
| | | |
| | | /** |
| | | * A fluent API for parsing attributes as different types of value. An attribute |
| | | * parser is obtained from an entry using the method |
| | | * {@link Entry#parseAttribute} or from an attribute using |
| | | * {@link Attribute#parse()}. |
| | | * <p> |
| | | * Methods throw an {@code IllegalArgumentException} when a value cannot be |
| | | * parsed (e.g. because its syntax is invalid). Methods which return a |
| | | * {@code Set} always return a modifiable non-{@code null} result. |
| | | * <p> |
| | | * Examples: |
| | | * |
| | | * <pre> |
| | | * Entry entry = ...; |
| | | * |
| | | * Calendar timestamp = entry.parseAttribute("createTimestamp").asCalendar(); |
| | | * boolean isEnabled = entry.parseAttribute("enabled").asBoolean(false); |
| | | * |
| | | * Entry group = ...; |
| | | * Schema schema = ...; |
| | | * |
| | | * Set<DN> members = group.parseAttribute("member").usingSchema(schema).asSetOfDN(); |
| | | * </pre> |
| | | * |
| | | * @see Entry#parseAttribute |
| | | * @see Attribute#parse() |
| | | */ |
| | | public final class AttributeParser { |
| | | // TODO: enums, filters, rdns? |
| | | |
| | | private static final AttributeParser NULL_INSTANCE = new AttributeParser(null); |
| | | |
| | | /** |
| | | * Returns an attribute parser for the provided attribute. {@code null} |
| | | * attributes are permitted and will be treated as if an empty attribute was |
| | | * provided. |
| | | * |
| | | * @param attribute |
| | | * The attribute to be parsed, which may be {@code null}. |
| | | * @return The attribute parser. |
| | | */ |
| | | public static AttributeParser parseAttribute(final Attribute attribute) { |
| | | return isEmpty(attribute) ? NULL_INSTANCE : new AttributeParser(attribute); |
| | | } |
| | | |
| | | private static boolean isEmpty(final Attribute attribute) { |
| | | return (attribute == null) || attribute.isEmpty(); |
| | | } |
| | | |
| | | private final Attribute attribute; |
| | | private Schema schema; |
| | | |
| | | private AttributeParser(final Attribute attribute) { |
| | | this.attribute = attribute; |
| | | } |
| | | |
| | | /** |
| | | * Returns the first value decoded as an {@code AttributeDescription} using |
| | | * the schema associated with this parser, or {@code null} if the attribute |
| | | * does not contain any values. |
| | | * |
| | | * @return The first value decoded as an {@code AttributeDescription}. |
| | | */ |
| | | public AttributeDescription asAttributeDescription() { |
| | | return asAttributeDescription(null); |
| | | } |
| | | |
| | | /** |
| | | * Returns the first value decoded as an {@code AttributeDescription} using |
| | | * the schema associated with this parser, or {@code defaultValue} if the |
| | | * attribute does not contain any values. |
| | | * |
| | | * @param defaultValue |
| | | * The default value to return if the attribute is empty. |
| | | * @return The first value decoded as an {@code AttributeDescription}. |
| | | */ |
| | | public AttributeDescription asAttributeDescription(final AttributeDescription defaultValue) { |
| | | return parseSingleValue(Functions.valueToAttributeDescription(getSchema()), defaultValue); |
| | | } |
| | | |
| | | /** |
| | | * Returns the first value encoded as base64, or {@code null} if the |
| | | * attribute does not contain any values. |
| | | * |
| | | * @return The first value encoded as base64. |
| | | */ |
| | | public String asBase64() { |
| | | return asBase64(null); |
| | | } |
| | | |
| | | /** |
| | | * Returns the first value encoded as base64, or {@code defaultValue} if the |
| | | * attribute does not contain any values. |
| | | * |
| | | * @param defaultValue |
| | | * The default value to return if the attribute is empty. |
| | | * @return The first value encoded as base64. |
| | | */ |
| | | public String asBase64(final ByteString defaultValue) { |
| | | return parseSingleValue(Functions.valueToBase64(), Base64.encode(defaultValue)); |
| | | } |
| | | |
| | | /** |
| | | * Returns the first value decoded as a boolean, or {@code null} if the |
| | | * attribute does not contain any values. |
| | | * |
| | | * @return The first value decoded as a boolean. |
| | | */ |
| | | public Boolean asBoolean() { |
| | | return isEmpty(attribute) ? null : asBoolean(false /* ignored */); |
| | | } |
| | | |
| | | /** |
| | | * Returns the first value decoded as an {@code Boolean}, or |
| | | * {@code defaultValue} if the attribute does not contain any values. |
| | | * |
| | | * @param defaultValue |
| | | * The default value to return if the attribute is empty. |
| | | * @return The first value decoded as an {@code Boolean}. |
| | | */ |
| | | public boolean asBoolean(final boolean defaultValue) { |
| | | return parseSingleValue(Functions.valueToBoolean(), defaultValue); |
| | | } |
| | | |
| | | /** |
| | | * Returns the first value, or {@code null} if the attribute does not |
| | | * contain any values. |
| | | * |
| | | * @return The first value. |
| | | */ |
| | | public ByteString asByteString() { |
| | | return asByteString(null); |
| | | } |
| | | |
| | | /** |
| | | * Returns the first value, or {@code defaultValue} if the attribute does |
| | | * not contain any values. |
| | | * |
| | | * @param defaultValue |
| | | * The default value to return if the attribute is empty. |
| | | * @return The first value. |
| | | */ |
| | | public ByteString asByteString(final ByteString defaultValue) { |
| | | return parseSingleValue(Functions.<ByteString> identityFunction(), defaultValue); |
| | | } |
| | | |
| | | /** |
| | | * Returns the first value decoded as a {@code Calendar} using the |
| | | * generalized time syntax, or {@code null} if the attribute does not |
| | | * contain any values. |
| | | * |
| | | * @return The first value decoded as a {@code Calendar}. |
| | | */ |
| | | public Calendar asCalendar() { |
| | | return asCalendar(null); |
| | | } |
| | | |
| | | /** |
| | | * Returns the first value decoded as an {@code Calendar} using the |
| | | * generalized time syntax, or {@code defaultValue} if the attribute does |
| | | * not contain any values. |
| | | * |
| | | * @param defaultValue |
| | | * The default value to return if the attribute is empty. |
| | | * @return The first value decoded as an {@code Calendar}. |
| | | */ |
| | | public Calendar asCalendar(final Calendar defaultValue) { |
| | | return parseSingleValue(Functions.valueToCalendar(), defaultValue); |
| | | } |
| | | |
| | | /** |
| | | * Returns the first value decoded as a {@code DN} using the schema |
| | | * associated with this parser, or {@code null} if the attribute does not |
| | | * contain any values. |
| | | * |
| | | * @return The first value decoded as a {@code DN}. |
| | | */ |
| | | public DN asDN() { |
| | | return asDN(null); |
| | | } |
| | | |
| | | /** |
| | | * Returns the first value decoded as a {@code DN} using the schema |
| | | * associated with this parser, or {@code defaultValue} if the attribute |
| | | * does not contain any values. |
| | | * |
| | | * @param defaultValue |
| | | * The default value to return if the attribute is empty. |
| | | * @return The first value decoded as a {@code DN}. |
| | | */ |
| | | public DN asDN(final DN defaultValue) { |
| | | return parseSingleValue(Functions.valueToDN(getSchema()), defaultValue); |
| | | } |
| | | |
| | | /** |
| | | * Returns the first value decoded as an {@code Integer}, or {@code null} if |
| | | * the attribute does not contain any values. |
| | | * |
| | | * @return The first value decoded as an {@code Integer}. |
| | | */ |
| | | public Integer asInteger() { |
| | | return isEmpty(attribute) ? null : asInteger(0 /* ignored */); |
| | | } |
| | | |
| | | /** |
| | | * Returns the first value decoded as an {@code Integer}, or |
| | | * {@code defaultValue} if the attribute does not contain any values. |
| | | * |
| | | * @param defaultValue |
| | | * The default value to return if the attribute is empty. |
| | | * @return The first value decoded as an {@code Integer}. |
| | | */ |
| | | public int asInteger(final int defaultValue) { |
| | | return parseSingleValue(Functions.valueToInteger(), defaultValue); |
| | | } |
| | | |
| | | /** |
| | | * Returns the first value decoded as a {@code Long}, or {@code null} if the |
| | | * attribute does not contain any values. |
| | | * |
| | | * @return The first value decoded as a {@code Long}. |
| | | */ |
| | | public Long asLong() { |
| | | return isEmpty(attribute) ? null : asLong(0L /* ignored */); |
| | | } |
| | | |
| | | /** |
| | | * Returns the first value decoded as a {@code Long}, or |
| | | * {@code defaultValue} if the attribute does not contain any values. |
| | | * |
| | | * @param defaultValue |
| | | * The default value to return if the attribute is empty. |
| | | * @return The first value decoded as a {@code Long}. |
| | | */ |
| | | public long asLong(final long defaultValue) { |
| | | return parseSingleValue(Functions.valueToLong(), defaultValue); |
| | | } |
| | | |
| | | /** |
| | | * Returns the values decoded as a set of {@code AttributeDescription}s |
| | | * using the schema associated with this parser, or {@code defaultValues} if |
| | | * the attribute does not contain any values. |
| | | * |
| | | * @param defaultValues |
| | | * The default values to return if the attribute is empty. |
| | | * @return The values decoded as a set of {@code AttributeDescription}s. |
| | | */ |
| | | public Set<AttributeDescription> asSetOfAttributeDescription( |
| | | final AttributeDescription... defaultValues) { |
| | | return asSetOfAttributeDescription(Arrays.asList(defaultValues)); |
| | | } |
| | | |
| | | /** |
| | | * Returns the values decoded as a set of {@code AttributeDescription}s |
| | | * using the schema associated with this parser, or {@code defaultValues} if |
| | | * the attribute does not contain any values. |
| | | * |
| | | * @param defaultValues |
| | | * The default values to return if the attribute is empty. |
| | | * @return The values decoded as a set of {@code AttributeDescription}s. |
| | | */ |
| | | public Set<AttributeDescription> asSetOfAttributeDescription( |
| | | final Collection<AttributeDescription> defaultValues) { |
| | | return parseMultipleValues(Functions.valueToAttributeDescription(), defaultValues); |
| | | } |
| | | |
| | | /** |
| | | * Returns the values contained in the attribute encoded as base64, or |
| | | * {@code defaultValues} if the attribute does not contain any values. |
| | | * |
| | | * @param defaultValues |
| | | * The default values to return if the attribute is empty. |
| | | * @return The values contained in the attribute encoded as base64. |
| | | */ |
| | | public Set<String> asSetOfBase64(final Collection<ByteString> defaultValues) { |
| | | return parseMultipleValues(Functions.valueToString(), Collections2.transformedCollection( |
| | | defaultValues, Functions.valueToBase64(), null)); |
| | | } |
| | | |
| | | /** |
| | | * Returns the values contained in the attribute encoded as base64, or |
| | | * {@code defaultValues} if the attribute does not contain any values. |
| | | * |
| | | * @param defaultValues |
| | | * The default values to return if the attribute is empty. |
| | | * @return The values contained in the attribute encoded as base64. |
| | | */ |
| | | public Set<String> asSetOfBase64(final String... defaultValues) { |
| | | return asSetOfString(Arrays.asList(defaultValues)); |
| | | } |
| | | |
| | | /** |
| | | * Returns the values decoded as a set of {@code Boolean}s, or |
| | | * {@code defaultValues} if the attribute does not contain any values. |
| | | * |
| | | * @param defaultValues |
| | | * The default values to return if the attribute is empty. |
| | | * @return The values decoded as a set of {@code Boolean}s. |
| | | */ |
| | | public Set<Boolean> asSetOfBoolean(final Boolean... defaultValues) { |
| | | return asSetOfBoolean(Arrays.asList(defaultValues)); |
| | | } |
| | | |
| | | /** |
| | | * Returns the values decoded as a set of {@code Boolean}s, or |
| | | * {@code defaultValues} if the attribute does not contain any values. |
| | | * |
| | | * @param defaultValues |
| | | * The default values to return if the attribute is empty. |
| | | * @return The values decoded as a set of {@code Boolean}s. |
| | | */ |
| | | public Set<Boolean> asSetOfBoolean(final Collection<Boolean> defaultValues) { |
| | | return parseMultipleValues(Functions.valueToBoolean(), defaultValues); |
| | | } |
| | | |
| | | /** |
| | | * Returns the values contained in the attribute, or {@code defaultValues} |
| | | * if the attribute does not contain any values. |
| | | * |
| | | * @param defaultValues |
| | | * The default values to return if the attribute is empty. |
| | | * @return The values contained in the attribute. |
| | | */ |
| | | public Set<ByteString> asSetOfByteString(final ByteString... defaultValues) { |
| | | return asSetOfByteString(Arrays.asList(defaultValues)); |
| | | } |
| | | |
| | | /** |
| | | * Returns the values contained in the attribute, or {@code defaultValues} |
| | | * if the attribute does not contain any values. |
| | | * |
| | | * @param defaultValues |
| | | * The default values to return if the attribute is empty. |
| | | * @return The values contained in the attribute. |
| | | */ |
| | | public Set<ByteString> asSetOfByteString(final Collection<ByteString> defaultValues) { |
| | | return parseMultipleValues(Functions.<ByteString> identityFunction(), defaultValues); |
| | | } |
| | | |
| | | /** |
| | | * Returns the values decoded as a set of {@code Calendar}s using the |
| | | * generalized time syntax, or {@code defaultValues} if the attribute does |
| | | * not contain any values. |
| | | * |
| | | * @param defaultValues |
| | | * The default values to return if the attribute is empty. |
| | | * @return The values decoded as a set of {@code Calendar}s. |
| | | */ |
| | | public Set<Calendar> asSetOfCalendar(final Calendar... defaultValues) { |
| | | return asSetOfCalendar(Arrays.asList(defaultValues)); |
| | | } |
| | | |
| | | /** |
| | | * Returns the values decoded as a set of {@code Calendar}s using the |
| | | * generalized time syntax, or {@code defaultValues} if the attribute does |
| | | * not contain any values. |
| | | * |
| | | * @param defaultValues |
| | | * The default values to return if the attribute is empty. |
| | | * @return The values decoded as a set of {@code Calendar}s. |
| | | */ |
| | | public Set<Calendar> asSetOfCalendar(final Collection<Calendar> defaultValues) { |
| | | return parseMultipleValues(Functions.valueToCalendar(), defaultValues); |
| | | } |
| | | |
| | | /** |
| | | * Returns the values decoded as a set of {@code DN}s using the schema |
| | | * associated with this parser, or {@code defaultValues} if the attribute |
| | | * does not contain any values. |
| | | * |
| | | * @param defaultValues |
| | | * The default values to return if the attribute is empty. |
| | | * @return The values decoded as a set of {@code DN}s. |
| | | */ |
| | | public Set<DN> asSetOfDN(final Collection<DN> defaultValues) { |
| | | return parseMultipleValues(Functions.valueToDN(), defaultValues); |
| | | } |
| | | |
| | | /** |
| | | * Returns the values decoded as a set of {@code DN}s using the schema |
| | | * associated with this parser, or {@code defaultValues} if the attribute |
| | | * does not contain any values. |
| | | * |
| | | * @param defaultValues |
| | | * The default values to return if the attribute is empty. |
| | | * @return The values decoded as a set of {@code DN}s. |
| | | */ |
| | | public Set<DN> asSetOfDN(final DN... defaultValues) { |
| | | return asSetOfDN(Arrays.asList(defaultValues)); |
| | | } |
| | | |
| | | /** |
| | | * Returns the values decoded as a set of {@code Integer}s, or |
| | | * {@code defaultValues} if the attribute does not contain any values. |
| | | * |
| | | * @param defaultValues |
| | | * The default values to return if the attribute is empty. |
| | | * @return The values decoded as a set of {@code Integer}s. |
| | | */ |
| | | public Set<Integer> asSetOfInteger(final Collection<Integer> defaultValues) { |
| | | return parseMultipleValues(Functions.valueToInteger(), defaultValues); |
| | | } |
| | | |
| | | /** |
| | | * Returns the values decoded as a set of {@code Integer}s, or |
| | | * {@code defaultValues} if the attribute does not contain any values. |
| | | * |
| | | * @param defaultValues |
| | | * The default values to return if the attribute is empty. |
| | | * @return The values decoded as a set of {@code Integer}s. |
| | | */ |
| | | public Set<Integer> asSetOfInteger(final Integer... defaultValues) { |
| | | return asSetOfInteger(Arrays.asList(defaultValues)); |
| | | } |
| | | |
| | | /** |
| | | * Returns the values decoded as a set of {@code Long}s, or |
| | | * {@code defaultValues} if the attribute does not contain any values. |
| | | * |
| | | * @param defaultValues |
| | | * The default values to return if the attribute is empty. |
| | | * @return The values decoded as a set of {@code Long}s. |
| | | */ |
| | | public Set<Long> asSetOfLong(final Collection<Long> defaultValues) { |
| | | return parseMultipleValues(Functions.valueToLong(), defaultValues); |
| | | } |
| | | |
| | | /** |
| | | * Returns the values decoded as a set of {@code Long}s, or |
| | | * {@code defaultValues} if the attribute does not contain any values. |
| | | * |
| | | * @param defaultValues |
| | | * The default values to return if the attribute is empty. |
| | | * @return The values decoded as a set of {@code Long}s. |
| | | */ |
| | | public Set<Long> asSetOfLong(final Long... defaultValues) { |
| | | return asSetOfLong(Arrays.asList(defaultValues)); |
| | | } |
| | | |
| | | /** |
| | | * Returns the values decoded as a set of {@code String}s, or |
| | | * {@code defaultValues} if the attribute does not contain any values. |
| | | * |
| | | * @param defaultValues |
| | | * The default values to return if the attribute is empty. |
| | | * @return The values decoded as a set of {@code String}s. |
| | | */ |
| | | public Set<String> asSetOfString(final Collection<String> defaultValues) { |
| | | return parseMultipleValues(Functions.valueToString(), defaultValues); |
| | | } |
| | | |
| | | /** |
| | | * Returns the values decoded as a set of {@code String}s, or |
| | | * {@code defaultValues} if the attribute does not contain any values. |
| | | * |
| | | * @param defaultValues |
| | | * The default values to return if the attribute is empty. |
| | | * @return The values decoded as a set of {@code String}s. |
| | | */ |
| | | public Set<String> asSetOfString(final String... defaultValues) { |
| | | return asSetOfString(Arrays.asList(defaultValues)); |
| | | } |
| | | |
| | | /** |
| | | * Returns the first value decoded as a {@code String}, or {@code null} if |
| | | * the attribute does not contain any values. |
| | | * |
| | | * @return The first value decoded as a {@code String}. |
| | | */ |
| | | public String asString() { |
| | | return asString(null); |
| | | } |
| | | |
| | | /** |
| | | * Returns the first value decoded as a {@code String}, or |
| | | * {@code defaultValue} if the attribute does not contain any values. |
| | | * |
| | | * @param defaultValue |
| | | * The default value to return if the attribute is empty. |
| | | * @return The first value decoded as a {@code String}. |
| | | */ |
| | | public String asString(final String defaultValue) { |
| | | return parseSingleValue(Functions.valueToString(), defaultValue); |
| | | } |
| | | |
| | | /** |
| | | * Throws a {@code NoSuchElementException} if the attribute referenced by |
| | | * this parser is {@code null} or empty. |
| | | * |
| | | * @return A reference to this attribute parser. |
| | | * @throws NoSuchElementException |
| | | * If the attribute referenced by this parser is {@code null} or |
| | | * empty. |
| | | */ |
| | | public AttributeParser requireValue() { |
| | | if (isEmpty(attribute)) { |
| | | throw new NoSuchElementException(); |
| | | } else { |
| | | return this; |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Sets the {@code Schema} which will be used when parsing schema sensitive |
| | | * values such as DNs and attribute descriptions. |
| | | * |
| | | * @param schema |
| | | * The {@code Schema} which will be used when parsing schema |
| | | * sensitive values. |
| | | * @return This attribute parser. |
| | | */ |
| | | public AttributeParser usingSchema(final Schema schema) { |
| | | // Avoid modifying the null instance: a schema will not be needed |
| | | // anyway. |
| | | if (this != NULL_INSTANCE) { |
| | | this.schema = schema; |
| | | } |
| | | return this; |
| | | } |
| | | |
| | | private Schema getSchema() { |
| | | return schema == null ? Schema.getDefaultSchema() : schema; |
| | | } |
| | | |
| | | private <T> Set<T> parseMultipleValues(final Function<ByteString, T, ?> f, |
| | | final Collection<? extends T> defaultValues) { |
| | | if (!isEmpty(attribute)) { |
| | | final LinkedHashSet<T> result = new LinkedHashSet<T>(attribute.size()); |
| | | for (final ByteString b : attribute) { |
| | | result.add(f.apply(b, null)); |
| | | } |
| | | return result; |
| | | } else if (defaultValues != null) { |
| | | return new LinkedHashSet<T>(defaultValues); |
| | | } else { |
| | | return new LinkedHashSet<T>(0); |
| | | } |
| | | } |
| | | |
| | | private <T> T parseSingleValue(final Function<ByteString, T, ?> f, final T defaultValue) { |
| | | if (!isEmpty(attribute)) { |
| | | return f.apply(attribute.firstValue(), null); |
| | | } else { |
| | | return defaultValue; |
| | | } |
| | | } |
| | | } |
| | |
| | | return AbstractAttribute.hashCode(this); |
| | | } |
| | | |
| | | public AttributeParser parse() { |
| | | return attribute.parse(); |
| | | } |
| | | |
| | | public boolean isEmpty() { |
| | | return attribute.isEmpty(); |
| | | } |
| | |
| | | return Iterators.unmodifiableIterator(attribute.iterator()); |
| | | } |
| | | |
| | | public AttributeParser parse() { |
| | | return attribute.parse(); |
| | | } |
| | | |
| | | public boolean remove(final Object value) { |
| | | throw new UnsupportedOperationException(); |
| | | } |
| | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public AttributeParser parseAttribute(AttributeDescription attributeDescription) { |
| | | return entry.parseAttribute(attributeDescription); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public AttributeParser parseAttribute(String attributeDescription) { |
| | | return entry.parseAttribute(attributeDescription); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override |
| | | public boolean removeAttribute(final Attribute attribute, |
| | | final Collection<ByteString> missingValues) { |
| | |
| | | int hashCode(); |
| | | |
| | | /** |
| | | * Returns a parser for the named attribute contained in this entry. |
| | | * |
| | | * @param attributeDescription |
| | | * The name of the attribute to be parsed. |
| | | * @return A parser for the named attribute. |
| | | * @throws NullPointerException |
| | | * If {@code attributeDescription} was {@code null}. |
| | | */ |
| | | AttributeParser parseAttribute(AttributeDescription attributeDescription); |
| | | |
| | | /** |
| | | * Returns a parser for the named attribute contained in this entry. |
| | | * <p> |
| | | * The attribute description will be decoded using the schema associated |
| | | * with this entry (usually the default schema). |
| | | * |
| | | * @param attributeDescription |
| | | * The name of the attribute to be parsed. |
| | | * @return A parser for the named attribute. |
| | | * @throws LocalizedIllegalArgumentException |
| | | * If {@code attributeDescription} could not be decoded using |
| | | * the schema associated with this entry. |
| | | * @throws NullPointerException |
| | | * If {@code attributeDescription} was {@code null}. |
| | | */ |
| | | AttributeParser parseAttribute(String attributeDescription); |
| | | |
| | | /** |
| | | * Removes all of the attribute values contained in {@code attribute} from |
| | | * this entry if it is present (optional operation). If {@code attribute} is |
| | | * empty then the entire attribute will be removed if it is present. |
| | |
| | | |
| | | import org.forgerock.opendj.ldap.Attribute; |
| | | import org.forgerock.opendj.ldap.AttributeDescription; |
| | | import org.forgerock.opendj.ldap.AttributeParser; |
| | | import org.forgerock.opendj.ldap.ByteString; |
| | | import org.forgerock.opendj.ldap.DN; |
| | | import org.forgerock.opendj.ldap.Entry; |
| | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public AttributeParser parseAttribute(AttributeDescription attributeDescription) { |
| | | return entry.parseAttribute(attributeDescription); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public AttributeParser parseAttribute(String attributeDescription) { |
| | | return entry.parseAttribute(attributeDescription); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override |
| | | public boolean equals(final Object object) { |
| | | return entry.equals(object); |
| | |
| | | * |
| | | * |
| | | * Copyright 2010 Sun Microsystems, Inc. |
| | | * Portions copyright 2012 ForgeRock AS. |
| | | */ |
| | | |
| | | package org.forgerock.opendj.ldap.requests; |
| | |
| | | |
| | | import org.forgerock.opendj.ldap.Attribute; |
| | | import org.forgerock.opendj.ldap.AttributeDescription; |
| | | import org.forgerock.opendj.ldap.AttributeParser; |
| | | import org.forgerock.opendj.ldap.Attributes; |
| | | import org.forgerock.opendj.ldap.ByteString; |
| | | import org.forgerock.opendj.ldap.DN; |
| | |
| | | return impl.getName(); |
| | | } |
| | | |
| | | public AttributeParser parseAttribute(AttributeDescription attributeDescription) { |
| | | return impl.parseAttribute(attributeDescription); |
| | | } |
| | | |
| | | public AttributeParser parseAttribute(String attributeDescription) { |
| | | return impl.parseAttribute(attributeDescription); |
| | | } |
| | | |
| | | public boolean removeAttribute(Attribute attribute, Collection<ByteString> missingValues) { |
| | | throw new UnsupportedOperationException(); |
| | | } |
| | |
| | | |
| | | import org.forgerock.opendj.ldap.Attribute; |
| | | import org.forgerock.opendj.ldap.AttributeDescription; |
| | | import org.forgerock.opendj.ldap.AttributeParser; |
| | | import org.forgerock.opendj.ldap.ByteString; |
| | | import org.forgerock.opendj.ldap.DN; |
| | | import org.forgerock.opendj.ldap.Entry; |
| | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public AttributeParser parseAttribute(AttributeDescription attributeDescription) { |
| | | return entry.parseAttribute(attributeDescription); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public AttributeParser parseAttribute(String attributeDescription) { |
| | | return entry.parseAttribute(attributeDescription); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | public boolean removeAttribute(final Attribute attribute, |
| | | final Collection<ByteString> missingValues) { |
| | | return entry.removeAttribute(attribute, missingValues); |
| | |
| | | |
| | | import org.forgerock.opendj.ldap.Attribute; |
| | | import org.forgerock.opendj.ldap.AttributeDescription; |
| | | import org.forgerock.opendj.ldap.AttributeParser; |
| | | import org.forgerock.opendj.ldap.Attributes; |
| | | import org.forgerock.opendj.ldap.ByteString; |
| | | import org.forgerock.opendj.ldap.DN; |
| | |
| | | return impl.getName(); |
| | | } |
| | | |
| | | public AttributeParser parseAttribute(AttributeDescription attributeDescription) { |
| | | return impl.parseAttribute(attributeDescription); |
| | | } |
| | | |
| | | public AttributeParser parseAttribute(String attributeDescription) { |
| | | return impl.parseAttribute(attributeDescription); |
| | | } |
| | | |
| | | public boolean removeAttribute(Attribute attribute, Collection<ByteString> missingValues) { |
| | | throw new UnsupportedOperationException(); |
| | | } |
| | |
| | | * |
| | | * |
| | | * Copyright 2009 Sun Microsystems, Inc. |
| | | * Portions copyright 2012 ForgeRock AS. |
| | | */ |
| | | package org.forgerock.opendj.ldap.schema; |
| | | |
| | |
| | | import org.forgerock.opendj.ldap.ByteString; |
| | | import org.forgerock.opendj.ldap.DecodeException; |
| | | |
| | | import com.forgerock.opendj.util.GeneralizedTime; |
| | | |
| | | /** |
| | | * This class defines the generalizedTimeMatch matching rule defined in X.520 |
| | | * and referenced in RFC 2252. |
| | |
| | | final class GeneralizedTimeEqualityMatchingRuleImpl extends AbstractMatchingRuleImpl { |
| | | public ByteString normalizeAttributeValue(final Schema schema, final ByteSequence value) |
| | | throws DecodeException { |
| | | return ByteString.valueOf(GeneralizedTimeSyntaxImpl.decodeGeneralizedTimeValue(value)); |
| | | return ByteString.valueOf(GeneralizedTime.decode(value).getTimeInMillis()); |
| | | } |
| | | } |
| | |
| | | * |
| | | * |
| | | * Copyright 2009 Sun Microsystems, Inc. |
| | | * Portions copyright 2012 ForgeRock AS. |
| | | */ |
| | | package org.forgerock.opendj.ldap.schema; |
| | | |
| | |
| | | import org.forgerock.opendj.ldap.ByteString; |
| | | import org.forgerock.opendj.ldap.DecodeException; |
| | | |
| | | import com.forgerock.opendj.util.GeneralizedTime; |
| | | |
| | | /** |
| | | * This class defines the generalizedTimeOrderingMatch matching rule defined in |
| | | * X.520 and referenced in RFC 2252. |
| | |
| | | final class GeneralizedTimeOrderingMatchingRuleImpl extends AbstractOrderingMatchingRuleImpl { |
| | | public ByteString normalizeAttributeValue(final Schema schema, final ByteSequence value) |
| | | throws DecodeException { |
| | | return ByteString.valueOf(GeneralizedTimeSyntaxImpl.decodeGeneralizedTimeValue(value)); |
| | | return ByteString.valueOf(GeneralizedTime.decode(value).getTimeInMillis()); |
| | | } |
| | | } |
| | |
| | | |
| | | package org.forgerock.opendj.ldap.schema; |
| | | |
| | | import static org.forgerock.opendj.ldap.CoreMessages.*; |
| | | import static org.forgerock.opendj.ldap.schema.SchemaConstants.*; |
| | | import static org.forgerock.opendj.ldap.schema.SchemaConstants.EMR_GENERALIZED_TIME_OID; |
| | | import static org.forgerock.opendj.ldap.schema.SchemaConstants.OMR_GENERALIZED_TIME_OID; |
| | | import static org.forgerock.opendj.ldap.schema.SchemaConstants.SMR_CASE_IGNORE_OID; |
| | | import static org.forgerock.opendj.ldap.schema.SchemaConstants.SYNTAX_GENERALIZED_TIME_NAME; |
| | | |
| | | import java.util.Calendar; |
| | | import java.util.GregorianCalendar; |
| | | import java.util.TimeZone; |
| | | |
| | | import org.forgerock.i18n.LocalizableMessage; |
| | | import org.forgerock.i18n.LocalizableMessageBuilder; |
| | | import org.forgerock.opendj.ldap.ByteSequence; |
| | | import org.forgerock.opendj.ldap.DecodeException; |
| | | |
| | | import com.forgerock.opendj.util.StaticUtils; |
| | | import com.forgerock.opendj.util.GeneralizedTime; |
| | | |
| | | /** |
| | | * This class implements the fax attribute syntax. This should be restricted to |
| | |
| | | */ |
| | | final class GeneralizedTimeSyntaxImpl extends AbstractSyntaxImpl { |
| | | |
| | | // UTC TimeZone is assumed to never change over JVM lifetime |
| | | private static final TimeZone TIME_ZONE_UTC_OBJ = TimeZone.getTimeZone(TIME_ZONE_UTC); |
| | | |
| | | /** |
| | | * Decodes the provided normalized value as a generalized time value and |
| | | * retrieves a timestamp containing its representation. |
| | | * |
| | | * @param value |
| | | * The normalized value to decode using the generalized time |
| | | * syntax. |
| | | * @return The timestamp created from the provided generalized time value. |
| | | * @throws DecodeException |
| | | * If the provided value cannot be parsed as a valid generalized |
| | | * time string. |
| | | */ |
| | | static long decodeGeneralizedTimeValue(final ByteSequence value) throws DecodeException { |
| | | int year = 0; |
| | | int month = 0; |
| | | int day = 0; |
| | | int hour = 0; |
| | | int minute = 0; |
| | | int second = 0; |
| | | |
| | | // Get the value as a string and verify that it is at least long |
| | | // enough for "YYYYMMDDhhZ", which is the shortest allowed value. |
| | | final String valueString = value.toString().toUpperCase(); |
| | | final int length = valueString.length(); |
| | | if (length < 11) { |
| | | final LocalizableMessage message = |
| | | WARN_ATTR_SYNTAX_GENERALIZED_TIME_TOO_SHORT.get(valueString); |
| | | final DecodeException e = DecodeException.error(message); |
| | | StaticUtils.DEBUG_LOG.throwing("GeneralizedTimeSyntax", "valueIsAcceptable", e); |
| | | throw e; |
| | | } |
| | | |
| | | // The first four characters are the century and year, and they must |
| | | // be numeric digits between 0 and 9. |
| | | for (int i = 0; i < 4; i++) { |
| | | switch (valueString.charAt(i)) { |
| | | case '0': |
| | | year = year * 10; |
| | | break; |
| | | |
| | | case '1': |
| | | year = year * 10 + 1; |
| | | break; |
| | | |
| | | case '2': |
| | | year = year * 10 + 2; |
| | | break; |
| | | |
| | | case '3': |
| | | year = year * 10 + 3; |
| | | break; |
| | | |
| | | case '4': |
| | | year = year * 10 + 4; |
| | | break; |
| | | |
| | | case '5': |
| | | year = year * 10 + 5; |
| | | break; |
| | | |
| | | case '6': |
| | | year = year * 10 + 6; |
| | | break; |
| | | |
| | | case '7': |
| | | year = year * 10 + 7; |
| | | break; |
| | | |
| | | case '8': |
| | | year = year * 10 + 8; |
| | | break; |
| | | |
| | | case '9': |
| | | year = year * 10 + 9; |
| | | break; |
| | | |
| | | default: |
| | | final LocalizableMessage message = |
| | | WARN_ATTR_SYNTAX_GENERALIZED_TIME_INVALID_YEAR.get(valueString, String |
| | | .valueOf(valueString.charAt(i))); |
| | | final DecodeException e = DecodeException.error(message); |
| | | StaticUtils.DEBUG_LOG.throwing("GeneralizedTimeSyntax", "valueIsAcceptable", e); |
| | | throw e; |
| | | } |
| | | } |
| | | |
| | | // The next two characters are the month, and they must form the |
| | | // string representation of an integer between 01 and 12. |
| | | char m1 = valueString.charAt(4); |
| | | final char m2 = valueString.charAt(5); |
| | | switch (m1) { |
| | | case '0': |
| | | // m2 must be a digit between 1 and 9. |
| | | switch (m2) { |
| | | case '1': |
| | | month = Calendar.JANUARY; |
| | | break; |
| | | |
| | | case '2': |
| | | month = Calendar.FEBRUARY; |
| | | break; |
| | | |
| | | case '3': |
| | | month = Calendar.MARCH; |
| | | break; |
| | | |
| | | case '4': |
| | | month = Calendar.APRIL; |
| | | break; |
| | | |
| | | case '5': |
| | | month = Calendar.MAY; |
| | | break; |
| | | |
| | | case '6': |
| | | month = Calendar.JUNE; |
| | | break; |
| | | |
| | | case '7': |
| | | month = Calendar.JULY; |
| | | break; |
| | | |
| | | case '8': |
| | | month = Calendar.AUGUST; |
| | | break; |
| | | |
| | | case '9': |
| | | month = Calendar.SEPTEMBER; |
| | | break; |
| | | |
| | | default: |
| | | final LocalizableMessage message = |
| | | WARN_ATTR_SYNTAX_GENERALIZED_TIME_INVALID_MONTH.get(valueString, |
| | | valueString.substring(4, 6)); |
| | | final DecodeException e = DecodeException.error(message); |
| | | StaticUtils.DEBUG_LOG.throwing("GeneralizedTimeSyntax", "valueIsAcceptable", e); |
| | | throw e; |
| | | } |
| | | break; |
| | | case '1': |
| | | // m2 must be a digit between 0 and 2. |
| | | switch (m2) { |
| | | case '0': |
| | | month = Calendar.OCTOBER; |
| | | break; |
| | | |
| | | case '1': |
| | | month = Calendar.NOVEMBER; |
| | | break; |
| | | |
| | | case '2': |
| | | month = Calendar.DECEMBER; |
| | | break; |
| | | |
| | | default: |
| | | final LocalizableMessage message = |
| | | WARN_ATTR_SYNTAX_GENERALIZED_TIME_INVALID_MONTH.get(valueString, |
| | | valueString.substring(4, 6)); |
| | | final DecodeException e = DecodeException.error(message); |
| | | StaticUtils.DEBUG_LOG.throwing("GeneralizedTimeSyntax", "valueIsAcceptable", e); |
| | | throw e; |
| | | } |
| | | break; |
| | | default: |
| | | final LocalizableMessage message = |
| | | WARN_ATTR_SYNTAX_GENERALIZED_TIME_INVALID_MONTH.get(valueString, valueString |
| | | .substring(4, 6)); |
| | | final DecodeException e = DecodeException.error(message); |
| | | StaticUtils.DEBUG_LOG.throwing("GeneralizedTimeSyntax", "valueIsAcceptable", e); |
| | | throw e; |
| | | } |
| | | |
| | | // The next two characters should be the day of the month, and they |
| | | // must form the string representation of an integer between 01 and |
| | | // 31. This doesn't do any validation against the year or month, so |
| | | // it will allow dates like April 31, or February 29 in a non-leap |
| | | // year, but we'll let those slide. |
| | | final char d1 = valueString.charAt(6); |
| | | final char d2 = valueString.charAt(7); |
| | | switch (d1) { |
| | | case '0': |
| | | // d2 must be a digit between 1 and 9. |
| | | switch (d2) { |
| | | case '1': |
| | | day = 1; |
| | | break; |
| | | |
| | | case '2': |
| | | day = 2; |
| | | break; |
| | | |
| | | case '3': |
| | | day = 3; |
| | | break; |
| | | |
| | | case '4': |
| | | day = 4; |
| | | break; |
| | | |
| | | case '5': |
| | | day = 5; |
| | | break; |
| | | |
| | | case '6': |
| | | day = 6; |
| | | break; |
| | | |
| | | case '7': |
| | | day = 7; |
| | | break; |
| | | |
| | | case '8': |
| | | day = 8; |
| | | break; |
| | | |
| | | case '9': |
| | | day = 9; |
| | | break; |
| | | |
| | | default: |
| | | final LocalizableMessage message = |
| | | WARN_ATTR_SYNTAX_GENERALIZED_TIME_INVALID_DAY.get(valueString, valueString |
| | | .substring(6, 8)); |
| | | final DecodeException e = DecodeException.error(message); |
| | | StaticUtils.DEBUG_LOG.throwing("GeneralizedTimeSyntax", "valueIsAcceptable", e); |
| | | throw e; |
| | | } |
| | | break; |
| | | |
| | | case '1': |
| | | // d2 must be a digit between 0 and 9. |
| | | switch (d2) { |
| | | case '0': |
| | | day = 10; |
| | | break; |
| | | |
| | | case '1': |
| | | day = 11; |
| | | break; |
| | | |
| | | case '2': |
| | | day = 12; |
| | | break; |
| | | |
| | | case '3': |
| | | day = 13; |
| | | break; |
| | | |
| | | case '4': |
| | | day = 14; |
| | | break; |
| | | |
| | | case '5': |
| | | day = 15; |
| | | break; |
| | | |
| | | case '6': |
| | | day = 16; |
| | | break; |
| | | |
| | | case '7': |
| | | day = 17; |
| | | break; |
| | | |
| | | case '8': |
| | | day = 18; |
| | | break; |
| | | |
| | | case '9': |
| | | day = 19; |
| | | break; |
| | | |
| | | default: |
| | | final LocalizableMessage message = |
| | | WARN_ATTR_SYNTAX_GENERALIZED_TIME_INVALID_DAY.get(valueString, valueString |
| | | .substring(6, 8)); |
| | | final DecodeException e = DecodeException.error(message); |
| | | StaticUtils.DEBUG_LOG.throwing("GeneralizedTimeSyntax", "valueIsAcceptable", e); |
| | | throw e; |
| | | } |
| | | break; |
| | | |
| | | case '2': |
| | | // d2 must be a digit between 0 and 9. |
| | | switch (d2) { |
| | | case '0': |
| | | day = 20; |
| | | break; |
| | | |
| | | case '1': |
| | | day = 21; |
| | | break; |
| | | |
| | | case '2': |
| | | day = 22; |
| | | break; |
| | | |
| | | case '3': |
| | | day = 23; |
| | | break; |
| | | |
| | | case '4': |
| | | day = 24; |
| | | break; |
| | | |
| | | case '5': |
| | | day = 25; |
| | | break; |
| | | |
| | | case '6': |
| | | day = 26; |
| | | break; |
| | | |
| | | case '7': |
| | | day = 27; |
| | | break; |
| | | |
| | | case '8': |
| | | day = 28; |
| | | break; |
| | | |
| | | case '9': |
| | | day = 29; |
| | | break; |
| | | |
| | | default: |
| | | final LocalizableMessage message = |
| | | WARN_ATTR_SYNTAX_GENERALIZED_TIME_INVALID_DAY.get(valueString, valueString |
| | | .substring(6, 8)); |
| | | final DecodeException e = DecodeException.error(message); |
| | | StaticUtils.DEBUG_LOG.throwing("GeneralizedTimeSyntax", "valueIsAcceptable", e); |
| | | throw e; |
| | | } |
| | | break; |
| | | |
| | | case '3': |
| | | // d2 must be either 0 or 1. |
| | | switch (d2) { |
| | | case '0': |
| | | day = 30; |
| | | break; |
| | | |
| | | case '1': |
| | | day = 31; |
| | | break; |
| | | |
| | | default: |
| | | final LocalizableMessage message = |
| | | WARN_ATTR_SYNTAX_GENERALIZED_TIME_INVALID_DAY.get(valueString, valueString |
| | | .substring(6, 8)); |
| | | final DecodeException e = DecodeException.error(message); |
| | | StaticUtils.DEBUG_LOG.throwing("GeneralizedTimeSyntax", "valueIsAcceptable", e); |
| | | throw e; |
| | | } |
| | | break; |
| | | |
| | | default: |
| | | final LocalizableMessage message = |
| | | WARN_ATTR_SYNTAX_GENERALIZED_TIME_INVALID_DAY.get(valueString, valueString |
| | | .substring(6, 8)); |
| | | final DecodeException e = DecodeException.error(message); |
| | | StaticUtils.DEBUG_LOG.throwing("GeneralizedTimeSyntax", "valueIsAcceptable", e); |
| | | throw e; |
| | | } |
| | | |
| | | // The next two characters must be the hour, and they must form the |
| | | // string representation of an integer between 00 and 23. |
| | | final char h1 = valueString.charAt(8); |
| | | final char h2 = valueString.charAt(9); |
| | | switch (h1) { |
| | | case '0': |
| | | switch (h2) { |
| | | case '0': |
| | | hour = 0; |
| | | break; |
| | | |
| | | case '1': |
| | | hour = 1; |
| | | break; |
| | | |
| | | case '2': |
| | | hour = 2; |
| | | break; |
| | | |
| | | case '3': |
| | | hour = 3; |
| | | break; |
| | | |
| | | case '4': |
| | | hour = 4; |
| | | break; |
| | | |
| | | case '5': |
| | | hour = 5; |
| | | break; |
| | | |
| | | case '6': |
| | | hour = 6; |
| | | break; |
| | | |
| | | case '7': |
| | | hour = 7; |
| | | break; |
| | | |
| | | case '8': |
| | | hour = 8; |
| | | break; |
| | | |
| | | case '9': |
| | | hour = 9; |
| | | break; |
| | | |
| | | default: |
| | | final LocalizableMessage message = |
| | | WARN_ATTR_SYNTAX_GENERALIZED_TIME_INVALID_HOUR.get(valueString, valueString |
| | | .substring(8, 10)); |
| | | final DecodeException e = DecodeException.error(message); |
| | | StaticUtils.DEBUG_LOG.throwing("GeneralizedTimeSyntax", "valueIsAcceptable", e); |
| | | throw e; |
| | | } |
| | | break; |
| | | |
| | | case '1': |
| | | switch (h2) { |
| | | case '0': |
| | | hour = 10; |
| | | break; |
| | | |
| | | case '1': |
| | | hour = 11; |
| | | break; |
| | | |
| | | case '2': |
| | | hour = 12; |
| | | break; |
| | | |
| | | case '3': |
| | | hour = 13; |
| | | break; |
| | | |
| | | case '4': |
| | | hour = 14; |
| | | break; |
| | | |
| | | case '5': |
| | | hour = 15; |
| | | break; |
| | | |
| | | case '6': |
| | | hour = 16; |
| | | break; |
| | | |
| | | case '7': |
| | | hour = 17; |
| | | break; |
| | | |
| | | case '8': |
| | | hour = 18; |
| | | break; |
| | | |
| | | case '9': |
| | | hour = 19; |
| | | break; |
| | | |
| | | default: |
| | | final LocalizableMessage message = |
| | | WARN_ATTR_SYNTAX_GENERALIZED_TIME_INVALID_HOUR.get(valueString, valueString |
| | | .substring(8, 10)); |
| | | final DecodeException e = DecodeException.error(message); |
| | | StaticUtils.DEBUG_LOG.throwing("GeneralizedTimeSyntax", "valueIsAcceptable", e); |
| | | throw e; |
| | | } |
| | | break; |
| | | |
| | | case '2': |
| | | switch (h2) { |
| | | case '0': |
| | | hour = 20; |
| | | break; |
| | | |
| | | case '1': |
| | | hour = 21; |
| | | break; |
| | | |
| | | case '2': |
| | | hour = 22; |
| | | break; |
| | | |
| | | case '3': |
| | | hour = 23; |
| | | break; |
| | | |
| | | default: |
| | | final LocalizableMessage message = |
| | | WARN_ATTR_SYNTAX_GENERALIZED_TIME_INVALID_HOUR.get(valueString, valueString |
| | | .substring(8, 10)); |
| | | final DecodeException e = DecodeException.error(message); |
| | | StaticUtils.DEBUG_LOG.throwing("GeneralizedTimeSyntax", "valueIsAcceptable", e); |
| | | throw e; |
| | | } |
| | | break; |
| | | |
| | | default: |
| | | final LocalizableMessage message = |
| | | WARN_ATTR_SYNTAX_GENERALIZED_TIME_INVALID_HOUR.get(valueString, valueString |
| | | .substring(8, 10)); |
| | | final DecodeException e = DecodeException.error(message); |
| | | StaticUtils.DEBUG_LOG.throwing("GeneralizedTimeSyntax", "valueIsAcceptable", e); |
| | | throw e; |
| | | } |
| | | |
| | | // Next, there should be either two digits comprising an integer |
| | | // between 00 and 59 (for the minute), a letter 'Z' (for the UTC |
| | | // specifier), a plus or minus sign followed by two or four digits |
| | | // (for the UTC offset), or a period or comma representing the |
| | | // fraction. |
| | | m1 = valueString.charAt(10); |
| | | switch (m1) { |
| | | case '0': |
| | | case '1': |
| | | case '2': |
| | | case '3': |
| | | case '4': |
| | | case '5': |
| | | // There must be at least two more characters, and the next one |
| | | // must be a digit between 0 and 9. |
| | | if (length < 13) { |
| | | final LocalizableMessage message = |
| | | WARN_ATTR_SYNTAX_GENERALIZED_TIME_INVALID_CHAR.get(valueString, String |
| | | .valueOf(m1), 10); |
| | | final DecodeException e = DecodeException.error(message); |
| | | StaticUtils.DEBUG_LOG.throwing("GeneralizedTimeSyntax", "valueIsAcceptable", e); |
| | | throw e; |
| | | } |
| | | |
| | | minute = 10 * (m1 - '0'); |
| | | |
| | | switch (valueString.charAt(11)) { |
| | | case '0': |
| | | break; |
| | | |
| | | case '1': |
| | | minute += 1; |
| | | break; |
| | | |
| | | case '2': |
| | | minute += 2; |
| | | break; |
| | | |
| | | case '3': |
| | | minute += 3; |
| | | break; |
| | | |
| | | case '4': |
| | | minute += 4; |
| | | break; |
| | | |
| | | case '5': |
| | | minute += 5; |
| | | break; |
| | | |
| | | case '6': |
| | | minute += 6; |
| | | break; |
| | | |
| | | case '7': |
| | | minute += 7; |
| | | break; |
| | | |
| | | case '8': |
| | | minute += 8; |
| | | break; |
| | | |
| | | case '9': |
| | | minute += 9; |
| | | break; |
| | | |
| | | default: |
| | | final LocalizableMessage message = |
| | | WARN_ATTR_SYNTAX_GENERALIZED_TIME_INVALID_MINUTE.get(valueString, |
| | | valueString.substring(10, 12)); |
| | | final DecodeException e = DecodeException.error(message); |
| | | StaticUtils.DEBUG_LOG.throwing("GeneralizedTimeSyntax", "valueIsAcceptable", e); |
| | | throw e; |
| | | } |
| | | |
| | | break; |
| | | |
| | | case 'Z': |
| | | // This is fine only if we are at the end of the value. |
| | | if (length == 11) { |
| | | final TimeZone tz = TIME_ZONE_UTC_OBJ; |
| | | return createTime(valueString, year, month, day, hour, minute, second, tz); |
| | | } else { |
| | | final LocalizableMessage message = |
| | | WARN_ATTR_SYNTAX_GENERALIZED_TIME_INVALID_CHAR.get(valueString, String |
| | | .valueOf(m1), 10); |
| | | final DecodeException e = DecodeException.error(message); |
| | | StaticUtils.DEBUG_LOG.throwing("GeneralizedTimeSyntax", "valueIsAcceptable", e); |
| | | throw e; |
| | | } |
| | | |
| | | case '+': |
| | | case '-': |
| | | // These are fine only if there are exactly two or four more |
| | | // digits that specify a valid offset. |
| | | if (length == 13 || length == 15) { |
| | | final TimeZone tz = getTimeZoneForOffset(valueString, 10); |
| | | return createTime(valueString, year, month, day, hour, minute, second, tz); |
| | | } else { |
| | | final LocalizableMessage message = |
| | | WARN_ATTR_SYNTAX_GENERALIZED_TIME_INVALID_CHAR.get(valueString, String |
| | | .valueOf(m1), 10); |
| | | final DecodeException e = DecodeException.error(message); |
| | | StaticUtils.DEBUG_LOG.throwing("GeneralizedTimeSyntax", "valueIsAcceptable", e); |
| | | throw e; |
| | | } |
| | | |
| | | case '.': |
| | | case ',': |
| | | return finishDecodingFraction(valueString, 11, year, month, day, hour, minute, second, |
| | | 3600000); |
| | | |
| | | default: |
| | | final LocalizableMessage message = |
| | | WARN_ATTR_SYNTAX_GENERALIZED_TIME_INVALID_CHAR.get(valueString, String |
| | | .valueOf(m1), 10); |
| | | final DecodeException e = DecodeException.error(message); |
| | | StaticUtils.DEBUG_LOG.throwing("GeneralizedTimeSyntax", "valueIsAcceptable", e); |
| | | throw e; |
| | | } |
| | | |
| | | // Next, there should be either two digits comprising an integer |
| | | // between 00 and 60 (for the second, including a possible leap |
| | | // second), a letter 'Z' (for the UTC specifier), a plus or minus |
| | | // sign followed by two or four digits (for the UTC offset), or a |
| | | // period or comma to start the fraction. |
| | | final char s1 = valueString.charAt(12); |
| | | switch (s1) { |
| | | case '0': |
| | | case '1': |
| | | case '2': |
| | | case '3': |
| | | case '4': |
| | | case '5': |
| | | // There must be at least two more characters, and the next one |
| | | // must be a digit between 0 and 9. |
| | | if (length < 15) { |
| | | final LocalizableMessage message = |
| | | WARN_ATTR_SYNTAX_GENERALIZED_TIME_INVALID_CHAR.get(valueString, String |
| | | .valueOf(s1), 12); |
| | | final DecodeException e = DecodeException.error(message); |
| | | StaticUtils.DEBUG_LOG.throwing("GeneralizedTimeSyntax", "valueIsAcceptable", e); |
| | | throw e; |
| | | } |
| | | |
| | | second = 10 * (s1 - '0'); |
| | | |
| | | switch (valueString.charAt(13)) { |
| | | case '0': |
| | | break; |
| | | |
| | | case '1': |
| | | second += 1; |
| | | break; |
| | | |
| | | case '2': |
| | | second += 2; |
| | | break; |
| | | |
| | | case '3': |
| | | second += 3; |
| | | break; |
| | | |
| | | case '4': |
| | | second += 4; |
| | | break; |
| | | |
| | | case '5': |
| | | second += 5; |
| | | break; |
| | | |
| | | case '6': |
| | | second += 6; |
| | | break; |
| | | |
| | | case '7': |
| | | second += 7; |
| | | break; |
| | | |
| | | case '8': |
| | | second += 8; |
| | | break; |
| | | |
| | | case '9': |
| | | second += 9; |
| | | break; |
| | | |
| | | default: |
| | | final LocalizableMessage message = |
| | | WARN_ATTR_SYNTAX_GENERALIZED_TIME_INVALID_MINUTE.get(valueString, |
| | | valueString.substring(12, 14)); |
| | | final DecodeException e = DecodeException.error(message); |
| | | StaticUtils.DEBUG_LOG.throwing("GeneralizedTimeSyntax", "valueIsAcceptable", e); |
| | | throw e; |
| | | } |
| | | |
| | | break; |
| | | |
| | | case '6': |
| | | // There must be at least two more characters and the next one |
| | | // must be a 0. |
| | | if (length < 15) { |
| | | final LocalizableMessage message = |
| | | WARN_ATTR_SYNTAX_GENERALIZED_TIME_INVALID_CHAR.get(valueString, String |
| | | .valueOf(s1), 12); |
| | | final DecodeException e = DecodeException.error(message); |
| | | StaticUtils.DEBUG_LOG.throwing("GeneralizedTimeSyntax", "valueIsAcceptable", e); |
| | | throw e; |
| | | } |
| | | |
| | | if (valueString.charAt(13) != '0') { |
| | | final LocalizableMessage message = |
| | | WARN_ATTR_SYNTAX_GENERALIZED_TIME_INVALID_SECOND.get(valueString, |
| | | valueString.substring(12, 14)); |
| | | final DecodeException e = DecodeException.error(message); |
| | | StaticUtils.DEBUG_LOG.throwing("GeneralizedTimeSyntax", "valueIsAcceptable", e); |
| | | throw e; |
| | | } |
| | | |
| | | second = 60; |
| | | break; |
| | | |
| | | case 'Z': |
| | | // This is fine only if we are at the end of the value. |
| | | if (length == 13) { |
| | | final TimeZone tz = TIME_ZONE_UTC_OBJ; |
| | | return createTime(valueString, year, month, day, hour, minute, second, tz); |
| | | } else { |
| | | final LocalizableMessage message = |
| | | WARN_ATTR_SYNTAX_GENERALIZED_TIME_INVALID_CHAR.get(valueString, String |
| | | .valueOf(s1), 12); |
| | | final DecodeException e = DecodeException.error(message); |
| | | StaticUtils.DEBUG_LOG.throwing("GeneralizedTimeSyntax", "valueIsAcceptable", e); |
| | | throw e; |
| | | } |
| | | |
| | | case '+': |
| | | case '-': |
| | | // These are fine only if there are exactly two or four more |
| | | // digits that specify a valid offset. |
| | | if (length == 15 || length == 17) { |
| | | final TimeZone tz = getTimeZoneForOffset(valueString, 12); |
| | | return createTime(valueString, year, month, day, hour, minute, second, tz); |
| | | } else { |
| | | final LocalizableMessage message = |
| | | WARN_ATTR_SYNTAX_GENERALIZED_TIME_INVALID_CHAR.get(valueString, String |
| | | .valueOf(s1), 12); |
| | | final DecodeException e = DecodeException.error(message); |
| | | StaticUtils.DEBUG_LOG.throwing("GeneralizedTimeSyntax", "valueIsAcceptable", e); |
| | | throw e; |
| | | } |
| | | |
| | | case '.': |
| | | case ',': |
| | | return finishDecodingFraction(valueString, 13, year, month, day, hour, minute, second, |
| | | 60000); |
| | | |
| | | default: |
| | | final LocalizableMessage message = |
| | | WARN_ATTR_SYNTAX_GENERALIZED_TIME_INVALID_CHAR.get(valueString, String |
| | | .valueOf(s1), 12); |
| | | final DecodeException e = DecodeException.error(message); |
| | | StaticUtils.DEBUG_LOG.throwing("GeneralizedTimeSyntax", "valueIsAcceptable", e); |
| | | throw e; |
| | | } |
| | | |
| | | // Next, there should be either a period or comma followed by |
| | | // between one and three digits (to specify the sub-second), a |
| | | // letter 'Z' (for the UTC specifier), or a plus or minus sign |
| | | // followed by two our four digits (for the UTC offset). |
| | | switch (valueString.charAt(14)) { |
| | | case '.': |
| | | case ',': |
| | | return finishDecodingFraction(valueString, 15, year, month, day, hour, minute, second, |
| | | 1000); |
| | | |
| | | case 'Z': |
| | | // This is fine only if we are at the end of the value. |
| | | if (length == 15) { |
| | | final TimeZone tz = TIME_ZONE_UTC_OBJ; |
| | | return createTime(valueString, year, month, day, hour, minute, second, tz); |
| | | } else { |
| | | final LocalizableMessage message = |
| | | WARN_ATTR_SYNTAX_GENERALIZED_TIME_INVALID_CHAR.get(valueString, String |
| | | .valueOf(valueString.charAt(14)), 14); |
| | | final DecodeException e = DecodeException.error(message); |
| | | StaticUtils.DEBUG_LOG.throwing("GeneralizedTimeSyntax", "valueIsAcceptable", e); |
| | | throw e; |
| | | } |
| | | |
| | | case '+': |
| | | case '-': |
| | | // These are fine only if there are exactly two or four more |
| | | // digits that specify a valid offset. |
| | | if (length == 17 || length == 19) { |
| | | final TimeZone tz = getTimeZoneForOffset(valueString, 14); |
| | | return createTime(valueString, year, month, day, hour, minute, second, tz); |
| | | } else { |
| | | final LocalizableMessage message = |
| | | WARN_ATTR_SYNTAX_GENERALIZED_TIME_INVALID_CHAR.get(valueString, String |
| | | .valueOf(valueString.charAt(14)), 14); |
| | | final DecodeException e = DecodeException.error(message); |
| | | StaticUtils.DEBUG_LOG.throwing("GeneralizedTimeSyntax", "valueIsAcceptable", e); |
| | | throw e; |
| | | } |
| | | |
| | | default: |
| | | final LocalizableMessage message = |
| | | WARN_ATTR_SYNTAX_GENERALIZED_TIME_INVALID_CHAR.get(valueString, String |
| | | .valueOf(valueString.charAt(14)), 14); |
| | | final DecodeException e = DecodeException.error(message); |
| | | StaticUtils.DEBUG_LOG.throwing("GeneralizedTimeSyntax", "valueIsAcceptable", e); |
| | | throw e; |
| | | } |
| | | } |
| | | |
| | | private static long createTime(final String value, int year, int month, int day, int hour, |
| | | int minute, int second, final TimeZone tz) throws DecodeException { |
| | | try { |
| | | final GregorianCalendar calendar = new GregorianCalendar(); |
| | | calendar.setLenient(false); |
| | | calendar.setTimeZone(tz); |
| | | calendar.set(year, month, day, hour, minute, second); |
| | | calendar.set(Calendar.MILLISECOND, 0); |
| | | return calendar.getTimeInMillis(); |
| | | } catch (final Exception e) { |
| | | // This should only happen if the provided date wasn't legal |
| | | // (e.g., September 31). |
| | | final LocalizableMessage message = |
| | | WARN_ATTR_SYNTAX_GENERALIZED_TIME_ILLEGAL_TIME.get(value, String |
| | | .valueOf(e)); |
| | | final DecodeException de = DecodeException.error(message, e); |
| | | StaticUtils.DEBUG_LOG |
| | | .throwing("GeneralizedTimeSyntax", "valueIsAcceptable", de); |
| | | throw de; |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Completes decoding the generalized time value containing a fractional |
| | | * component. It will also decode the trailing 'Z' or offset. |
| | | * |
| | | * @param value |
| | | * The whole value, including the fractional component and time |
| | | * zone information. |
| | | * @param startPos |
| | | * The position of the first character after the period in the |
| | | * value string. |
| | | * @param year |
| | | * The year decoded from the provided value. |
| | | * @param month |
| | | * The month decoded from the provided value. |
| | | * @param day |
| | | * The day decoded from the provided value. |
| | | * @param hour |
| | | * The hour decoded from the provided value. |
| | | * @param minute |
| | | * The minute decoded from the provided value. |
| | | * @param second |
| | | * The second decoded from the provided value. |
| | | * @param multiplier |
| | | * The multiplier value that should be used to scale the fraction |
| | | * appropriately. If it's a fraction of an hour, then it should |
| | | * be 3600000 (60*60*1000). If it's a fraction of a minute, then |
| | | * it should be 60000. If it's a fraction of a second, then it |
| | | * should be 1000. |
| | | * @return The timestamp created from the provided generalized time value |
| | | * including the fractional element. |
| | | * @throws DecodeException |
| | | * If the provided value cannot be parsed as a valid generalized |
| | | * time string. |
| | | */ |
| | | private static long finishDecodingFraction(final String value, final int startPos, |
| | | final int year, final int month, final int day, final int hour, final int minute, |
| | | final int second, final int multiplier) throws DecodeException { |
| | | final int length = value.length(); |
| | | final StringBuilder fractionBuffer = new StringBuilder(2 + length - startPos); |
| | | fractionBuffer.append("0."); |
| | | |
| | | TimeZone timeZone = null; |
| | | |
| | | outerLoop: |
| | | for (int i = startPos; i < length; i++) { |
| | | final char c = value.charAt(i); |
| | | switch (c) { |
| | | case '0': |
| | | case '1': |
| | | case '2': |
| | | case '3': |
| | | case '4': |
| | | case '5': |
| | | case '6': |
| | | case '7': |
| | | case '8': |
| | | case '9': |
| | | fractionBuffer.append(c); |
| | | break; |
| | | |
| | | case 'Z': |
| | | // This is only acceptable if we're at the end of the value. |
| | | if (i != value.length() - 1) { |
| | | final LocalizableMessage message = |
| | | WARN_ATTR_SYNTAX_GENERALIZED_TIME_ILLEGAL_FRACTION_CHAR.get(value, |
| | | String.valueOf(c)); |
| | | final DecodeException e = DecodeException.error(message); |
| | | StaticUtils.DEBUG_LOG.throwing("GeneralizedTimeSyntax", |
| | | "finishDecodingFraction", e); |
| | | throw e; |
| | | } |
| | | |
| | | timeZone = TIME_ZONE_UTC_OBJ; |
| | | break outerLoop; |
| | | |
| | | case '+': |
| | | case '-': |
| | | timeZone = getTimeZoneForOffset(value, i); |
| | | break outerLoop; |
| | | |
| | | default: |
| | | final LocalizableMessage message = |
| | | WARN_ATTR_SYNTAX_GENERALIZED_TIME_ILLEGAL_FRACTION_CHAR.get(value, String |
| | | .valueOf(c)); |
| | | final DecodeException e = DecodeException.error(message); |
| | | StaticUtils.DEBUG_LOG |
| | | .throwing("GeneralizedTimeSyntax", "finishDecodingFraction", e); |
| | | throw e; |
| | | } |
| | | } |
| | | |
| | | if (fractionBuffer.length() == 2) { |
| | | final LocalizableMessage message = |
| | | WARN_ATTR_SYNTAX_GENERALIZED_TIME_EMPTY_FRACTION.get(value); |
| | | final DecodeException e = DecodeException.error(message); |
| | | StaticUtils.DEBUG_LOG.throwing("GeneralizedTimeSyntax", "finishDecodingFraction", e); |
| | | throw e; |
| | | } |
| | | |
| | | if (timeZone == null) { |
| | | final LocalizableMessage message = |
| | | WARN_ATTR_SYNTAX_GENERALIZED_TIME_NO_TIME_ZONE_INFO.get(value); |
| | | final DecodeException e = DecodeException.error(message); |
| | | StaticUtils.DEBUG_LOG.throwing("GeneralizedTimeSyntax", "finishDecodingFraction", e); |
| | | throw e; |
| | | } |
| | | |
| | | final Double fractionValue = Double.parseDouble(fractionBuffer.toString()); |
| | | final long additionalMilliseconds = Math.round(fractionValue * multiplier); |
| | | |
| | | try { |
| | | final GregorianCalendar calendar = new GregorianCalendar(); |
| | | calendar.setLenient(false); |
| | | calendar.setTimeZone(timeZone); |
| | | calendar.set(year, month, day, hour, minute, second); |
| | | calendar.set(Calendar.MILLISECOND, 0); |
| | | return calendar.getTimeInMillis() + additionalMilliseconds; |
| | | } catch (final Exception e) { |
| | | |
| | | // This should only happen if the provided date wasn't legal |
| | | // (e.g., September 31). |
| | | final LocalizableMessage message = |
| | | WARN_ATTR_SYNTAX_GENERALIZED_TIME_ILLEGAL_TIME.get(value, String.valueOf(e)); |
| | | final DecodeException de = DecodeException.error(message, e); |
| | | StaticUtils.DEBUG_LOG.throwing("GeneralizedTimeSyntax", "valueIsAcceptable", de); |
| | | throw de; |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Decodes a time zone offset from the provided value. |
| | | * |
| | | * @param value |
| | | * The whole value, including the offset. |
| | | * @param startPos |
| | | * The position of the first character that is contained in the |
| | | * offset. This should be the position of the plus or minus |
| | | * character. |
| | | * @return The {@code TimeZone} object representing the decoded time zone. |
| | | * @throws DecodeException |
| | | * If the provided value does not contain a valid offset. |
| | | */ |
| | | private static TimeZone getTimeZoneForOffset(final String value, final int startPos) |
| | | throws DecodeException { |
| | | final String offSetStr = value.substring(startPos); |
| | | if (offSetStr.length() != 3 && offSetStr.length() != 5) { |
| | | final LocalizableMessage message = |
| | | WARN_ATTR_SYNTAX_GENERALIZED_TIME_INVALID_OFFSET.get(value, offSetStr); |
| | | final DecodeException e = DecodeException.error(message); |
| | | StaticUtils.DEBUG_LOG.throwing("GeneralizedTimeSyntax", "getTimeZoneForOffset", e); |
| | | throw e; |
| | | } |
| | | |
| | | // The first character must be either a plus or minus. |
| | | switch (offSetStr.charAt(0)) { |
| | | case '+': |
| | | case '-': |
| | | // These are OK. |
| | | break; |
| | | |
| | | default: |
| | | final LocalizableMessage message = |
| | | WARN_ATTR_SYNTAX_GENERALIZED_TIME_INVALID_OFFSET.get(value, offSetStr); |
| | | final DecodeException e = DecodeException.error(message); |
| | | StaticUtils.DEBUG_LOG.throwing("GeneralizedTimeSyntax", "getTimeZoneForOffset", e); |
| | | throw e; |
| | | } |
| | | |
| | | // The first two characters must be an integer between 00 and 23. |
| | | switch (offSetStr.charAt(1)) { |
| | | case '0': |
| | | case '1': |
| | | switch (offSetStr.charAt(2)) { |
| | | case '0': |
| | | case '1': |
| | | case '2': |
| | | case '3': |
| | | case '4': |
| | | case '5': |
| | | case '6': |
| | | case '7': |
| | | case '8': |
| | | case '9': |
| | | // These are all fine. |
| | | break; |
| | | |
| | | default: |
| | | final LocalizableMessage message = |
| | | WARN_ATTR_SYNTAX_GENERALIZED_TIME_INVALID_OFFSET.get(value, offSetStr); |
| | | final DecodeException e = DecodeException.error(message); |
| | | StaticUtils.DEBUG_LOG.throwing("GeneralizedTimeSyntax", "getTimeZoneForOffset", e); |
| | | throw e; |
| | | } |
| | | break; |
| | | |
| | | case '2': |
| | | switch (offSetStr.charAt(2)) { |
| | | case '0': |
| | | case '1': |
| | | case '2': |
| | | case '3': |
| | | // These are all fine. |
| | | break; |
| | | |
| | | default: |
| | | final LocalizableMessage message = |
| | | WARN_ATTR_SYNTAX_GENERALIZED_TIME_INVALID_OFFSET.get(value, offSetStr); |
| | | final DecodeException e = DecodeException.error(message); |
| | | StaticUtils.DEBUG_LOG.throwing("GeneralizedTimeSyntax", "getTimeZoneForOffset", e); |
| | | throw e; |
| | | } |
| | | break; |
| | | |
| | | default: |
| | | final LocalizableMessage message = |
| | | WARN_ATTR_SYNTAX_GENERALIZED_TIME_INVALID_OFFSET.get(value, offSetStr); |
| | | final DecodeException e = DecodeException.error(message); |
| | | StaticUtils.DEBUG_LOG.throwing("GeneralizedTimeSyntax", "getTimeZoneForOffset", e); |
| | | throw e; |
| | | } |
| | | |
| | | // If there are two more characters, then they must be an integer |
| | | // between 00 and 59. |
| | | if (offSetStr.length() == 5) { |
| | | switch (offSetStr.charAt(3)) { |
| | | case '0': |
| | | case '1': |
| | | case '2': |
| | | case '3': |
| | | case '4': |
| | | case '5': |
| | | switch (offSetStr.charAt(4)) { |
| | | case '0': |
| | | case '1': |
| | | case '2': |
| | | case '3': |
| | | case '4': |
| | | case '5': |
| | | case '6': |
| | | case '7': |
| | | case '8': |
| | | case '9': |
| | | // These are all fine. |
| | | break; |
| | | |
| | | default: |
| | | final LocalizableMessage message = |
| | | WARN_ATTR_SYNTAX_GENERALIZED_TIME_INVALID_OFFSET.get(value, offSetStr); |
| | | final DecodeException e = DecodeException.error(message); |
| | | StaticUtils.DEBUG_LOG.throwing("GeneralizedTimeSyntax", "getTimeZoneForOffset", |
| | | e); |
| | | throw e; |
| | | } |
| | | break; |
| | | |
| | | default: |
| | | final LocalizableMessage message = |
| | | WARN_ATTR_SYNTAX_GENERALIZED_TIME_INVALID_OFFSET.get(value, offSetStr); |
| | | final DecodeException e = DecodeException.error(message); |
| | | StaticUtils.DEBUG_LOG.throwing("GeneralizedTimeSyntax", "getTimeZoneForOffset", e); |
| | | throw e; |
| | | } |
| | | } |
| | | |
| | | // If we've gotten here, then it looks like a valid offset. We can |
| | | // create a time zone by using "GMT" followed by the offset. |
| | | return TimeZone.getTimeZone("GMT" + offSetStr); |
| | | } |
| | | |
| | | @Override |
| | | public String getEqualityMatchingRule() { |
| | | return EMR_GENERALIZED_TIME_OID; |
| | |
| | | public boolean valueIsAcceptable(final Schema schema, final ByteSequence value, |
| | | final LocalizableMessageBuilder invalidReason) { |
| | | try { |
| | | decodeGeneralizedTimeValue(value); |
| | | GeneralizedTime.decode(value); |
| | | return true; |
| | | } catch (final DecodeException de) { |
| | | invalidReason.append(de.getMessageObject()); |