Re-implement the way that the server handles the generalized time syntax to
make it more standards compliant and fix problems reported with its behavior.
OpenDS Issue Numbers: 675, 1520, 1521, 1522
| | |
| | | |
| | | |
| | | /** |
| | | * The message ID for the message that will be used if a generalized time |
| | | * value contains an illegal character in the fraction component. This takes |
| | | * two arguments, which are the value string and the illegal character. |
| | | */ |
| | | public static final int |
| | | MSGID_ATTR_SYNTAX_GENERALIZED_TIME_ILLEGAL_FRACTION_CHAR = |
| | | CATEGORY_MASK_SCHEMA | SEVERITY_MASK_SEVERE_WARNING | 275; |
| | | |
| | | |
| | | |
| | | /** |
| | | * The message ID for the message that will be used if a generalized time |
| | | * value contains an empty fractional component. This takes a single |
| | | * argument, which is the value string. |
| | | */ |
| | | public static final int MSGID_ATTR_SYNTAX_GENERALIZED_TIME_EMPTY_FRACTION = |
| | | CATEGORY_MASK_SCHEMA | SEVERITY_MASK_SEVERE_WARNING | 276; |
| | | |
| | | |
| | | |
| | | /** |
| | | * The message ID for the message that will be used if a generalized time |
| | | * value does not contain any time zone information. This takes a single |
| | | * argument, which is the value string. |
| | | */ |
| | | public static final int MSGID_ATTR_SYNTAX_GENERALIZED_TIME_NO_TIME_ZONE_INFO = |
| | | CATEGORY_MASK_SCHEMA | SEVERITY_MASK_SEVERE_WARNING | 277; |
| | | |
| | | |
| | | |
| | | /** |
| | | * The message ID for the message that will be used if a generalized time |
| | | * value represents an invalid date or time (e.g., September 31). This takes |
| | | * two arguments, which are the value string and a string representation of |
| | | * the exception that was caught. |
| | | */ |
| | | public static final int MSGID_ATTR_SYNTAX_GENERALIZED_TIME_ILLEGAL_TIME = |
| | | CATEGORY_MASK_SCHEMA | SEVERITY_MASK_SEVERE_WARNING | 278; |
| | | |
| | | |
| | | |
| | | /** |
| | | * Associates a set of generic messages with the message IDs defined in this |
| | | * class. |
| | | */ |
| | |
| | | registerMessage(MSGID_ATTR_SYNTAX_GENERALIZED_TIME_NORMALIZE_FAILURE, |
| | | "An unexpected error occurred while trying to normalize " + |
| | | "value %s as a generalized time value: %s."); |
| | | registerMessage(MSGID_ATTR_SYNTAX_GENERALIZED_TIME_ILLEGAL_FRACTION_CHAR, |
| | | "The provided value %s is not a valid generalized time " + |
| | | "value because it contains illegal character %s in the " + |
| | | "fraction component."); |
| | | registerMessage(MSGID_ATTR_SYNTAX_GENERALIZED_TIME_EMPTY_FRACTION, |
| | | "The provided value %s is not a valid generalized time " + |
| | | "value because it does not contain at least one digit " + |
| | | "after the period to use as the fractional component."); |
| | | registerMessage(MSGID_ATTR_SYNTAX_GENERALIZED_TIME_NO_TIME_ZONE_INFO, |
| | | "The provided value %s is not a valid generalized time " + |
| | | "value because it does not end with 'Z' or a time zone " + |
| | | "offset."); |
| | | registerMessage(MSGID_ATTR_SYNTAX_GENERALIZED_TIME_ILLEGAL_TIME, |
| | | "The provided value %s is not a valid generalized time " + |
| | | "value because it represents an invalid time (e.g., a " + |
| | | "date that does not exist): %s."); |
| | | |
| | | |
| | | registerMessage(MSGID_ATTR_SYNTAX_DN_INVALID, |
| | |
| | | |
| | | |
| | | import java.text.SimpleDateFormat; |
| | | import java.util.Arrays; |
| | | import java.util.Calendar; |
| | | import java.util.GregorianCalendar; |
| | | import java.util.TimeZone; |
| | | import java.util.concurrent.locks.ReentrantLock; |
| | | |
| | |
| | | import org.opends.server.config.ConfigException; |
| | | import org.opends.server.core.DirectoryServer; |
| | | import org.opends.server.protocols.asn1.ASN1OctetString; |
| | | import org.opends.server.types.AcceptRejectWarn; |
| | | import org.opends.server.types.ByteString; |
| | | import org.opends.server.types.DirectoryException; |
| | | import org.opends.server.types.ErrorLogCategory; |
| | | import org.opends.server.types.ErrorLogSeverity; |
| | | import org.opends.server.types.InitializationException; |
| | | import org.opends.server.types.ResultCode; |
| | | |
| | | import static org.opends.server.loggers.debug.DebugLogger.debugCaught; |
| | | import static org.opends.server.loggers.debug.DebugLogger.debugEnabled; |
| | |
| | | public class GeneralizedTimeEqualityMatchingRule |
| | | extends EqualityMatchingRule |
| | | { |
| | | |
| | | |
| | | |
| | | /** |
| | | * The lock that will be used to provide threadsafe access to the date |
| | | * formatter. |
| | |
| | | public ByteString normalizeValue(ByteString value) |
| | | throws DirectoryException |
| | | { |
| | | String valueString = value.stringValue().toUpperCase(); |
| | | int length = valueString.length(); |
| | | |
| | | |
| | | //Make sure that it has at least eleven characters and parse the first ten |
| | | // as the year, month, day, and hour. |
| | | if (length < 11) |
| | | try |
| | | { |
| | | int msgID = MSGID_ATTR_SYNTAX_GENERALIZED_TIME_TOO_SHORT; |
| | | String message = getMessage(msgID, valueString); |
| | | long timestamp = GeneralizedTimeSyntax.decodeGeneralizedTimeValue(value); |
| | | return new ASN1OctetString(GeneralizedTimeSyntax.format(timestamp)); |
| | | } |
| | | catch (DirectoryException de) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | debugCaught(DebugLogLevel.ERROR, de); |
| | | } |
| | | |
| | | switch (DirectoryServer.getSyntaxEnforcementPolicy()) |
| | | { |
| | | case REJECT: |
| | | throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, |
| | | message, msgID); |
| | | throw de; |
| | | |
| | | case WARN: |
| | | logError(ErrorLogCategory.SCHEMA, ErrorLogSeverity.SEVERE_WARNING, |
| | | message, msgID); |
| | | return new ASN1OctetString(valueString); |
| | | de.getErrorMessage(), de.getErrorMessageID()); |
| | | return new ASN1OctetString(value.value()); |
| | | |
| | | default: |
| | | return new ASN1OctetString(valueString); |
| | | } |
| | | } |
| | | |
| | | |
| | | // The year, month, day, and hour must always be specified. |
| | | int year; |
| | | int month; |
| | | int day; |
| | | int hour; |
| | | try |
| | | { |
| | | year = Integer.parseInt(valueString.substring(0, 4)); |
| | | month = Integer.parseInt(valueString.substring(4, 6)); |
| | | day = Integer.parseInt(valueString.substring(6, 8)); |
| | | hour = Integer.parseInt(valueString.substring(8, 10)); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | debugCaught(DebugLogLevel.ERROR, e); |
| | | } |
| | | |
| | | int msgID = MSGID_ATTR_SYNTAX_GENERALIZED_TIME_CANNOT_PARSE; |
| | | String message = getMessage(msgID, valueString, |
| | | String.valueOf(e)); |
| | | |
| | | switch (DirectoryServer.getSyntaxEnforcementPolicy()) |
| | | { |
| | | case REJECT: |
| | | throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, |
| | | message, msgID); |
| | | case WARN: |
| | | logError(ErrorLogCategory.SCHEMA, ErrorLogSeverity.SEVERE_WARNING, |
| | | message, msgID); |
| | | return new ASN1OctetString(valueString); |
| | | default: |
| | | return new ASN1OctetString(valueString); |
| | | } |
| | | } |
| | | |
| | | |
| | | // The minute may come next, but if not then it should indicate that we've |
| | | // hit the end of the value. |
| | | int minute; |
| | | if (isDigit(valueString.charAt(10))) |
| | | { |
| | | try |
| | | { |
| | | minute = Integer.parseInt(valueString.substring(10, 12)); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | debugCaught(DebugLogLevel.ERROR, e); |
| | | } |
| | | |
| | | int msgID = MSGID_ATTR_SYNTAX_GENERALIZED_TIME_CANNOT_PARSE; |
| | | String message = getMessage(msgID, valueString, |
| | | String.valueOf(e)); |
| | | |
| | | switch (DirectoryServer.getSyntaxEnforcementPolicy()) |
| | | { |
| | | case REJECT: |
| | | throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, |
| | | message, msgID); |
| | | case WARN: |
| | | logError(ErrorLogCategory.SCHEMA, ErrorLogSeverity.SEVERE_WARNING, |
| | | message, msgID); |
| | | return new ASN1OctetString(valueString); |
| | | default: |
| | | return new ASN1OctetString(valueString); |
| | | } |
| | | } |
| | | } |
| | | else |
| | | { |
| | | return processValueEnd(valueString, 10, year, month, day, hour, 0, 0, 0); |
| | | } |
| | | |
| | | |
| | | // The second should come next, but if not then it should indicate that |
| | | // we've hit the end of the value. |
| | | int second; |
| | | if (length < 13) |
| | | { |
| | | // Technically, this is invalid. If we're enforcing strict syntax |
| | | // adherence, then throw an exception. Otherwise, just assume that it's |
| | | // a time with a second of zero and parse it in the local time zone. |
| | | if (DirectoryServer.getSyntaxEnforcementPolicy() == |
| | | AcceptRejectWarn.REJECT) |
| | | { |
| | | int msgID = MSGID_ATTR_SYNTAX_GENERALIZED_TIME_TOO_SHORT; |
| | | String message = getMessage(msgID, valueString); |
| | | throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, |
| | | message, msgID); |
| | | } |
| | | else |
| | | { |
| | | GregorianCalendar calendar = |
| | | new GregorianCalendar(year, (month-1), day, hour, minute, 0); |
| | | calendar.setTimeZone(utcTimeZone); |
| | | |
| | | dateFormatLock.lock(); |
| | | |
| | | try |
| | | { |
| | | return new ASN1OctetString(dateFormat.format(calendar.getTime())); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | debugCaught(DebugLogLevel.ERROR, e); |
| | | } |
| | | |
| | | int msgID = MSGID_ATTR_SYNTAX_GENERALIZED_TIME_NORMALIZE_FAILURE; |
| | | String message = getMessage(msgID, valueString, |
| | | stackTraceToSingleLineString(e)); |
| | | |
| | | throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, |
| | | message, msgID, e); |
| | | } |
| | | finally |
| | | { |
| | | dateFormatLock.unlock(); |
| | | } |
| | | } |
| | | } |
| | | else |
| | | { |
| | | if (isDigit(valueString.charAt(12))) |
| | | { |
| | | try |
| | | { |
| | | second = Integer.parseInt(valueString.substring(12, 14)); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | debugCaught(DebugLogLevel.ERROR, e); |
| | | } |
| | | |
| | | int msgID = MSGID_ATTR_SYNTAX_GENERALIZED_TIME_CANNOT_PARSE; |
| | | String message = getMessage(msgID, valueString, |
| | | String.valueOf(e)); |
| | | |
| | | switch (DirectoryServer.getSyntaxEnforcementPolicy()) |
| | | { |
| | | case REJECT: |
| | | throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, |
| | | message, msgID); |
| | | case WARN: |
| | | logError(ErrorLogCategory.SCHEMA, ErrorLogSeverity.SEVERE_WARNING, |
| | | message, msgID); |
| | | return new ASN1OctetString(valueString); |
| | | default: |
| | | return new ASN1OctetString(valueString); |
| | | } |
| | | } |
| | | } |
| | | else |
| | | { |
| | | return processValueEnd(valueString, 12, year, month, day, hour, minute, |
| | | 0, 0); |
| | | } |
| | | } |
| | | |
| | | |
| | | // If the next character is a period, then it will start the sub-second |
| | | // portion of the value. Otherwise, it should indicate that we've hit the |
| | | // end of the value. |
| | | if (length < 15) |
| | | { |
| | | // Technically, this is invalid. If we're enforcing strict syntax |
| | | // adherence, then throw an exception. Otherwise, just assume that it's |
| | | // a time with a second of zero and parse it in the local time zone. |
| | | if (DirectoryServer.getSyntaxEnforcementPolicy() == |
| | | AcceptRejectWarn.REJECT) |
| | | { |
| | | int msgID = MSGID_ATTR_SYNTAX_GENERALIZED_TIME_TOO_SHORT; |
| | | String message = getMessage(msgID, valueString); |
| | | throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, |
| | | message, msgID); |
| | | } |
| | | else |
| | | { |
| | | GregorianCalendar calendar = |
| | | new GregorianCalendar(year, (month-1), day, hour, minute, second); |
| | | calendar.setTimeZone(utcTimeZone); |
| | | |
| | | dateFormatLock.lock(); |
| | | |
| | | try |
| | | { |
| | | return new ASN1OctetString(dateFormat.format(calendar.getTime())); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | debugCaught(DebugLogLevel.ERROR, e); |
| | | } |
| | | |
| | | int msgID = MSGID_ATTR_SYNTAX_GENERALIZED_TIME_NORMALIZE_FAILURE; |
| | | String message = getMessage(msgID, valueString, |
| | | stackTraceToSingleLineString(e)); |
| | | |
| | | throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, |
| | | message, msgID, e); |
| | | } |
| | | finally |
| | | { |
| | | dateFormatLock.unlock(); |
| | | } |
| | | } |
| | | } |
| | | else |
| | | { |
| | | if (valueString.charAt(14) == '.') |
| | | { |
| | | // There should be some number of digits following the decimal point to |
| | | // indicate the sub-second value. We'll read all of them now, but may |
| | | // throw some away later. |
| | | char c; |
| | | int pos = 15; |
| | | StringBuilder buffer = new StringBuilder(3); |
| | | for ( ; pos < length; pos++) |
| | | { |
| | | if (isDigit(c = valueString.charAt(pos))) |
| | | { |
| | | buffer.append(c); |
| | | } |
| | | else |
| | | { |
| | | break; |
| | | } |
| | | } |
| | | |
| | | int millisecond; |
| | | switch (buffer.length()) |
| | | { |
| | | case 0: |
| | | millisecond = 0; |
| | | break; |
| | | case 1: |
| | | millisecond = (100 * Integer.parseInt(buffer.toString())); |
| | | break; |
| | | case 2: |
| | | millisecond = (10 * Integer.parseInt(buffer.toString())); |
| | | break; |
| | | case 3: |
| | | millisecond = Integer.parseInt(buffer.toString()); |
| | | break; |
| | | default: |
| | | // We only want three digits for the millisecond, but if the fourth |
| | | // digit is greater than or equal to five, then we may need to round |
| | | // up. |
| | | millisecond = Integer.parseInt(buffer.toString().substring(0, 3)); |
| | | switch (buffer.charAt(3)) |
| | | { |
| | | case '5': |
| | | case '6': |
| | | case '7': |
| | | case '8': |
| | | case '9': |
| | | millisecond++; |
| | | break; |
| | | } |
| | | break; |
| | | } |
| | | |
| | | return processValueEnd(valueString, pos, year, month, day, hour, minute, |
| | | second, millisecond); |
| | | } |
| | | else |
| | | { |
| | | return processValueEnd(valueString, 14, year, month, day, hour, minute, |
| | | second, 0); |
| | | } |
| | | } |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Processes the specified portion of the value as the end of the generalized |
| | | * time specification. If the character at the specified location is a 'Z', |
| | | * then it will be assumed that the value is already in UTC. If it is a '+' |
| | | * or '-', then it will be assumed that the remainder is an offset from UTC. |
| | | * Otherwise, it will be an error. |
| | | * |
| | | * @param valueString The value being parsed as a generalized time string. |
| | | * @param endPos The position at which the end of the value begins. |
| | | * @param year The year parsed from the value. |
| | | * @param month The month parsed from the value. |
| | | * @param day The day parsed from the value. |
| | | * @param hour The hour parsed from the value. |
| | | * @param minute The minute parsed from the value. |
| | | * @param second The second parsed from the value. |
| | | * @param millisecond The millisecond parsed from the value. |
| | | * |
| | | * @return The normalized representation of the generalized time parsed from |
| | | * the value. |
| | | * |
| | | * @throws DirectoryException If a problem occurs while attempting to decode |
| | | * the end of the generalized time value. |
| | | */ |
| | | private ByteString processValueEnd(String valueString, int endPos, int year, |
| | | int month, int day, int hour, int minute, |
| | | int second, int millisecond) |
| | | throws DirectoryException |
| | | { |
| | | // First, check to see if we are at the end of the string. If so, then |
| | | // that could either result in an exception or assuming that we should just |
| | | // use the local time zone. |
| | | int length = valueString.length(); |
| | | if (endPos >= length) |
| | | { |
| | | if (DirectoryServer.getSyntaxEnforcementPolicy() == |
| | | AcceptRejectWarn.REJECT) |
| | | { |
| | | int msgID = MSGID_ATTR_SYNTAX_GENERALIZED_TIME_TOO_SHORT; |
| | | String message = getMessage(msgID, valueString); |
| | | throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, |
| | | message, msgID); |
| | | } |
| | | else |
| | | { |
| | | GregorianCalendar calendar = |
| | | new GregorianCalendar(year, (month-1), day, hour, minute, second); |
| | | calendar.setTimeZone(utcTimeZone); |
| | | calendar.set(Calendar.MILLISECOND, millisecond); |
| | | |
| | | dateFormatLock.lock(); |
| | | |
| | | try |
| | | { |
| | | return new ASN1OctetString(dateFormat.format(calendar.getTime())); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | debugCaught(DebugLogLevel.ERROR, e); |
| | | } |
| | | |
| | | int msgID = MSGID_ATTR_SYNTAX_GENERALIZED_TIME_NORMALIZE_FAILURE; |
| | | String message = getMessage(msgID, valueString, |
| | | stackTraceToSingleLineString(e)); |
| | | |
| | | throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, |
| | | message, msgID, e); |
| | | } |
| | | finally |
| | | { |
| | | dateFormatLock.unlock(); |
| | | } |
| | | } |
| | | } |
| | | |
| | | |
| | | // See what the character is at the specified position. If it is a 'Z', |
| | | // then make sure it's the end of the value and treat it as a UTC date. |
| | | char c = valueString.charAt(endPos); |
| | | if (c == 'Z') |
| | | { |
| | | if (endPos == (length-1)) |
| | | { |
| | | GregorianCalendar calendar = |
| | | new GregorianCalendar(year, (month-1), day, hour, minute, second); |
| | | calendar.setTimeZone(utcTimeZone); |
| | | calendar.set(Calendar.MILLISECOND, millisecond); |
| | | |
| | | dateFormatLock.lock(); |
| | | |
| | | try |
| | | { |
| | | return new ASN1OctetString(dateFormat.format(calendar.getTime())); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | debugCaught(DebugLogLevel.ERROR, e); |
| | | } |
| | | |
| | | int msgID = MSGID_ATTR_SYNTAX_GENERALIZED_TIME_NORMALIZE_FAILURE; |
| | | String message = getMessage(msgID, valueString, |
| | | stackTraceToSingleLineString(e)); |
| | | |
| | | throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, |
| | | message, msgID, e); |
| | | } |
| | | finally |
| | | { |
| | | dateFormatLock.unlock(); |
| | | } |
| | | } |
| | | else |
| | | { |
| | | // This is weird because the Z wasn't the last character. If we should |
| | | // enforce strict syntax checking, then throw an exception. Otherwise, |
| | | // return what we've got so far. |
| | | if (DirectoryServer.getSyntaxEnforcementPolicy() == |
| | | AcceptRejectWarn.REJECT) |
| | | { |
| | | int msgID = MSGID_ATTR_SYNTAX_GENERALIZED_TIME_INVALID_CHAR; |
| | | String message = getMessage(msgID, valueString, 'Z', endPos); |
| | | throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, |
| | | message, msgID); |
| | | } |
| | | else |
| | | { |
| | | GregorianCalendar calendar = |
| | | new GregorianCalendar(TimeZone.getTimeZone("UTC")); |
| | | calendar.setTimeZone(utcTimeZone); |
| | | calendar.set(year, (month-1), day, hour, minute, second); |
| | | calendar.set(Calendar.MILLISECOND, millisecond); |
| | | |
| | | dateFormatLock.lock(); |
| | | |
| | | try |
| | | { |
| | | return new ASN1OctetString(dateFormat.format(calendar.getTime())); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | debugCaught(DebugLogLevel.ERROR, e); |
| | | } |
| | | |
| | | int msgID = MSGID_ATTR_SYNTAX_GENERALIZED_TIME_NORMALIZE_FAILURE; |
| | | String message = getMessage(msgID, valueString, |
| | | stackTraceToSingleLineString(e)); |
| | | |
| | | throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, |
| | | message, msgID, e); |
| | | } |
| | | finally |
| | | { |
| | | dateFormatLock.unlock(); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | |
| | | // If the character is a plus or minus, then take the next two or four |
| | | // digits and use them as a time zone offset. |
| | | else if ((c == '-') || (c == '+')) |
| | | { |
| | | int offset; |
| | | int charsRemaining = length - endPos - 1; |
| | | if (charsRemaining == 2) |
| | | { |
| | | // The offset specifies the number of hours off GMT. |
| | | try |
| | | { |
| | | offset = Integer.parseInt(valueString.substring(endPos+1)) * 3600000; |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | debugCaught(DebugLogLevel.ERROR, e); |
| | | } |
| | | |
| | | int msgID = MSGID_ATTR_SYNTAX_GENERALIZED_TIME_INVALID_OFFSET; |
| | | String message = getMessage(msgID, valueString, |
| | | valueString.substring(endPos)); |
| | | |
| | | if (DirectoryServer.getSyntaxEnforcementPolicy() == |
| | | AcceptRejectWarn.REJECT) |
| | | { |
| | | throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, |
| | | message, msgID); |
| | | } |
| | | else |
| | | { |
| | | logError(ErrorLogCategory.SCHEMA, ErrorLogSeverity.MILD_ERROR, |
| | | message, msgID); |
| | | offset = 0; |
| | | } |
| | | } |
| | | } |
| | | else if (charsRemaining == 4) |
| | | { |
| | | // The offset specifies the number of hours and minutes off GMT. |
| | | try |
| | | { |
| | | String hourStr = valueString.substring(endPos+1, endPos+3); |
| | | String minStr = valueString.substring(endPos+3, endPos+5); |
| | | offset = (Integer.parseInt(hourStr) * 3600000) + |
| | | (Integer.parseInt(minStr) * 60000); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | debugCaught(DebugLogLevel.ERROR, e); |
| | | } |
| | | |
| | | int msgID = MSGID_ATTR_SYNTAX_GENERALIZED_TIME_INVALID_OFFSET; |
| | | String message = getMessage(msgID, valueString, |
| | | valueString.substring(endPos)); |
| | | |
| | | if (DirectoryServer.getSyntaxEnforcementPolicy() == |
| | | AcceptRejectWarn.REJECT) |
| | | { |
| | | throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, |
| | | message, msgID); |
| | | } |
| | | else |
| | | { |
| | | logError(ErrorLogCategory.SCHEMA, ErrorLogSeverity.MILD_ERROR, |
| | | message, msgID); |
| | | offset = 0; |
| | | } |
| | | } |
| | | } |
| | | else |
| | | { |
| | | // It is an invalid offset, so either throw an exception or assume the |
| | | // local time zone. |
| | | int msgID = MSGID_ATTR_SYNTAX_GENERALIZED_TIME_INVALID_OFFSET; |
| | | String message = getMessage(msgID, valueString, |
| | | valueString.substring(endPos)); |
| | | |
| | | if (DirectoryServer.getSyntaxEnforcementPolicy() == |
| | | AcceptRejectWarn.REJECT) |
| | | { |
| | | throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, |
| | | message, msgID); |
| | | } |
| | | else |
| | | { |
| | | logError(ErrorLogCategory.SCHEMA, ErrorLogSeverity.MILD_ERROR, |
| | | message, msgID); |
| | | offset = TimeZone.getDefault().getRawOffset(); |
| | | } |
| | | } |
| | | |
| | | GregorianCalendar calendar = new GregorianCalendar(year, (month-1), day, |
| | | hour, minute, second); |
| | | calendar.setTimeZone(utcTimeZone); |
| | | calendar.set(Calendar.MILLISECOND, millisecond); |
| | | calendar.set(Calendar.ZONE_OFFSET, offset); |
| | | |
| | | dateFormatLock.lock(); |
| | | |
| | | try |
| | | { |
| | | return new ASN1OctetString(dateFormat.format(calendar.getTime())); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | debugCaught(DebugLogLevel.ERROR, e); |
| | | } |
| | | |
| | | int msgID = MSGID_ATTR_SYNTAX_GENERALIZED_TIME_NORMALIZE_FAILURE; |
| | | String message = getMessage(msgID, valueString, |
| | | stackTraceToSingleLineString(e)); |
| | | |
| | | throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, |
| | | message, msgID, e); |
| | | } |
| | | finally |
| | | { |
| | | dateFormatLock.unlock(); |
| | | } |
| | | } |
| | | |
| | | |
| | | // If we've gotten here, then there was an illegal character at the end of |
| | | // the value. Either throw an exception or assume the default time zone. |
| | | int msgID = MSGID_ATTR_SYNTAX_GENERALIZED_TIME_INVALID_CHAR; |
| | | String message = getMessage(msgID, valueString, c, endPos); |
| | | |
| | | if (DirectoryServer.getSyntaxEnforcementPolicy() == |
| | | AcceptRejectWarn.REJECT) |
| | | { |
| | | throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, |
| | | message, msgID); |
| | | } |
| | | else |
| | | { |
| | | logError(ErrorLogCategory.SCHEMA, ErrorLogSeverity.MILD_ERROR, message, |
| | | msgID); |
| | | |
| | | GregorianCalendar calendar = new GregorianCalendar(year, (month-1), day, |
| | | hour, minute, second); |
| | | calendar.setTimeZone(utcTimeZone); |
| | | calendar.set(Calendar.MILLISECOND, millisecond); |
| | | |
| | | dateFormatLock.lock(); |
| | | |
| | | try |
| | | { |
| | | return new ASN1OctetString(dateFormat.format(calendar.getTime())); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | debugCaught(DebugLogLevel.ERROR, e); |
| | | } |
| | | |
| | | msgID = MSGID_ATTR_SYNTAX_GENERALIZED_TIME_NORMALIZE_FAILURE; |
| | | message = getMessage(msgID, valueString, |
| | | stackTraceToSingleLineString(e)); |
| | | |
| | | throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, |
| | | message, msgID, e); |
| | | } |
| | | finally |
| | | { |
| | | dateFormatLock.unlock(); |
| | | return new ASN1OctetString(value.value()); |
| | | } |
| | | } |
| | | } |
| | |
| | | */ |
| | | public boolean areEqual(ByteString value1, ByteString value2) |
| | | { |
| | | // Since the values are already normalized, we just need to compare the |
| | | // associated byte arrays. |
| | | return Arrays.equals(value1.value(), value2.value()); |
| | | try |
| | | { |
| | | long time1 = GeneralizedTimeSyntax.decodeGeneralizedTimeValue(value1); |
| | | long time2 = GeneralizedTimeSyntax.decodeGeneralizedTimeValue(value2); |
| | | return (time1 == time2); |
| | | } |
| | | catch (DirectoryException de) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | debugCaught(DebugLogLevel.ERROR, de); |
| | | } |
| | | |
| | | return false; |
| | | } |
| | | } |
| | | } |
| | | |
| | |
| | | |
| | | |
| | | import java.text.SimpleDateFormat; |
| | | import java.util.Calendar; |
| | | import java.util.GregorianCalendar; |
| | | import java.util.TimeZone; |
| | | import java.util.concurrent.locks.ReentrantLock; |
| | | |
| | |
| | | import org.opends.server.config.ConfigException; |
| | | import org.opends.server.core.DirectoryServer; |
| | | import org.opends.server.protocols.asn1.ASN1OctetString; |
| | | import org.opends.server.types.AcceptRejectWarn; |
| | | import org.opends.server.types.ByteString; |
| | | import org.opends.server.types.DirectoryException; |
| | | import org.opends.server.types.ErrorLogCategory; |
| | | import org.opends.server.types.ErrorLogSeverity; |
| | | import org.opends.server.types.InitializationException; |
| | | import org.opends.server.types.ResultCode; |
| | | |
| | | import static org.opends.server.loggers.debug.DebugLogger.debugCaught; |
| | | import static org.opends.server.loggers.debug.DebugLogger.debugEnabled; |
| | |
| | | public class GeneralizedTimeOrderingMatchingRule |
| | | extends OrderingMatchingRule |
| | | { |
| | | |
| | | |
| | | |
| | | /** |
| | | * The serial version identifier required to satisfy the compiler because this |
| | | * class implements the <CODE>java.io.Serializable</CODE> interface. This |
| | |
| | | public ByteString normalizeValue(ByteString value) |
| | | throws DirectoryException |
| | | { |
| | | String valueString = value.stringValue().toUpperCase(); |
| | | int length = valueString.length(); |
| | | |
| | | |
| | | //Make sure that it has at least eleven characters and parse the first ten |
| | | // as the year, month, day, and hour. |
| | | if (length < 11) |
| | | try |
| | | { |
| | | int msgID = MSGID_ATTR_SYNTAX_GENERALIZED_TIME_TOO_SHORT; |
| | | String message = getMessage(msgID, valueString); |
| | | long timestamp = GeneralizedTimeSyntax.decodeGeneralizedTimeValue(value); |
| | | return new ASN1OctetString(GeneralizedTimeSyntax.format(timestamp)); |
| | | } |
| | | catch (DirectoryException de) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | debugCaught(DebugLogLevel.ERROR, de); |
| | | } |
| | | |
| | | switch (DirectoryServer.getSyntaxEnforcementPolicy()) |
| | | { |
| | | case REJECT: |
| | | throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, |
| | | message, msgID); |
| | | throw de; |
| | | |
| | | case WARN: |
| | | logError(ErrorLogCategory.SCHEMA, ErrorLogSeverity.SEVERE_WARNING, |
| | | message, msgID); |
| | | return new ASN1OctetString(valueString); |
| | | de.getErrorMessage(), de.getErrorMessageID()); |
| | | return new ASN1OctetString(value.value()); |
| | | |
| | | default: |
| | | return new ASN1OctetString(valueString); |
| | | } |
| | | } |
| | | |
| | | |
| | | // The year, month, day, and hour must always be specified. |
| | | int year; |
| | | int month; |
| | | int day; |
| | | int hour; |
| | | try |
| | | { |
| | | year = Integer.parseInt(valueString.substring(0, 4)); |
| | | month = Integer.parseInt(valueString.substring(4, 6)); |
| | | day = Integer.parseInt(valueString.substring(6, 8)); |
| | | hour = Integer.parseInt(valueString.substring(8, 10)); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | debugCaught(DebugLogLevel.ERROR, e); |
| | | } |
| | | |
| | | int msgID = MSGID_ATTR_SYNTAX_GENERALIZED_TIME_CANNOT_PARSE; |
| | | String message = getMessage(msgID, valueString, |
| | | String.valueOf(e)); |
| | | |
| | | switch (DirectoryServer.getSyntaxEnforcementPolicy()) |
| | | { |
| | | case REJECT: |
| | | throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, |
| | | message, msgID); |
| | | case WARN: |
| | | logError(ErrorLogCategory.SCHEMA, ErrorLogSeverity.SEVERE_WARNING, |
| | | message, msgID); |
| | | return new ASN1OctetString(valueString); |
| | | default: |
| | | return new ASN1OctetString(valueString); |
| | | } |
| | | } |
| | | |
| | | |
| | | // The minute may come next, but if not then it should indicate that we've |
| | | // hit the end of the value. |
| | | int minute; |
| | | if (isDigit(valueString.charAt(10))) |
| | | { |
| | | try |
| | | { |
| | | minute = Integer.parseInt(valueString.substring(10, 12)); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | debugCaught(DebugLogLevel.ERROR, e); |
| | | } |
| | | |
| | | int msgID = MSGID_ATTR_SYNTAX_GENERALIZED_TIME_CANNOT_PARSE; |
| | | String message = getMessage(msgID, valueString, |
| | | String.valueOf(e)); |
| | | |
| | | switch (DirectoryServer.getSyntaxEnforcementPolicy()) |
| | | { |
| | | case REJECT: |
| | | throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, |
| | | message, msgID); |
| | | case WARN: |
| | | logError(ErrorLogCategory.SCHEMA, ErrorLogSeverity.SEVERE_WARNING, |
| | | message, msgID); |
| | | return new ASN1OctetString(valueString); |
| | | default: |
| | | return new ASN1OctetString(valueString); |
| | | } |
| | | } |
| | | } |
| | | else |
| | | { |
| | | return processValueEnd(valueString, 10, year, month, day, hour, 0, 0, 0); |
| | | } |
| | | |
| | | |
| | | // The second should come next, but if not then it should indicate that |
| | | // we've hit the end of the value. |
| | | int second; |
| | | if (length < 13) |
| | | { |
| | | // Technically, this is invalid. If we're enforcing strict syntax |
| | | // adherence, then throw an exception. Otherwise, just assume that it's |
| | | // a time with a second of zero and parse it in the local time zone. |
| | | if (DirectoryServer.getSyntaxEnforcementPolicy() == |
| | | AcceptRejectWarn.REJECT) |
| | | { |
| | | int msgID = MSGID_ATTR_SYNTAX_GENERALIZED_TIME_TOO_SHORT; |
| | | String message = getMessage(msgID, valueString); |
| | | throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, |
| | | message, msgID); |
| | | } |
| | | else |
| | | { |
| | | GregorianCalendar calendar = |
| | | new GregorianCalendar(year, (month-1), day, hour, minute, 0); |
| | | calendar.setTimeZone(utcTimeZone); |
| | | |
| | | dateFormatLock.lock(); |
| | | |
| | | try |
| | | { |
| | | return new ASN1OctetString(dateFormat.format(calendar.getTime())); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | debugCaught(DebugLogLevel.ERROR, e); |
| | | } |
| | | |
| | | int msgID = MSGID_ATTR_SYNTAX_GENERALIZED_TIME_NORMALIZE_FAILURE; |
| | | String message = getMessage(msgID, valueString, |
| | | stackTraceToSingleLineString(e)); |
| | | |
| | | throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, |
| | | message, msgID, e); |
| | | } |
| | | finally |
| | | { |
| | | dateFormatLock.unlock(); |
| | | } |
| | | } |
| | | } |
| | | else |
| | | { |
| | | if (isDigit(valueString.charAt(12))) |
| | | { |
| | | try |
| | | { |
| | | second = Integer.parseInt(valueString.substring(12, 14)); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | debugCaught(DebugLogLevel.ERROR, e); |
| | | } |
| | | |
| | | int msgID = MSGID_ATTR_SYNTAX_GENERALIZED_TIME_CANNOT_PARSE; |
| | | String message = getMessage(msgID, valueString, |
| | | String.valueOf(e)); |
| | | |
| | | if (DirectoryServer.getSyntaxEnforcementPolicy() == |
| | | AcceptRejectWarn.REJECT) |
| | | { |
| | | throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, |
| | | message, msgID, e); |
| | | } |
| | | else |
| | | { |
| | | logError(ErrorLogCategory.SCHEMA, ErrorLogSeverity.MILD_ERROR, |
| | | message, msgID); |
| | | return new ASN1OctetString(valueString); |
| | | } |
| | | } |
| | | } |
| | | else |
| | | { |
| | | return processValueEnd(valueString, 12, year, month, day, hour, minute, |
| | | 0, 0); |
| | | } |
| | | } |
| | | |
| | | |
| | | // If the next character is a period, then it will start the sub-second |
| | | // portion of the value. Otherwise, it should indicate that we've hit the |
| | | // end of the value. |
| | | if (length < 15) |
| | | { |
| | | // Technically, this is invalid. If we're enforcing strict syntax |
| | | // adherence, then throw an exception. Otherwise, just assume that it's |
| | | // a time with a second of zero and parse it in the local time zone. |
| | | if (DirectoryServer.getSyntaxEnforcementPolicy() == |
| | | AcceptRejectWarn.REJECT) |
| | | { |
| | | int msgID = MSGID_ATTR_SYNTAX_GENERALIZED_TIME_TOO_SHORT; |
| | | String message = getMessage(msgID, valueString); |
| | | throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, |
| | | message, msgID); |
| | | } |
| | | else |
| | | { |
| | | GregorianCalendar calendar = |
| | | new GregorianCalendar(year, (month-1), day, hour, minute, second); |
| | | calendar.setTimeZone(utcTimeZone); |
| | | |
| | | dateFormatLock.lock(); |
| | | |
| | | try |
| | | { |
| | | return new ASN1OctetString(dateFormat.format(calendar.getTime())); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | debugCaught(DebugLogLevel.ERROR, e); |
| | | } |
| | | |
| | | int msgID = MSGID_ATTR_SYNTAX_GENERALIZED_TIME_NORMALIZE_FAILURE; |
| | | String message = getMessage(msgID, valueString, |
| | | stackTraceToSingleLineString(e)); |
| | | |
| | | throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, |
| | | message, msgID, e); |
| | | } |
| | | finally |
| | | { |
| | | dateFormatLock.unlock(); |
| | | } |
| | | } |
| | | } |
| | | else |
| | | { |
| | | if (valueString.charAt(14) == '.') |
| | | { |
| | | // There should be some number of digits following the decimal point to |
| | | // indicate the sub-second value. We'll read all of them now, but may |
| | | // throw some away later. |
| | | char c; |
| | | int pos = 15; |
| | | StringBuilder buffer = new StringBuilder(3); |
| | | for ( ; pos < length; pos++) |
| | | { |
| | | if (isDigit(c = valueString.charAt(pos))) |
| | | { |
| | | buffer.append(c); |
| | | } |
| | | else |
| | | { |
| | | break; |
| | | } |
| | | } |
| | | |
| | | int millisecond; |
| | | switch (buffer.length()) |
| | | { |
| | | case 0: |
| | | millisecond = 0; |
| | | break; |
| | | case 1: |
| | | millisecond = (100 * Integer.parseInt(buffer.toString())); |
| | | break; |
| | | case 2: |
| | | millisecond = (10 * Integer.parseInt(buffer.toString())); |
| | | break; |
| | | case 3: |
| | | millisecond = Integer.parseInt(buffer.toString()); |
| | | break; |
| | | default: |
| | | // We only want three digits for the millisecond, but if the fourth |
| | | // digit is greater than or equal to five, then we may need to round |
| | | // up. |
| | | millisecond = Integer.parseInt(buffer.toString().substring(0, 3)); |
| | | switch (buffer.charAt(3)) |
| | | { |
| | | case 5: |
| | | case 6: |
| | | case 7: |
| | | case 8: |
| | | case 9: |
| | | millisecond++; |
| | | break; |
| | | } |
| | | break; |
| | | } |
| | | |
| | | return processValueEnd(valueString, pos, year, month, day, hour, minute, |
| | | second, millisecond); |
| | | } |
| | | else |
| | | { |
| | | return processValueEnd(valueString, 14, year, month, day, hour, minute, |
| | | second, 0); |
| | | } |
| | | } |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Processes the specified portion of the value as the end of the generalized |
| | | * time specification. If the character at the specified location is a 'Z', |
| | | * then it will be assumed that the value is already in UTC. If it is a '+' |
| | | * or '-', then it will be assumed that the remainder is an offset from UTC. |
| | | * Otherwise, it will be an error. |
| | | * |
| | | * @param valueString The value being parsed as a generalized time string. |
| | | * @param endPos The position at which the end of the value begins. |
| | | * @param year The year parsed from the value. |
| | | * @param month The month parsed from the value. |
| | | * @param day The day parsed from the value. |
| | | * @param hour The hour parsed from the value. |
| | | * @param minute The minute parsed from the value. |
| | | * @param second The second parsed from the value. |
| | | * @param millisecond The millisecond parsed from the value. |
| | | * |
| | | * @return The normalized representation of the generalized time parsed from |
| | | * the value. |
| | | * |
| | | * @throws DirectoryException If a problem occurs while attempting to decode |
| | | * the end of the generalized time value. |
| | | */ |
| | | private ByteString processValueEnd(String valueString, int endPos, int year, |
| | | int month, int day, int hour, int minute, |
| | | int second, int millisecond) |
| | | throws DirectoryException |
| | | { |
| | | // First, check to see if we are at the end of the string. If so, then |
| | | // that could either result in an exception or assuming that we should just |
| | | // use the local time zone. |
| | | int length = valueString.length(); |
| | | if (endPos >= length) |
| | | { |
| | | if (DirectoryServer.getSyntaxEnforcementPolicy() == |
| | | AcceptRejectWarn.REJECT) |
| | | { |
| | | int msgID = MSGID_ATTR_SYNTAX_GENERALIZED_TIME_TOO_SHORT; |
| | | String message = getMessage(msgID, valueString); |
| | | throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, |
| | | message, msgID); |
| | | } |
| | | else |
| | | { |
| | | GregorianCalendar calendar = |
| | | new GregorianCalendar(year, (month-1), day, hour, minute, second); |
| | | calendar.setTimeZone(utcTimeZone); |
| | | calendar.set(Calendar.MILLISECOND, millisecond); |
| | | |
| | | dateFormatLock.lock(); |
| | | |
| | | try |
| | | { |
| | | return new ASN1OctetString(dateFormat.format(calendar.getTime())); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | debugCaught(DebugLogLevel.ERROR, e); |
| | | } |
| | | |
| | | int msgID = MSGID_ATTR_SYNTAX_GENERALIZED_TIME_NORMALIZE_FAILURE; |
| | | String message = getMessage(msgID, valueString, |
| | | stackTraceToSingleLineString(e)); |
| | | |
| | | throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, |
| | | message, msgID, e); |
| | | } |
| | | finally |
| | | { |
| | | dateFormatLock.unlock(); |
| | | } |
| | | } |
| | | } |
| | | |
| | | |
| | | // See what the character is at the specified position. If it is a 'Z', |
| | | // then make sure it's the end of the value and treat it as a UTC date. |
| | | char c = valueString.charAt(endPos); |
| | | if (c == 'Z') |
| | | { |
| | | if (endPos == (length-1)) |
| | | { |
| | | GregorianCalendar calendar = |
| | | new GregorianCalendar(year, (month-1), day, hour, minute, second); |
| | | calendar.setTimeZone(utcTimeZone); |
| | | calendar.set(Calendar.MILLISECOND, millisecond); |
| | | |
| | | dateFormatLock.lock(); |
| | | |
| | | try |
| | | { |
| | | return new ASN1OctetString(dateFormat.format(calendar.getTime())); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | debugCaught(DebugLogLevel.ERROR, e); |
| | | } |
| | | |
| | | int msgID = MSGID_ATTR_SYNTAX_GENERALIZED_TIME_NORMALIZE_FAILURE; |
| | | String message = getMessage(msgID, valueString, |
| | | stackTraceToSingleLineString(e)); |
| | | |
| | | throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, |
| | | message, msgID, e); |
| | | } |
| | | finally |
| | | { |
| | | dateFormatLock.unlock(); |
| | | } |
| | | } |
| | | else |
| | | { |
| | | // This is weird because the Z wasn't the last character. If we should |
| | | // enforce strict syntax checking, then throw an exception. Otherwise, |
| | | // return what we've got so far. |
| | | if (DirectoryServer.getSyntaxEnforcementPolicy() == |
| | | AcceptRejectWarn.REJECT) |
| | | { |
| | | int msgID = MSGID_ATTR_SYNTAX_GENERALIZED_TIME_INVALID_CHAR; |
| | | String message = getMessage(msgID, valueString, 'Z', endPos); |
| | | throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, |
| | | message, msgID); |
| | | } |
| | | else |
| | | { |
| | | GregorianCalendar calendar = |
| | | new GregorianCalendar(TimeZone.getTimeZone("UTC")); |
| | | calendar.setTimeZone(utcTimeZone); |
| | | calendar.set(year, (month-1), day, hour, minute, second); |
| | | calendar.set(Calendar.MILLISECOND, millisecond); |
| | | |
| | | dateFormatLock.lock(); |
| | | |
| | | try |
| | | { |
| | | return new ASN1OctetString(dateFormat.format(calendar.getTime())); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | debugCaught(DebugLogLevel.ERROR, e); |
| | | } |
| | | |
| | | int msgID = MSGID_ATTR_SYNTAX_GENERALIZED_TIME_NORMALIZE_FAILURE; |
| | | String message = getMessage(msgID, valueString, |
| | | stackTraceToSingleLineString(e)); |
| | | |
| | | throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, |
| | | message, msgID, e); |
| | | } |
| | | finally |
| | | { |
| | | dateFormatLock.unlock(); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | |
| | | // If the character is a plus or minus, then take the next two or four |
| | | // digits and use them as a time zone offset. |
| | | else if ((c == '-') || (c == '+')) |
| | | { |
| | | int offset; |
| | | int charsRemaining = length - endPos - 1; |
| | | if (charsRemaining == 2) |
| | | { |
| | | // The offset specifies the number of hours off GMT. |
| | | try |
| | | { |
| | | offset = Integer.parseInt(valueString.substring(endPos+1)) * 3600000; |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | debugCaught(DebugLogLevel.ERROR, e); |
| | | } |
| | | |
| | | int msgID = MSGID_ATTR_SYNTAX_GENERALIZED_TIME_INVALID_OFFSET; |
| | | String message = getMessage(msgID, valueString, |
| | | valueString.substring(endPos)); |
| | | |
| | | if (DirectoryServer.getSyntaxEnforcementPolicy() == |
| | | AcceptRejectWarn.REJECT) |
| | | { |
| | | throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, |
| | | message, msgID); |
| | | } |
| | | else |
| | | { |
| | | logError(ErrorLogCategory.SCHEMA, ErrorLogSeverity.MILD_ERROR, |
| | | message, msgID); |
| | | offset = 0; |
| | | } |
| | | } |
| | | } |
| | | else if (charsRemaining == 4) |
| | | { |
| | | // The offset specifies the number of hours and minutes off GMT. |
| | | try |
| | | { |
| | | String hourStr = valueString.substring(endPos+1, endPos+3); |
| | | String minStr = valueString.substring(endPos+3, endPos+5); |
| | | offset = (Integer.parseInt(hourStr) * 3600000) + |
| | | (Integer.parseInt(minStr) * 1000); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | debugCaught(DebugLogLevel.ERROR, e); |
| | | } |
| | | |
| | | int msgID = MSGID_ATTR_SYNTAX_GENERALIZED_TIME_INVALID_OFFSET; |
| | | String message = getMessage(msgID, valueString, |
| | | valueString.substring(endPos)); |
| | | |
| | | if (DirectoryServer.getSyntaxEnforcementPolicy() == |
| | | AcceptRejectWarn.REJECT) |
| | | { |
| | | throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, |
| | | message, msgID); |
| | | } |
| | | else |
| | | { |
| | | logError(ErrorLogCategory.SCHEMA, ErrorLogSeverity.MILD_ERROR, |
| | | message, msgID); |
| | | offset = 0; |
| | | } |
| | | } |
| | | } |
| | | else |
| | | { |
| | | // It is an invalid offset, so either throw an exception or assume the |
| | | // local time zone. |
| | | int msgID = MSGID_ATTR_SYNTAX_GENERALIZED_TIME_INVALID_OFFSET; |
| | | String message = getMessage(msgID, valueString, |
| | | valueString.substring(endPos)); |
| | | |
| | | if (DirectoryServer.getSyntaxEnforcementPolicy() == |
| | | AcceptRejectWarn.REJECT) |
| | | { |
| | | throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, |
| | | message, msgID); |
| | | } |
| | | else |
| | | { |
| | | logError(ErrorLogCategory.SCHEMA, ErrorLogSeverity.MILD_ERROR, |
| | | message, msgID); |
| | | offset = TimeZone.getDefault().getRawOffset(); |
| | | } |
| | | } |
| | | |
| | | GregorianCalendar calendar = new GregorianCalendar(year, (month-1), day, |
| | | hour, minute, second); |
| | | calendar.setTimeZone(utcTimeZone); |
| | | calendar.set(Calendar.MILLISECOND, millisecond); |
| | | calendar.set(Calendar.ZONE_OFFSET, offset); |
| | | |
| | | dateFormatLock.lock(); |
| | | |
| | | try |
| | | { |
| | | return new ASN1OctetString(dateFormat.format(calendar.getTime())); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | debugCaught(DebugLogLevel.ERROR, e); |
| | | } |
| | | |
| | | int msgID = MSGID_ATTR_SYNTAX_GENERALIZED_TIME_NORMALIZE_FAILURE; |
| | | String message = getMessage(msgID, valueString, |
| | | stackTraceToSingleLineString(e)); |
| | | |
| | | throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, |
| | | message, msgID, e); |
| | | } |
| | | finally |
| | | { |
| | | dateFormatLock.unlock(); |
| | | } |
| | | } |
| | | |
| | | |
| | | // If we've gotten here, then there was an illegal character at the end of |
| | | // the value. Either throw an exception or assume the default time zone. |
| | | int msgID = MSGID_ATTR_SYNTAX_GENERALIZED_TIME_INVALID_CHAR; |
| | | String message = getMessage(msgID, valueString, c, endPos); |
| | | |
| | | if (DirectoryServer.getSyntaxEnforcementPolicy() == |
| | | AcceptRejectWarn.REJECT) |
| | | { |
| | | throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, |
| | | message, msgID); |
| | | } |
| | | else |
| | | { |
| | | logError(ErrorLogCategory.SCHEMA, ErrorLogSeverity.MILD_ERROR, message, |
| | | msgID); |
| | | |
| | | GregorianCalendar calendar = new GregorianCalendar(year, (month-1), day, |
| | | hour, minute, second); |
| | | calendar.setTimeZone(utcTimeZone); |
| | | calendar.set(Calendar.MILLISECOND, millisecond); |
| | | |
| | | dateFormatLock.lock(); |
| | | |
| | | try |
| | | { |
| | | return new ASN1OctetString(dateFormat.format(calendar.getTime())); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | debugCaught(DebugLogLevel.ERROR, e); |
| | | } |
| | | |
| | | msgID = MSGID_ATTR_SYNTAX_GENERALIZED_TIME_NORMALIZE_FAILURE; |
| | | message = getMessage(msgID, valueString, |
| | | stackTraceToSingleLineString(e)); |
| | | |
| | | throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, |
| | | message, msgID, e); |
| | | } |
| | | finally |
| | | { |
| | | dateFormatLock.unlock(); |
| | | return new ASN1OctetString(value.value()); |
| | | } |
| | | } |
| | | } |
| | |
| | | */ |
| | | public int compareValues(ByteString value1, ByteString value2) |
| | | { |
| | | return compare(value1.value(), value2.value()); |
| | | try |
| | | { |
| | | long time1 = GeneralizedTimeSyntax.decodeGeneralizedTimeValue(value1); |
| | | long time2 = GeneralizedTimeSyntax.decodeGeneralizedTimeValue(value2); |
| | | |
| | | if (time1 == time2) |
| | | { |
| | | return 0; |
| | | } |
| | | else if (time1 > time2) |
| | | { |
| | | return 1; |
| | | } |
| | | else |
| | | { |
| | | return -1; |
| | | } |
| | | } |
| | | catch (DirectoryException de) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | debugCaught(DebugLogLevel.ERROR, de); |
| | | } |
| | | |
| | | return 0; |
| | | } |
| | | } |
| | | |
| | | |
| | |
| | | */ |
| | | public int compare(byte[] b1, byte[] b2) |
| | | { |
| | | int minLength = Math.min(b1.length, b2.length); |
| | | |
| | | for (int i=0; i < minLength; i++) |
| | | { |
| | | if (b1[i] == b2[i]) |
| | | { |
| | | continue; |
| | | } |
| | | else if (b1[i] < b2[i]) |
| | | { |
| | | return -1; |
| | | } |
| | | else if (b1[i] > b2[i]) |
| | | { |
| | | return 1; |
| | | } |
| | | } |
| | | |
| | | if (b1.length == b2.length) |
| | | { |
| | | return 0; |
| | | } |
| | | else if (b1.length < b2.length) |
| | | { |
| | | return -1; |
| | | } |
| | | else |
| | | { |
| | | return 1; |
| | | } |
| | | return compareValues(new ASN1OctetString(b1), new ASN1OctetString(b2)); |
| | | } |
| | | } |
| | | |
| | |
| | | |
| | | |
| | | import java.text.SimpleDateFormat; |
| | | import java.util.Calendar; |
| | | import java.util.Date; |
| | | import java.util.GregorianCalendar; |
| | | import java.util.TimeZone; |
| | | import java.util.concurrent.locks.ReentrantLock; |
| | | |
| | |
| | | public class GeneralizedTimeSyntax |
| | | extends AttributeSyntax |
| | | { |
| | | |
| | | |
| | | |
| | | /** |
| | | * The lock that will be used to provide threadsafe access to the date |
| | | * formatter. |
| | |
| | | public boolean valueIsAcceptable(ByteString value, |
| | | StringBuilder invalidReason) |
| | | { |
| | | // Get the value as a string and verify that it is at least long enough for |
| | | // "YYYYMMDDhhZ", which is the shortest allowed value. |
| | | String valueString = value.stringValue().toUpperCase(); |
| | | int length = valueString.length(); |
| | | if (length < 11) |
| | | try |
| | | { |
| | | int msgID = MSGID_ATTR_SYNTAX_GENERALIZED_TIME_TOO_SHORT; |
| | | String message = getMessage(msgID, valueString); |
| | | invalidReason.append(message); |
| | | return false; |
| | | } |
| | | |
| | | |
| | | // 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': |
| | | case '1': |
| | | case '2': |
| | | case '3': |
| | | case '4': |
| | | case '5': |
| | | case '6': |
| | | case '7': |
| | | case '8': |
| | | case '9': |
| | | // These are all fine. |
| | | break; |
| | | default: |
| | | int msgID = MSGID_ATTR_SYNTAX_GENERALIZED_TIME_INVALID_YEAR; |
| | | String message = getMessage(msgID, valueString, |
| | | valueString.charAt(i)); |
| | | invalidReason.append(message); |
| | | return false; |
| | | } |
| | | } |
| | | |
| | | |
| | | // 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); |
| | | char m2 = valueString.charAt(5); |
| | | switch (m1) |
| | | { |
| | | case '0': |
| | | // m2 must be a digit between 1 and 9. |
| | | switch (m2) |
| | | { |
| | | case '1': |
| | | case '2': |
| | | case '3': |
| | | case '4': |
| | | case '5': |
| | | case '6': |
| | | case '7': |
| | | case '8': |
| | | case '9': |
| | | // These are all fine. |
| | | break; |
| | | default: |
| | | int msgID = MSGID_ATTR_SYNTAX_GENERALIZED_TIME_INVALID_MONTH; |
| | | String message = getMessage(msgID, valueString, |
| | | valueString.substring(4, 6)); |
| | | invalidReason.append(message); |
| | | return false; |
| | | } |
| | | break; |
| | | case '1': |
| | | // m2 must be a digit between 0 and 2. |
| | | switch (m2) |
| | | { |
| | | case '0': |
| | | case '1': |
| | | case '2': |
| | | // These are all fine. |
| | | break; |
| | | default: |
| | | int msgID = MSGID_ATTR_SYNTAX_GENERALIZED_TIME_INVALID_MONTH; |
| | | String message = getMessage(msgID, valueString, |
| | | valueString.substring(4, 6)); |
| | | invalidReason.append(message); |
| | | return false; |
| | | } |
| | | break; |
| | | default: |
| | | int msgID = MSGID_ATTR_SYNTAX_GENERALIZED_TIME_INVALID_MONTH; |
| | | String message = getMessage(msgID, valueString, |
| | | valueString.substring(4, 6)); |
| | | invalidReason.append(message); |
| | | return false; |
| | | } |
| | | |
| | | |
| | | // 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. |
| | | char d1 = valueString.charAt(6); |
| | | char d2 = valueString.charAt(7); |
| | | switch (d1) |
| | | { |
| | | case '0': |
| | | // d2 must be a digit between 1 and 9. |
| | | switch (d2) |
| | | { |
| | | case '1': |
| | | case '2': |
| | | case '3': |
| | | case '4': |
| | | case '5': |
| | | case '6': |
| | | case '7': |
| | | case '8': |
| | | case '9': |
| | | // These are all fine. |
| | | break; |
| | | default: |
| | | int msgID = MSGID_ATTR_SYNTAX_GENERALIZED_TIME_INVALID_DAY; |
| | | String message = getMessage(msgID, valueString, |
| | | valueString.substring(6, 8)); |
| | | invalidReason.append(message); |
| | | return false; |
| | | } |
| | | break; |
| | | case '1': |
| | | // Treated the same as '2'. |
| | | case '2': |
| | | // d2 must be a digit between 0 and 9. |
| | | switch (d2) |
| | | { |
| | | 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: |
| | | int msgID = MSGID_ATTR_SYNTAX_GENERALIZED_TIME_INVALID_DAY; |
| | | String message = getMessage(msgID, valueString, |
| | | valueString.substring(6, 8)); |
| | | invalidReason.append(message); |
| | | return false; |
| | | } |
| | | break; |
| | | case '3': |
| | | // d2 must be either 0 or 1. |
| | | switch (d2) |
| | | { |
| | | case '0': |
| | | case '1': |
| | | // These are all fine. |
| | | break; |
| | | default: |
| | | int msgID = MSGID_ATTR_SYNTAX_GENERALIZED_TIME_INVALID_DAY; |
| | | String message = getMessage(msgID, valueString, |
| | | valueString.substring(6, 8)); |
| | | invalidReason.append(message); |
| | | return false; |
| | | } |
| | | break; |
| | | default: |
| | | int msgID = MSGID_ATTR_SYNTAX_GENERALIZED_TIME_INVALID_DAY; |
| | | String message = getMessage(msgID, valueString, |
| | | valueString.substring(6, 8)); |
| | | invalidReason.append(message); |
| | | return false; |
| | | } |
| | | |
| | | |
| | | // The next two characters must be the hour, and they must form the string |
| | | // representation of an integer between 00 and 23. |
| | | char h1 = valueString.charAt(8); |
| | | char h2 = valueString.charAt(9); |
| | | switch (h1) |
| | | { |
| | | case '0': |
| | | // This is treated the same as '1'. |
| | | case '1': |
| | | switch (h2) |
| | | { |
| | | 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: |
| | | int msgID = MSGID_ATTR_SYNTAX_GENERALIZED_TIME_INVALID_HOUR; |
| | | String message = getMessage(msgID, valueString, |
| | | valueString.substring(8, 10)); |
| | | invalidReason.append(message); |
| | | return false; |
| | | } |
| | | break; |
| | | case '2': |
| | | // This must be a digit between 0 and 3. |
| | | switch (h2) |
| | | { |
| | | case '0': |
| | | case '1': |
| | | case '2': |
| | | case '3': |
| | | // These are all fine. |
| | | break; |
| | | default: |
| | | int msgID = MSGID_ATTR_SYNTAX_GENERALIZED_TIME_INVALID_HOUR; |
| | | String message = getMessage(msgID, valueString, |
| | | valueString.substring(8, 10)); |
| | | invalidReason.append(message); |
| | | return false; |
| | | } |
| | | break; |
| | | default: |
| | | int msgID = MSGID_ATTR_SYNTAX_GENERALIZED_TIME_INVALID_HOUR; |
| | | String message = getMessage(msgID, valueString, |
| | | valueString.substring(8, 10)); |
| | | invalidReason.append(message); |
| | | return false; |
| | | } |
| | | |
| | | |
| | | // Next, there should be either two digits comprising an integer between 00 |
| | | // and 59 (for the minute), a letter 'Z' (for the UTC specifier), or a plus |
| | | // or minus sign followed by two or four digits (for the UTC offset). |
| | | 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) |
| | | { |
| | | int msgID = MSGID_ATTR_SYNTAX_GENERALIZED_TIME_INVALID_CHAR; |
| | | String message = getMessage(msgID, valueString, m1, 10); |
| | | invalidReason.append(message); |
| | | return false; |
| | | } |
| | | |
| | | switch (valueString.charAt(11)) |
| | | { |
| | | 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: |
| | | int msgID = MSGID_ATTR_SYNTAX_GENERALIZED_TIME_INVALID_MINUTE; |
| | | String message = getMessage(msgID, valueString, |
| | | valueString.substring(10, 12)); |
| | | invalidReason.append(message); |
| | | return false; |
| | | } |
| | | |
| | | break; |
| | | case 'Z': |
| | | // This is fine only if we are at the end of the value. |
| | | if (length == 11) |
| | | { |
| | | decodeGeneralizedTimeValue(value); |
| | | return true; |
| | | } |
| | | else |
| | | catch (DirectoryException de) |
| | | { |
| | | int msgID = MSGID_ATTR_SYNTAX_GENERALIZED_TIME_INVALID_CHAR; |
| | | String message = getMessage(msgID, valueString, m1, 10); |
| | | invalidReason.append(message); |
| | | invalidReason.append(de.getErrorMessage()); |
| | | return false; |
| | | } |
| | | |
| | | 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)) |
| | | { |
| | | return hasValidOffset(valueString, 11, invalidReason); |
| | | } |
| | | else |
| | | { |
| | | int msgID = MSGID_ATTR_SYNTAX_GENERALIZED_TIME_INVALID_CHAR; |
| | | String message = getMessage(msgID, valueString, m1, 10); |
| | | invalidReason.append(message); |
| | | return false; |
| | | } |
| | | |
| | | default: |
| | | int msgID = MSGID_ATTR_SYNTAX_GENERALIZED_TIME_INVALID_CHAR; |
| | | String message = getMessage(msgID, valueString, m1, 10); |
| | | invalidReason.append(message); |
| | | return false; |
| | | } |
| | | |
| | | |
| | | // 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), or a plus or minus sign followed by two or four |
| | | // digits (for the UTC offset). |
| | | 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) |
| | | { |
| | | int msgID = MSGID_ATTR_SYNTAX_GENERALIZED_TIME_INVALID_CHAR; |
| | | String message = getMessage(msgID, valueString, s1, 12); |
| | | invalidReason.append(message); |
| | | return false; |
| | | } |
| | | |
| | | switch (valueString.charAt(13)) |
| | | { |
| | | 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: |
| | | int msgID = MSGID_ATTR_SYNTAX_GENERALIZED_TIME_INVALID_SECOND; |
| | | String message = getMessage(msgID, valueString, |
| | | valueString.substring(12, 14)); |
| | | invalidReason.append(message); |
| | | return false; |
| | | } |
| | | |
| | | break; |
| | | case '6': |
| | | // There must be at least two more characters and the next one must be |
| | | // a 0. |
| | | if (length < 15) |
| | | { |
| | | int msgID = MSGID_ATTR_SYNTAX_GENERALIZED_TIME_INVALID_CHAR; |
| | | String message = getMessage(msgID, valueString, s1, 12); |
| | | invalidReason.append(message); |
| | | return false; |
| | | } |
| | | |
| | | if (valueString.charAt(13) != '0') |
| | | { |
| | | int msgID = MSGID_ATTR_SYNTAX_GENERALIZED_TIME_INVALID_SECOND; |
| | | String message = getMessage(msgID, valueString, |
| | | valueString.substring(12, 14)); |
| | | invalidReason.append(message); |
| | | return false; |
| | | } |
| | | |
| | | break; |
| | | case 'Z': |
| | | // This is fine only if we are at the end of the value. |
| | | if (length == 13) |
| | | { |
| | | return true; |
| | | } |
| | | else |
| | | { |
| | | int msgID = MSGID_ATTR_SYNTAX_GENERALIZED_TIME_INVALID_CHAR; |
| | | String message = getMessage(msgID, valueString, s1, 12); |
| | | invalidReason.append(message); |
| | | return false; |
| | | } |
| | | |
| | | 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)) |
| | | { |
| | | return hasValidOffset(valueString, 13, invalidReason); |
| | | } |
| | | else |
| | | { |
| | | int msgID = MSGID_ATTR_SYNTAX_GENERALIZED_TIME_INVALID_CHAR; |
| | | String message = getMessage(msgID, valueString, s1, 12); |
| | | invalidReason.append(message); |
| | | return false; |
| | | } |
| | | |
| | | default: |
| | | int msgID = MSGID_ATTR_SYNTAX_GENERALIZED_TIME_INVALID_CHAR; |
| | | String message = getMessage(msgID, valueString, s1, 12); |
| | | invalidReason.append(message); |
| | | return false; |
| | | } |
| | | |
| | | |
| | | // 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 ',': |
| | | // There will be a sub-second portion. Walk through the rest of the |
| | | // value until we find a Z, +, or -. |
| | | boolean endFound = false; |
| | | int pos = 15; |
| | | while (pos < length) |
| | | { |
| | | |
| | | switch (valueString.charAt(pos)) |
| | | { |
| | | case '0': |
| | | case '1': |
| | | case '2': |
| | | case '3': |
| | | case '4': |
| | | case '5': |
| | | case '6': |
| | | case '7': |
| | | case '8': |
| | | case '9': |
| | | // These are fine as long as we don't have more than three. |
| | | if (pos > 17) |
| | | { |
| | | int msgID = MSGID_ATTR_SYNTAX_GENERALIZED_TIME_LONG_SUBSECOND; |
| | | String message = getMessage(msgID, value); |
| | | invalidReason.append(message); |
| | | return false; |
| | | } |
| | | |
| | | break; |
| | | case 'Z': |
| | | // This must be the end of the string and there must have been |
| | | // at least one sub-second digit. |
| | | if (pos == 15) |
| | | { |
| | | int msgID = |
| | | MSGID_ATTR_SYNTAX_GENERALIZED_TIME_INVALID_SUBSECOND; |
| | | String message = getMessage(msgID, value); |
| | | invalidReason.append(message); |
| | | return false; |
| | | } |
| | | |
| | | if (pos != (length-1)) |
| | | { |
| | | int msgID = MSGID_ATTR_SYNTAX_GENERALIZED_TIME_INVALID_CHAR; |
| | | String message = getMessage(msgID, valueString, |
| | | valueString.charAt(pos), pos); |
| | | invalidReason.append(message); |
| | | return false; |
| | | } |
| | | |
| | | return true; |
| | | |
| | | case '+': |
| | | case '-': |
| | | // There must have been at least one sub-second digit, and there |
| | | // must be either two or four digits left. |
| | | if (pos == 15) |
| | | { |
| | | int msgID = |
| | | MSGID_ATTR_SYNTAX_GENERALIZED_TIME_INVALID_SUBSECOND; |
| | | String message = getMessage(msgID, value); |
| | | invalidReason.append(message); |
| | | return false; |
| | | } |
| | | else if ((length != 17) && (length != 19)) |
| | | { |
| | | int msgID = MSGID_ATTR_SYNTAX_GENERALIZED_TIME_INVALID_CHAR; |
| | | String message = getMessage(msgID, valueString, |
| | | valueString.charAt(pos), pos); |
| | | invalidReason.append(message); |
| | | return false; |
| | | } |
| | | else |
| | | { |
| | | return hasValidOffset(valueString, pos+1, invalidReason); |
| | | } |
| | | |
| | | default: |
| | | int msgID = MSGID_ATTR_SYNTAX_GENERALIZED_TIME_INVALID_CHAR; |
| | | String message = getMessage(msgID, valueString, |
| | | valueString.charAt(pos), pos); |
| | | invalidReason.append(message); |
| | | return false; |
| | | } |
| | | |
| | | pos++; |
| | | } |
| | | |
| | | |
| | | // There must be at least two more characters and the first must be a |
| | | // digit. |
| | | if (length < 16) |
| | | { |
| | | int msgID = MSGID_ATTR_SYNTAX_GENERALIZED_TIME_INVALID_CHAR; |
| | | String message = getMessage(msgID, valueString, s1, 12); |
| | | invalidReason.append(message); |
| | | return false; |
| | | } |
| | | |
| | | break; |
| | | |
| | | case 'Z': |
| | | // This is fine only if we are at the end of the value. |
| | | if (length == 15) |
| | | { |
| | | return true; |
| | | } |
| | | else |
| | | { |
| | | int msgID = MSGID_ATTR_SYNTAX_GENERALIZED_TIME_INVALID_CHAR; |
| | | String message = getMessage(msgID, valueString, |
| | | valueString.charAt(14), 14); |
| | | invalidReason.append(message); |
| | | return false; |
| | | } |
| | | |
| | | 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)) |
| | | { |
| | | return hasValidOffset(valueString, 15, invalidReason); |
| | | } |
| | | else |
| | | { |
| | | int msgID = MSGID_ATTR_SYNTAX_GENERALIZED_TIME_INVALID_CHAR; |
| | | String message = getMessage(msgID, valueString, |
| | | valueString.charAt(14), 14); |
| | | invalidReason.append(message); |
| | | return false; |
| | | } |
| | | |
| | | default: |
| | | int msgID = MSGID_ATTR_SYNTAX_GENERALIZED_TIME_INVALID_CHAR; |
| | | String message = getMessage(msgID, valueString, valueString.charAt(14), |
| | | 14); |
| | | invalidReason.append(message); |
| | | return false; |
| | | } |
| | | |
| | | return true; |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Indicates whether the provided string contains a valid set of two or four |
| | | * UTC offset digits. The provided string must have either two or four |
| | | * characters from the provided start position to the end of the value. |
| | | * |
| | | * @param value The whole value, including the offset. |
| | | * @param startPos The position of the first character that is |
| | | * contained in the offset. |
| | | * @param invalidReason The buffer to which the invalid reason may be |
| | | * appended if the string does not contain a valid set |
| | | * of UTC offset digits. |
| | | * |
| | | * @return <CODE>true</CODE> if the provided offset string is valid, or |
| | | * <CODE>false</CODE> if it is not. |
| | | */ |
| | | private boolean hasValidOffset(String value, int startPos, |
| | | StringBuilder invalidReason) |
| | | { |
| | | int offsetLength = value.length() - startPos; |
| | | if (offsetLength < 2) |
| | | { |
| | | int msgID = MSGID_ATTR_SYNTAX_GENERALIZED_TIME_TOO_SHORT; |
| | | String message = getMessage(msgID, value); |
| | | invalidReason.append(message); |
| | | return false; |
| | | } |
| | | |
| | | // The first two characters must be an integer between 00 and 23. |
| | | switch (value.charAt(startPos)) |
| | | { |
| | | case '0': |
| | | case '1': |
| | | switch (value.charAt(startPos+1)) |
| | | { |
| | | 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: |
| | | int msgID = MSGID_ATTR_SYNTAX_GENERALIZED_TIME_INVALID_OFFSET; |
| | | String message = getMessage(msgID, value, |
| | | value.substring(startPos, |
| | | startPos+offsetLength)); |
| | | invalidReason.append(message); |
| | | return false; |
| | | } |
| | | break; |
| | | case '2': |
| | | switch (value.charAt(startPos+1)) |
| | | { |
| | | case '0': |
| | | case '1': |
| | | case '2': |
| | | case '3': |
| | | // These are all fine. |
| | | break; |
| | | default: |
| | | int msgID = MSGID_ATTR_SYNTAX_GENERALIZED_TIME_INVALID_OFFSET; |
| | | String message = getMessage(msgID, value, |
| | | value.substring(startPos, |
| | | startPos+offsetLength)); |
| | | invalidReason.append(message); |
| | | return false; |
| | | } |
| | | break; |
| | | default: |
| | | int msgID = MSGID_ATTR_SYNTAX_GENERALIZED_TIME_INVALID_OFFSET; |
| | | String message = getMessage(msgID, value, |
| | | value.substring(startPos, |
| | | startPos+offsetLength)); |
| | | invalidReason.append(message); |
| | | return false; |
| | | } |
| | | |
| | | |
| | | // If there are two more characters, then they must be an integer between |
| | | // 00 and 59. |
| | | if (offsetLength == 4) |
| | | { |
| | | switch (value.charAt(startPos+2)) |
| | | { |
| | | case '0': |
| | | case '1': |
| | | case '2': |
| | | case '3': |
| | | case '4': |
| | | case '5': |
| | | switch (value.charAt(startPos+3)) |
| | | { |
| | | 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: |
| | | int msgID = MSGID_ATTR_SYNTAX_GENERALIZED_TIME_INVALID_OFFSET; |
| | | String message = |
| | | getMessage(msgID, value,value.substring(startPos, |
| | | startPos+offsetLength)); |
| | | invalidReason.append(message); |
| | | return false; |
| | | } |
| | | break; |
| | | default: |
| | | int msgID = MSGID_ATTR_SYNTAX_GENERALIZED_TIME_INVALID_OFFSET; |
| | | String message = getMessage(msgID, value, |
| | | value.substring(startPos, |
| | | startPos+offsetLength)); |
| | | invalidReason.append(message); |
| | | return false; |
| | | } |
| | | } |
| | | |
| | | return true; |
| | | } |
| | | |
| | | |
| | |
| | | * Decodes the provided normalized value as a generalized time value and |
| | | * retrieves a timestamp containing its representation. |
| | | * |
| | | * @param normalizedValue The normalized generalized time value to decode to |
| | | * a Java <CODE>Date</CODE>. |
| | | * @param value The normalized value to decode using the generalized time |
| | | * syntax. |
| | | * |
| | | * @return The timestamp created from the provided generalized time value. |
| | | * |
| | | * @throws DirectoryException If the provided value cannot be parsed as a |
| | | * valid generalized time string. |
| | | */ |
| | | public static long decodeGeneralizedTimeValue(ByteString normalizedValue) |
| | | public static long decodeGeneralizedTimeValue(ByteString value) |
| | | throws DirectoryException |
| | | { |
| | | String valueString = normalizedValue.stringValue(); |
| | | try |
| | | { |
| | | dateFormatLock.lock(); |
| | | 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. |
| | | String valueString = value.stringValue().toUpperCase(); |
| | | int length = valueString.length(); |
| | | if (length < 11) |
| | | { |
| | | int msgID = MSGID_ATTR_SYNTAX_GENERALIZED_TIME_TOO_SHORT; |
| | | String message = getMessage(msgID, valueString); |
| | | throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, message, |
| | | msgID); |
| | | } |
| | | |
| | | |
| | | // 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: |
| | | int msgID = MSGID_ATTR_SYNTAX_GENERALIZED_TIME_INVALID_YEAR; |
| | | String message = getMessage(msgID, valueString, |
| | | valueString.charAt(i)); |
| | | throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, |
| | | message, msgID); |
| | | } |
| | | } |
| | | |
| | | |
| | | // 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); |
| | | 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: |
| | | int msgID = MSGID_ATTR_SYNTAX_GENERALIZED_TIME_INVALID_MONTH; |
| | | String message = getMessage(msgID, valueString, |
| | | valueString.substring(4, 6)); |
| | | throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, |
| | | message, msgID); |
| | | } |
| | | 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: |
| | | int msgID = MSGID_ATTR_SYNTAX_GENERALIZED_TIME_INVALID_MONTH; |
| | | String message = getMessage(msgID, valueString, |
| | | valueString.substring(4, 6)); |
| | | throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, |
| | | message, msgID); |
| | | } |
| | | break; |
| | | default: |
| | | int msgID = MSGID_ATTR_SYNTAX_GENERALIZED_TIME_INVALID_MONTH; |
| | | String message = getMessage(msgID, valueString, |
| | | valueString.substring(4, 6)); |
| | | throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, |
| | | message, msgID); |
| | | } |
| | | |
| | | |
| | | // 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. |
| | | char d1 = valueString.charAt(6); |
| | | 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: |
| | | int msgID = MSGID_ATTR_SYNTAX_GENERALIZED_TIME_INVALID_DAY; |
| | | String message = getMessage(msgID, valueString, |
| | | valueString.substring(6, 8)); |
| | | throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, |
| | | message, msgID); |
| | | } |
| | | 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: |
| | | int msgID = MSGID_ATTR_SYNTAX_GENERALIZED_TIME_INVALID_DAY; |
| | | String message = getMessage(msgID, valueString, |
| | | valueString.substring(6, 8)); |
| | | throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, |
| | | message, msgID); |
| | | } |
| | | 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: |
| | | int msgID = MSGID_ATTR_SYNTAX_GENERALIZED_TIME_INVALID_DAY; |
| | | String message = getMessage(msgID, valueString, |
| | | valueString.substring(6, 8)); |
| | | throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, |
| | | message, msgID); |
| | | } |
| | | break; |
| | | |
| | | case '3': |
| | | // d2 must be either 0 or 1. |
| | | switch (d2) |
| | | { |
| | | case '0': |
| | | day = 30; |
| | | break; |
| | | |
| | | case '1': |
| | | day = 31; |
| | | break; |
| | | |
| | | default: |
| | | int msgID = MSGID_ATTR_SYNTAX_GENERALIZED_TIME_INVALID_DAY; |
| | | String message = getMessage(msgID, valueString, |
| | | valueString.substring(6, 8)); |
| | | throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, |
| | | message, msgID); |
| | | } |
| | | break; |
| | | |
| | | default: |
| | | int msgID = MSGID_ATTR_SYNTAX_GENERALIZED_TIME_INVALID_DAY; |
| | | String message = getMessage(msgID, valueString, |
| | | valueString.substring(6, 8)); |
| | | throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, |
| | | message, msgID); |
| | | } |
| | | |
| | | |
| | | // The next two characters must be the hour, and they must form the string |
| | | // representation of an integer between 00 and 23. |
| | | char h1 = valueString.charAt(8); |
| | | 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: |
| | | int msgID = MSGID_ATTR_SYNTAX_GENERALIZED_TIME_INVALID_HOUR; |
| | | String message = getMessage(msgID, valueString, |
| | | valueString.substring(8, 10)); |
| | | throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, |
| | | message, msgID); |
| | | } |
| | | 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: |
| | | int msgID = MSGID_ATTR_SYNTAX_GENERALIZED_TIME_INVALID_HOUR; |
| | | String message = getMessage(msgID, valueString, |
| | | valueString.substring(8, 10)); |
| | | throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, |
| | | message, msgID); |
| | | } |
| | | 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: |
| | | int msgID = MSGID_ATTR_SYNTAX_GENERALIZED_TIME_INVALID_HOUR; |
| | | String message = getMessage(msgID, valueString, |
| | | valueString.substring(8, 10)); |
| | | throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, |
| | | message, msgID); |
| | | } |
| | | break; |
| | | |
| | | default: |
| | | int msgID = MSGID_ATTR_SYNTAX_GENERALIZED_TIME_INVALID_HOUR; |
| | | String message = getMessage(msgID, valueString, |
| | | valueString.substring(8, 10)); |
| | | throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, |
| | | message, msgID); |
| | | } |
| | | |
| | | |
| | | // 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) |
| | | { |
| | | int msgID = MSGID_ATTR_SYNTAX_GENERALIZED_TIME_INVALID_CHAR; |
| | | String message = getMessage(msgID, valueString, m1, 10); |
| | | throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, |
| | | message, msgID); |
| | | } |
| | | |
| | | |
| | | 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: |
| | | int msgID = MSGID_ATTR_SYNTAX_GENERALIZED_TIME_INVALID_MINUTE; |
| | | String message = getMessage(msgID, valueString, |
| | | valueString.substring(10, 12)); |
| | | throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, |
| | | message, msgID); |
| | | } |
| | | |
| | | break; |
| | | |
| | | case 'Z': |
| | | // This is fine only if we are at the end of the value. |
| | | if (length == 11) |
| | | { |
| | | try |
| | | { |
| | | return dateFormat.parse(valueString).getTime(); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | // We'll let this one be handled by the outer try/catch block. |
| | | throw e; |
| | | } |
| | | finally |
| | | { |
| | | dateFormatLock.unlock(); |
| | | } |
| | | GregorianCalendar calendar = new GregorianCalendar(); |
| | | calendar.setLenient(false); |
| | | calendar.setTimeZone(TimeZone.getTimeZone(TIME_ZONE_UTC)); |
| | | calendar.set(year, month, day, hour, minute, second); |
| | | calendar.set(Calendar.MILLISECOND, 0); |
| | | return calendar.getTimeInMillis(); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | |
| | | debugCaught(DebugLogLevel.ERROR, e); |
| | | } |
| | | |
| | | int msgID = MSGID_ATTR_SYNTAX_GENERALIZED_TIME_CANNOT_PARSE; |
| | | // This should only happen if the provided date wasn't legal |
| | | // (e.g., September 31). |
| | | int msgID = MSGID_ATTR_SYNTAX_GENERALIZED_TIME_ILLEGAL_TIME; |
| | | String message = getMessage(msgID, valueString, String.valueOf(e)); |
| | | |
| | | throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, |
| | | message, msgID, e); |
| | | } |
| | | } |
| | | else |
| | | { |
| | | int msgID = MSGID_ATTR_SYNTAX_GENERALIZED_TIME_INVALID_CHAR; |
| | | String message = getMessage(msgID, valueString, m1, 10); |
| | | throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, |
| | | message, msgID); |
| | | } |
| | | |
| | | 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)) |
| | | { |
| | | try |
| | | { |
| | | GregorianCalendar calendar = new GregorianCalendar(); |
| | | calendar.setLenient(false); |
| | | calendar.setTimeZone(getTimeZoneForOffset(valueString, 10)); |
| | | calendar.set(year, month, day, hour, minute, second); |
| | | calendar.set(Calendar.MILLISECOND, 0); |
| | | return calendar.getTimeInMillis(); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | debugCaught(DebugLogLevel.ERROR, e); |
| | | } |
| | | |
| | | // This should only happen if the provided date wasn't legal |
| | | // (e.g., September 31). |
| | | int msgID = MSGID_ATTR_SYNTAX_GENERALIZED_TIME_ILLEGAL_TIME; |
| | | String message = getMessage(msgID, valueString, String.valueOf(e)); |
| | | throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, |
| | | message, msgID, e); |
| | | } |
| | | } |
| | | else |
| | | { |
| | | int msgID = MSGID_ATTR_SYNTAX_GENERALIZED_TIME_INVALID_CHAR; |
| | | String message = getMessage(msgID, valueString, m1, 10); |
| | | throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, |
| | | message, msgID); |
| | | } |
| | | |
| | | case '.': |
| | | case ',': |
| | | return finishDecodingFraction(valueString, 11, year, month, day, hour, |
| | | minute, second, 3600000); |
| | | |
| | | default: |
| | | int msgID = MSGID_ATTR_SYNTAX_GENERALIZED_TIME_INVALID_CHAR; |
| | | String message = getMessage(msgID, valueString, m1, 10); |
| | | throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, |
| | | message, msgID); |
| | | } |
| | | |
| | | |
| | | // 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. |
| | | 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) |
| | | { |
| | | int msgID = MSGID_ATTR_SYNTAX_GENERALIZED_TIME_INVALID_CHAR; |
| | | String message = getMessage(msgID, valueString, s1, 12); |
| | | throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, |
| | | message, msgID); |
| | | } |
| | | |
| | | |
| | | 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: |
| | | int msgID = MSGID_ATTR_SYNTAX_GENERALIZED_TIME_INVALID_MINUTE; |
| | | String message = getMessage(msgID, valueString, |
| | | valueString.substring(12, 14)); |
| | | throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, |
| | | message, msgID); |
| | | } |
| | | |
| | | break; |
| | | |
| | | case '6': |
| | | // There must be at least two more characters and the next one must be |
| | | // a 0. |
| | | if (length < 15) |
| | | { |
| | | int msgID = MSGID_ATTR_SYNTAX_GENERALIZED_TIME_INVALID_CHAR; |
| | | String message = getMessage(msgID, valueString, s1, 12); |
| | | throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, |
| | | message, msgID); |
| | | } |
| | | |
| | | if (valueString.charAt(13) != '0') |
| | | { |
| | | int msgID = MSGID_ATTR_SYNTAX_GENERALIZED_TIME_INVALID_SECOND; |
| | | String message = getMessage(msgID, valueString, |
| | | valueString.substring(12, 14)); |
| | | throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, |
| | | message, msgID); |
| | | } |
| | | |
| | | second = 60; |
| | | break; |
| | | |
| | | case 'Z': |
| | | // This is fine only if we are at the end of the value. |
| | | if (length == 13) |
| | | { |
| | | try |
| | | { |
| | | GregorianCalendar calendar = new GregorianCalendar(); |
| | | calendar.setLenient(false); |
| | | calendar.setTimeZone(TimeZone.getTimeZone(TIME_ZONE_UTC)); |
| | | calendar.set(year, month, day, hour, minute, second); |
| | | calendar.set(Calendar.MILLISECOND, 0); |
| | | return calendar.getTimeInMillis(); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | debugCaught(DebugLogLevel.ERROR, e); |
| | | } |
| | | |
| | | // This should only happen if the provided date wasn't legal |
| | | // (e.g., September 31). |
| | | int msgID = MSGID_ATTR_SYNTAX_GENERALIZED_TIME_ILLEGAL_TIME; |
| | | String message = getMessage(msgID, valueString, String.valueOf(e)); |
| | | throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, |
| | | message, msgID, e); |
| | | } |
| | | } |
| | | else |
| | | { |
| | | int msgID = MSGID_ATTR_SYNTAX_GENERALIZED_TIME_INVALID_CHAR; |
| | | String message = getMessage(msgID, valueString, s1, 12); |
| | | throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, |
| | | message, msgID); |
| | | } |
| | | |
| | | 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)) |
| | | { |
| | | try |
| | | { |
| | | GregorianCalendar calendar = new GregorianCalendar(); |
| | | calendar.setLenient(false); |
| | | calendar.setTimeZone(getTimeZoneForOffset(valueString, 12)); |
| | | calendar.set(year, month, day, hour, minute, second); |
| | | calendar.set(Calendar.MILLISECOND, 0); |
| | | return calendar.getTimeInMillis(); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | debugCaught(DebugLogLevel.ERROR, e); |
| | | } |
| | | |
| | | // This should only happen if the provided date wasn't legal |
| | | // (e.g., September 31). |
| | | int msgID = MSGID_ATTR_SYNTAX_GENERALIZED_TIME_ILLEGAL_TIME; |
| | | String message = getMessage(msgID, valueString, String.valueOf(e)); |
| | | throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, |
| | | message, msgID, e); |
| | | } |
| | | } |
| | | else |
| | | { |
| | | int msgID = MSGID_ATTR_SYNTAX_GENERALIZED_TIME_INVALID_CHAR; |
| | | String message = getMessage(msgID, valueString, s1, 12); |
| | | throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, |
| | | message, msgID); |
| | | } |
| | | |
| | | case '.': |
| | | case ',': |
| | | return finishDecodingFraction(valueString, 13, year, month, day, hour, |
| | | minute, second, 60000); |
| | | |
| | | default: |
| | | int msgID = MSGID_ATTR_SYNTAX_GENERALIZED_TIME_INVALID_CHAR; |
| | | String message = getMessage(msgID, valueString, s1, 12); |
| | | throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, |
| | | message, msgID); |
| | | } |
| | | |
| | | |
| | | // 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) |
| | | { |
| | | try |
| | | { |
| | | GregorianCalendar calendar = new GregorianCalendar(); |
| | | calendar.setLenient(false); |
| | | calendar.setTimeZone(TimeZone.getTimeZone(TIME_ZONE_UTC)); |
| | | calendar.set(year, month, day, hour, minute, second); |
| | | calendar.set(Calendar.MILLISECOND, 0); |
| | | return calendar.getTimeInMillis(); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | debugCaught(DebugLogLevel.ERROR, e); |
| | | } |
| | | |
| | | // This should only happen if the provided date wasn't legal |
| | | // (e.g., September 31). |
| | | int msgID = MSGID_ATTR_SYNTAX_GENERALIZED_TIME_ILLEGAL_TIME; |
| | | String message = getMessage(msgID, valueString, String.valueOf(e)); |
| | | throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, |
| | | message, msgID, e); |
| | | } |
| | | } |
| | | else |
| | | { |
| | | int msgID = MSGID_ATTR_SYNTAX_GENERALIZED_TIME_INVALID_CHAR; |
| | | String message = getMessage(msgID, valueString, |
| | | valueString.charAt(14), 14); |
| | | throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, |
| | | message, msgID); |
| | | } |
| | | |
| | | 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)) |
| | | { |
| | | try |
| | | { |
| | | GregorianCalendar calendar = new GregorianCalendar(); |
| | | calendar.setLenient(false); |
| | | calendar.setTimeZone(getTimeZoneForOffset(valueString, 14)); |
| | | calendar.set(year, month, day, hour, minute, second); |
| | | calendar.set(Calendar.MILLISECOND, 0); |
| | | return calendar.getTimeInMillis(); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | debugCaught(DebugLogLevel.ERROR, e); |
| | | } |
| | | |
| | | // This should only happen if the provided date wasn't legal |
| | | // (e.g., September 31). |
| | | int msgID = MSGID_ATTR_SYNTAX_GENERALIZED_TIME_ILLEGAL_TIME; |
| | | String message = getMessage(msgID, valueString, String.valueOf(e)); |
| | | throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, |
| | | message, msgID, e); |
| | | } |
| | | } |
| | | else |
| | | { |
| | | int msgID = MSGID_ATTR_SYNTAX_GENERALIZED_TIME_INVALID_CHAR; |
| | | String message = getMessage(msgID, valueString, |
| | | valueString.charAt(14), 14); |
| | | throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, |
| | | message, msgID); |
| | | } |
| | | |
| | | default: |
| | | int msgID = MSGID_ATTR_SYNTAX_GENERALIZED_TIME_INVALID_CHAR; |
| | | String message = getMessage(msgID, valueString, valueString.charAt(14), |
| | | 14); |
| | | throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, |
| | | message, msgID); |
| | | } |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * 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 DirectoryException If the provided value cannot be parsed as a |
| | | * valid generalized time string. |
| | | */ |
| | | private static long finishDecodingFraction(String value, int startPos, |
| | | int year, int month, int day, |
| | | int hour, int minute, int second, |
| | | int multiplier) |
| | | throws DirectoryException |
| | | { |
| | | int length = value.length(); |
| | | StringBuilder fractionBuffer = new StringBuilder(2 + length - startPos); |
| | | fractionBuffer.append("0."); |
| | | |
| | | TimeZone timeZone = null; |
| | | |
| | | outerLoop: |
| | | for (int i=startPos; i < length; i++) |
| | | { |
| | | 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)) |
| | | { |
| | | int msgID = |
| | | MSGID_ATTR_SYNTAX_GENERALIZED_TIME_ILLEGAL_FRACTION_CHAR; |
| | | String message = getMessage(msgID, value, String.valueOf(c)); |
| | | throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, |
| | | message, msgID); |
| | | } |
| | | |
| | | timeZone = TimeZone.getTimeZone(TIME_ZONE_UTC); |
| | | break outerLoop; |
| | | |
| | | case '+': |
| | | case '-': |
| | | timeZone = getTimeZoneForOffset(value, i); |
| | | break outerLoop; |
| | | |
| | | default: |
| | | int msgID = MSGID_ATTR_SYNTAX_GENERALIZED_TIME_ILLEGAL_FRACTION_CHAR; |
| | | String message = getMessage(msgID, value, String.valueOf(c)); |
| | | throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, |
| | | message, msgID); |
| | | } |
| | | } |
| | | |
| | | if (fractionBuffer.length() == 2) |
| | | { |
| | | int msgID = MSGID_ATTR_SYNTAX_GENERALIZED_TIME_EMPTY_FRACTION; |
| | | String message = getMessage(msgID, value); |
| | | throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, message, |
| | | msgID); |
| | | } |
| | | |
| | | if (timeZone == null) |
| | | { |
| | | int msgID = MSGID_ATTR_SYNTAX_GENERALIZED_TIME_NO_TIME_ZONE_INFO; |
| | | String message = getMessage(msgID, value); |
| | | throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, message, |
| | | msgID); |
| | | } |
| | | |
| | | Double fractionValue = Double.parseDouble(fractionBuffer.toString()); |
| | | long additionalMilliseconds = Math.round(fractionValue * multiplier); |
| | | |
| | | try |
| | | { |
| | | 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 (Exception e) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | debugCaught(DebugLogLevel.ERROR, e); |
| | | } |
| | | |
| | | // This should only happen if the provided date wasn't legal |
| | | // (e.g., September 31). |
| | | int msgID = MSGID_ATTR_SYNTAX_GENERALIZED_TIME_ILLEGAL_TIME; |
| | | String message = getMessage(msgID, value, String.valueOf(e)); |
| | | throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, |
| | | message, msgID, e); |
| | | } |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * 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 DirectoryException If the provided value does not contain a valid |
| | | * offset. |
| | | */ |
| | | private static TimeZone getTimeZoneForOffset(String value, int startPos) |
| | | throws DirectoryException |
| | | { |
| | | String offSetStr = value.substring(startPos); |
| | | if ((offSetStr.length() != 3) && (offSetStr.length() != 5)) |
| | | { |
| | | int msgID = MSGID_ATTR_SYNTAX_GENERALIZED_TIME_INVALID_OFFSET; |
| | | String message = getMessage(msgID, value, offSetStr); |
| | | throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, message, |
| | | msgID); |
| | | } |
| | | |
| | | |
| | | // The first character must be either a plus or minus. |
| | | switch (offSetStr.charAt(0)) |
| | | { |
| | | case '+': |
| | | case '-': |
| | | // These are OK. |
| | | break; |
| | | |
| | | default: |
| | | int msgID = MSGID_ATTR_SYNTAX_GENERALIZED_TIME_INVALID_OFFSET; |
| | | String message = getMessage(msgID, value, offSetStr); |
| | | throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, |
| | | message, msgID); |
| | | } |
| | | |
| | | |
| | | // 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: |
| | | int msgID = MSGID_ATTR_SYNTAX_GENERALIZED_TIME_INVALID_OFFSET; |
| | | String message = getMessage(msgID, value, offSetStr); |
| | | throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, |
| | | message, msgID); |
| | | } |
| | | break; |
| | | |
| | | case '2': |
| | | switch (offSetStr.charAt(2)) |
| | | { |
| | | case '0': |
| | | case '1': |
| | | case '2': |
| | | case '3': |
| | | // These are all fine. |
| | | break; |
| | | |
| | | default: |
| | | int msgID = MSGID_ATTR_SYNTAX_GENERALIZED_TIME_INVALID_OFFSET; |
| | | String message = getMessage(msgID, value, offSetStr); |
| | | throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, |
| | | message, msgID); |
| | | } |
| | | break; |
| | | |
| | | default: |
| | | int msgID = MSGID_ATTR_SYNTAX_GENERALIZED_TIME_INVALID_OFFSET; |
| | | String message = getMessage(msgID, value, offSetStr); |
| | | throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, |
| | | message, msgID); |
| | | } |
| | | |
| | | |
| | | // 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: |
| | | int msgID = MSGID_ATTR_SYNTAX_GENERALIZED_TIME_INVALID_OFFSET; |
| | | String message = getMessage(msgID, value, offSetStr); |
| | | throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, |
| | | message, msgID); |
| | | } |
| | | break; |
| | | |
| | | default: |
| | | int msgID = MSGID_ATTR_SYNTAX_GENERALIZED_TIME_INVALID_OFFSET; |
| | | String message = getMessage(msgID, value, offSetStr); |
| | | throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, |
| | | message, msgID); |
| | | } |
| | | } |
| | | |
| | | |
| | | // 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); |
| | | } |
| | | } |
| | | |
| | |
| | | {"2006090613mmZ"}, |
| | | {"20060906135030.011"}, |
| | | {"20060906135030Zx"}, |
| | | {"20060906135030.Z"}, |
| | | {"20060906135030.aZ"}, |
| | | {"20060906135030"}, |
| | | {"20060906135030.123"}, |
| | | {"20060906135030-2500"}, |
| | | {"20060906135030-2070"}, |
| | | {"20060931135030Z"}, |
| | | {"20060229135030Z"}, |
| | | {"20060230135030Z"}, |
| | | }; |
| | | } |
| | | |
| | |
| | | {"20060906135030Z", "20060906135030.000Z", true}, |
| | | {"20060906135030.3Z", "20060906135030.300Z", true}, |
| | | {"20060906135030.30Z", "20060906135030.300Z", true}, |
| | | {"20060906135030.Z", "20060906135030.000Z", true}, |
| | | {"20060906135030Z", "20060906135030.000Z", true}, |
| | | {"20060906135030.0Z", "20060906135030.000Z", true}, |
| | | {"20060906135030.0118Z", "20060906135030.012Z", true}, |
| | | {"20060906135030+01", "20060906125030.000Z", true}, |
| | | {"20060906135030+0101", "20060906124930.000Z", true}, |
| | | {"20070417055812.318-0500", "20070417105812.318Z", true}, |
| | | {"2007041705.5Z", "20070417053000.000Z", true}, |
| | | {"200704170558.5Z", "20070417055830.000Z", true}, |
| | | }; |
| | | } |
| | | |
| | |
| | | {"20060912180129.1hZ"}, |
| | | {"20060906135030+aa01"}, |
| | | {"2006"}, |
| | | /* disabled because these tests are failing |
| | | * see issue 675 |
| | | * {"20060906135030+3359"}, |
| | | * {"20060906135030+2389"}, |
| | | * {"20060906135030+2361"},*/ |
| | | {"20060906135030+3359"}, |
| | | {"20060906135030+2389"}, |
| | | {"20060906135030+2361"}, |
| | | {"20060906135030+"}, |
| | | {"20060906135030+0"}, |
| | | {"20060906135030+010"}, |