/*
* @(#)$Id: NumberType.java,v 1.19 2002/10/08 22:01:27 kk122374 Exp $
*
* Copyright 2001 Sun Microsystems, Inc. All Rights Reserved.
*
* This software is the proprietary information of Sun Microsystems, Inc.
* Use is subject to license terms.
*
*/
package com.sun.msv.datatype.xsd;
import java.math.BigDecimal;
import java.math.BigInteger;
import org.relaxng.datatype.ValidationContext;
import com.sun.msv.datatype.SerializationContext;
/**
* "decimal" type.
*
* type of the value object is <code>java.math.BigDecimal</code>.
* See http://www.w3.org/TR/xmlschema-2/#decimal for the spec.
* It was once known as "number" type.
*
* @author <a href="mailto:kohsuke.kawaguchi@eng.sun.com">Kohsuke KAWAGUCHI</a>
*/
public class NumberType extends BuiltinAtomicType implements Comparator {
public static final NumberType theInstance = new NumberType();
private NumberType() { super("decimal"); }
final public XSDatatype getBaseType() {
return SimpleURType.theInstance;
}
/** constant */
private static final BigInteger the10 = new BigInteger("10");
protected boolean checkFormat( String content, ValidationContext context ) {
final int len = content.length();
int i=0;
char ch;
boolean atLeastOneDigit = false;
if(len==0) return false; // length 0 is not allowed
// leading optional sign
ch = content.charAt(0);
if(ch=='-' || ch=='+') i++;
while(i<len) {
ch = content.charAt(i++);
if('0'<=ch && ch<='9') {
atLeastOneDigit = true;
continue;
}
if(ch=='.') break;
return false; // other characters are error
}
while(i<len) {
// fractional part
ch = content.charAt(i++);
if('0'<=ch && ch<='9') {
atLeastOneDigit = true;
continue;
}
return false; // other characters are error
}
return atLeastOneDigit; // at least one digit must be present.
}
public Object _createValue( String content, ValidationContext context ) {
// BigDecimal accepts expressions like "1E4",
// but XML Schema doesn't.
// so call checkFormat to make sure that
// format is XML Schema spec compliant.
if(!checkFormat(content,context)) return null;
return load(content);
}
public static BigDecimal load( String content ) {
try {
// XML Schema allows optional leading '+' sign,
// but BigDecimal doesn't.
// so remove it here.
if( content.length()==0 ) return null;
if( content.charAt(0)=='+' )
content = content.substring(1);
BigDecimal r = new BigDecimal(content);
// BigDecimal treats 0 != 0.0
// to workaround this, "normalize" BigDecimal;
// that is, trailing zeros in fractional digits are removed.
while(r.scale()>0) {
BigInteger[] q_r =
r.unscaledValue().divideAndRemainder(the10);
if( !q_r[1].equals(BigInteger.ZERO) ) break;
r = new BigDecimal(q_r[0], r.scale()-1);
}
return r;
} catch( NumberFormatException e ) {
return null;
}
}
public static String save( Object o ) {
return ((BigDecimal)o).toString();
}
public Class getJavaObjectType() {
return BigDecimal.class;
}
public String convertToLexicalValue( Object o, SerializationContext context ) {
if(o instanceof BigDecimal)
return o.toString();
else
throw new IllegalArgumentException();
}
public final int isFacetApplicable( String facetName ) {
if( facetName.equals(FACET_TOTALDIGITS)
|| facetName.equals(FACET_FRACTIONDIGITS)
|| facetName.equals(FACET_PATTERN)
|| facetName.equals(FACET_ENUMERATION)
|| facetName.equals(FACET_WHITESPACE)
|| facetName.equals(FACET_MAXINCLUSIVE)
|| facetName.equals(FACET_MININCLUSIVE)
|| facetName.equals(FACET_MAXEXCLUSIVE)
|| facetName.equals(FACET_MINEXCLUSIVE) )
return APPLICABLE;
else
return NOT_ALLOWED;
}
public final int compare( Object o1, Object o2 ) {
final int r = ((Comparable)o1).compareTo(o2);
if(r<0) return LESS;
if(r>0) return GREATER;
return EQUAL;
}
}