/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at
* trunk/opends/resource/legal-notices/OpenDS.LICENSE
* or https://OpenDS.dev.java.net/OpenDS.LICENSE.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at
* trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable,
* add the following below this CDDL HEADER, with the fields enclosed
* by brackets "[]" replaced with your own identifying * information:
* Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*
*
* Portions Copyright 2006 Sun Microsystems, Inc.
*/
package org.opends.server.schema;
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.api.OrderingMatchingRule;
import org.opends.server.config.ConfigEntry;
import org.opends.server.config.ConfigException;
import org.opends.server.core.DirectoryException;
import org.opends.server.core.DirectoryServer;
import org.opends.server.core.InitializationException;
import org.opends.server.protocols.asn1.ASN1OctetString;
import org.opends.server.types.AcceptRejectWarn;
import org.opends.server.types.ByteString;
import org.opends.server.types.ErrorLogCategory;
import org.opends.server.types.ErrorLogSeverity;
import org.opends.server.types.ResultCode;
import static org.opends.server.loggers.Debug.*;
import static org.opends.server.loggers.Error.*;
import static org.opends.server.messages.MessageHandler.*;
import static org.opends.server.messages.SchemaMessages.*;
import static org.opends.server.schema.SchemaConstants.*;
import static org.opends.server.util.ServerConstants.*;
import static org.opends.server.util.StaticUtils.*;
/**
* This class defines the generalizedTimeOrderingMatch matching rule defined in
* X.520 and referenced in RFC 2252.
*/
public class GeneralizedTimeOrderingMatchingRule
extends OrderingMatchingRule
{
/**
* The fully-qualified name of this class for debugging purposes.
*/
private static final String CLASS_NAME =
"org.opends.server.schema.GeneralizedTimeOrderingMatchingRule";
/**
* The serial version identifier required to satisfy the compiler because this
* class implements the java.io.Serializable interface. This
* value was generated using the serialver command-line utility
* included with the Java SDK.
*/
private static final long serialVersionUID = -6343622924726948145L;
/**
* The lock that will be used to provide threadsafe access to the date
* formatter.
*/
private static ReentrantLock dateFormatLock;
/**
* The date formatter that will be used to convert dates into generalized time
* values. Note that all interaction with it must be synchronized.
*/
private static SimpleDateFormat dateFormat;
/**
* The time zone used for UTC time.
*/
private static TimeZone utcTimeZone;
/*
* Create the date formatter that will be used to construct and parse
* normalized generalized time values.
*/
static
{
utcTimeZone = TimeZone.getTimeZone("UTC");
dateFormat = new SimpleDateFormat(DATE_FORMAT_GENERALIZED_TIME);
dateFormat.setLenient(false);
dateFormat.setTimeZone(utcTimeZone);
dateFormatLock = new ReentrantLock();
}
/**
* Creates a new instance of this generalizedTimeMatch matching rule.
*/
public GeneralizedTimeOrderingMatchingRule()
{
super();
assert debugConstructor(CLASS_NAME);
}
/**
* Initializes this matching rule based on the information in the provided
* configuration entry.
*
* @param configEntry The configuration entry that contains the information
* to use to initialize this matching rule.
*
* @throws ConfigException If an unrecoverable problem arises in the
* process of performing the initialization.
*
* @throws InitializationException If a problem that is not
* configuration-related occurs during
* initialization.
*/
public void initializeMatchingRule(ConfigEntry configEntry)
throws ConfigException, InitializationException
{
assert debugEnter(CLASS_NAME, "initializeMatchingRule",
String.valueOf(configEntry));
// No initialization is required.
}
/**
* Retrieves the common name for this matching rule.
*
* @return The common name for this matching rule, or null if
* it does not have a name.
*/
public String getName()
{
assert debugEnter(CLASS_NAME, "getName");
return OMR_GENERALIZED_TIME_NAME;
}
/**
* Retrieves the OID for this matching rule.
*
* @return The OID for this matching rule.
*/
public String getOID()
{
assert debugEnter(CLASS_NAME, "getOID");
return OMR_GENERALIZED_TIME_OID;
}
/**
* Retrieves the description for this matching rule.
*
* @return The description for this matching rule, or null if
* there is none.
*/
public String getDescription()
{
assert debugEnter(CLASS_NAME, "getDescription");
// There is no standard description for this matching rule.
return null;
}
/**
* Retrieves the OID of the syntax with which this matching rule is
* associated.
*
* @return The OID of the syntax with which this matching rule is associated.
*/
public String getSyntaxOID()
{
assert debugEnter(CLASS_NAME, "getSyntaxOID");
return SYNTAX_GENERALIZED_TIME_OID;
}
/**
* Retrieves the normalized form of the provided value, which is best suited
* for efficiently performing matching operations on that value.
*
* @param value The value to be normalized.
*
* @return The normalized version of the provided value.
*
* @throws DirectoryException If the provided value is invalid according to
* the associated attribute syntax.
*/
public ByteString normalizeValue(ByteString value)
throws DirectoryException
{
assert debugEnter(CLASS_NAME, "normalizeValue", String.valueOf(value));
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)
{
int msgID = MSGID_ATTR_SYNTAX_GENERALIZED_TIME_TOO_SHORT;
String message = getMessage(msgID, valueString);
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 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)
{
assert debugException(CLASS_NAME, "normalizeValue", 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)
{
assert debugException(CLASS_NAME, "normalizeValue", 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)
{
assert debugException(CLASS_NAME, "normalizeValue", 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)
{
assert debugException(CLASS_NAME, "normalizeValue", 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)
{
assert debugException(CLASS_NAME, "normalizeValue", 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
{
assert debugEnter(CLASS_NAME, "processValueEnd",
new String[]
{
String.valueOf(valueString),
String.valueOf(endPos),
String.valueOf(year),
String.valueOf(month),
String.valueOf(day),
String.valueOf(hour),
String.valueOf(minute),
String.valueOf(second),
String.valueOf(millisecond)
});
// 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)
{
assert debugException(CLASS_NAME, "processValueEnd", 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)
{
assert debugException(CLASS_NAME, "processValueEnd", 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)
{
assert debugException(CLASS_NAME, "processValueEnd", 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)
{
assert debugException(CLASS_NAME, "processValueEnd", 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)
{
assert debugException(CLASS_NAME, "processValueEnd", 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)
{
assert debugException(CLASS_NAME, "processValueEnd", 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)
{
assert debugException(CLASS_NAME, "processValueEnd", 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();
}
}
}
/**
* Compares the first value to the second and returns a value that indicates
* their relative order.
*
* @param value1 The normalized form of the first value to compare.
* @param value2 The normalized form of the second value to compare.
*
* @return A negative integer if value1 should come before
* value2 in ascending order, a positive integer if
* value1 should come after value2 in
* ascending order, or zero if there is no difference between the
* values with regard to ordering.
*/
public int compareValues(ByteString value1, ByteString value2)
{
assert debugEnter(CLASS_NAME, "compareValues", String.valueOf(value1),
String.valueOf(value2));
return compare(value1.value(), value2.value());
}
/**
* Compares the contents of the provided byte arrays to determine their
* relative order.
*
* @param b1 The first byte array to use in the comparison.
* @param b2 The second byte array to use in the comparison.
*
* @return A negative integer if b1 should come before
* b2 in ascending order, a positive integer if
* b1 should come after b2 in ascending
* order, or zero if there is no difference between the values with
* regard to ordering.
*/
public int compare(byte[] b1, byte[] b2)
{
assert debugEnter(CLASS_NAME, "compare", String.valueOf(b1),
String.valueOf(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;
}
}
}