/*
* The contents of this file are subject to the Mozilla Public License
* Version 1.1 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS"
* basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
* the License for the specific language governing rights and limitations
* under the License.
*
* The Original Code is the Kowari Metadata Store.
*
* The Initial Developer of the Original Code is Plugged In Software Pty
* Ltd (http://www.pisoftware.com, mailto:info@pisoftware.com). Portions
* created by Plugged In Software Pty Ltd are Copyright (C) 2001,2002
* Plugged In Software Pty Ltd. All Rights Reserved.
*
* Contributor(s): N/A.
*
* [NOTE: The text of this Exhibit A may differ slightly from the text
* of the notices in the Source Code files of the Original Code. You
* should use the text of this Exhibit A rather than the text found in the
* Original Code Source Code for Your Modifications.]
*
*/
package org.mulgara.store.stringpool.xa;
// Java 2 standard packages
import java.math.BigDecimal;
import java.net.URI;
import java.nio.ByteBuffer;
// Third party packages
import org.apache.log4j.Logger;
// Locally written packages
import org.mulgara.store.stringpool.*;
import org.mulgara.util.Constants;
/**
* An SPTypedLiteral that represents xsd:decimal literals.
* Data is encoded as a string or a long. If the data is the length of a long, then
* it is a long. If it is one more, then it could be a normal string, or a string
* that was padded to not look like a long.
*
* @created 2004-10-05
* @author David Makepeace
* @company <A href="mailto:info@PIsoftware.com">Plugged In Software</A>
* @copyright © 2004 <A href="http://www.PIsoftware.com/">Plugged In Software Pty Ltd</A>
* @licence <a href="{@docRoot}/../../LICENCE">Mozilla Public License v1.1</a>
*/
public abstract class SPDecimalImpl extends AbstractSPTypedLiteral implements SPNumber {
@SuppressWarnings("unused")
private final static Logger logger = Logger.getLogger(SPDecimalImpl.class);
/** An ID used for all Decimal types */
static final int TYPE_ID = 2; // Unique ID
/** The offset into the byte buffer to find out the encoding used for this data. */
static final int END_IDX = Constants.SIZEOF_LONG;
/**
* Common abstract constructor for all decimal types.
* @param subtypeId The subtype, this is the ID for either the full or abbreviated URI.
* @param typeURI The full URI for the data type.
*/
SPDecimalImpl(int subtypeId, URI typeURI) {
super(TYPE_ID, subtypeId, typeURI);
}
/**
* Reads a byte buffer and converts to a BigDecimal, regardless of formatting.
* @param bb The byte buffer to decode.
* @return A BigDecimal representing the stored number.
*/
static BigDecimal decode(ByteBuffer bb) {
ByteBuffer number = bb;
int limit = bb.limit();
if (limit == Constants.SIZEOF_LONG) return BigDecimal.valueOf(bb.getLong());
if (limit == END_IDX + 1) {
// is this buffer padded?
byte terminator = bb.get(END_IDX);
if (terminator == SPDecimalBaseImpl.END_BYTE) {
// remove the padding
bb.limit(END_IDX);
number = bb.slice();
}
}
return new BigDecimal(CHARSET.decode(number).toString());
}
}
/**
* This class represents xsd:decimal.
* The data format is as a string. If the string is exactly the length of a Long
* (the other format available) then it will be incremented by 1, and a -1 byte appended.
*/
class SPDecimalBaseImpl extends SPDecimalImpl {
/**
* The terminating byte, indicating the type of this buffer. Only needed if the buffer
* is the same length as a long.
*/
static final byte END_BYTE = -1;
/** The value of the data. */
final BigDecimal val;
/** The string representation of the data. */
final String lexical;
/**
* Creates an xsd:decimal out of a long.
* @param subtypeId The ID for either the full or abbreviated xsd:decimal.
* @param typeURI The full or abbreviated URI for xsd:decimal.
* @param l The long value to store.
*/
SPDecimalBaseImpl(int subtypeId, URI typeURI, long l) {
super(subtypeId, typeURI);
lexical = Long.toString(l);
val = new BigDecimal(l);
}
/**
* Creates an xsd:decimal out of a BigDecimal.
* @param subtypeId The ID for either the full or abbreviated xsd:decimal.
* @param typeURI The full or abbreviated URI for xsd:decimal.
* @param bd The BigDecimal value to store.
*/
SPDecimalBaseImpl(int subtypeId, URI typeURI, BigDecimal bd) {
super(subtypeId, typeURI);
lexical = bd.toPlainString();
val = bd;
}
/**
* Creates an xsd:decimal by decoding from a data buffer.
* @param subtypeId The ID for either the full or abbreviated xsd:decimal.
* @param typeURI The full or abbreviated URI for xsd:decimal.
* @param data The data containing the xsd:decimal value.
*/
SPDecimalBaseImpl(int subtypeId, URI typeURI, ByteBuffer data) {
super(subtypeId, typeURI);
if (data.limit() == Constants.SIZEOF_LONG) throw new IllegalArgumentException("Buffer does not hold a decimal.");
ByteBuffer number = data;
if (data.limit() == END_IDX + 1) {
if (data.get(END_IDX) == END_BYTE) {
data.limit(END_IDX);
number = data.slice();
}
}
lexical = CHARSET.decode(number).toString();
val = new BigDecimal(lexical);
}
/**
* Creates an xsd:decimal by decoding from a string.
* @param subtypeId The ID for either the full or abbreviated xsd:decimal.
* @param typeURI The full or abbreviated URI for xsd:decimal.
* @param lexicalForm The string containing the xsd:decimal value.
*/
SPDecimalBaseImpl(int subtypeId, URI typeURI, String lexicalForm) {
super(subtypeId, typeURI);
this.lexical = lexicalForm;
val = new BigDecimal(lexicalForm);
}
/** @see org.mulgara.store.stringpool.SPObject#getData() */
public ByteBuffer getData() {
ByteBuffer data = CHARSET.encode(lexical);
// if this is the same size as a long, expand it and pad it
if (data.limit() == END_IDX) {
ByteBuffer newData = ByteBuffer.allocate(END_IDX + 1);
newData.put(data);
newData.put(END_IDX, END_BYTE);
newData.rewind();
data = newData;
}
return data;
}
/** @see org.mulgara.store.stringpool.SPObject#getSPComparator() */
public SPComparator getSPComparator() {
return SPDecimalBaseComparator.getInstance();
}
/** @see org.mulgara.store.stringpool.SPObject#getLexicalForm() */
public String getLexicalForm() {
return lexical;
}
/** @see org.mulgara.store.stringpool.AbstractSPTypedLiteral#compareTo(org.mulgara.store.stringpool.SPObject) */
public int compareTo(SPObject o) {
// Compare types.
int c = super.compareTo(o);
if (c != 0) return c;
// Compare the longs.
if (o instanceof SPDecimalExtImpl) {
long ol = ((SPDecimalExtImpl)o).l;
return val.compareTo(BigDecimal.valueOf(ol));
}
SPDecimalBaseImpl di = (SPDecimalBaseImpl)o;
return val.compareTo(di.val);
}
/**
* Indicates if this object is a number.
* @return <code>true</code> since this object is a number.
*/
public boolean isNumber() {
return true;
}
/**
* @see org.mulgara.store.stringpool.AbstractSPObject#numericalCompare(org.mulgara.store.stringpool.SPObject)
*/
public int numericalCompare(SPObject o) {
if (!o.isNumber()) return compareTo(o);
return -((SPNumber)o).numericalCompareTo(val);
}
/**
* @see org.mulgara.store.stringpool.xa.SPNumber#numericalCompareTo(java.math.BigDecimal)
*/
public int numericalCompareTo(BigDecimal n) {
return val.compareTo(n);
}
/**
* @see org.mulgara.store.stringpool.xa.SPNumber#numericalCompareTo(double)
*/
public int numericalCompareTo(double d) {
return val.compareTo(new BigDecimal(d));
}
/**
* @see org.mulgara.store.stringpool.xa.SPNumber#numericalCompareTo(long)
*/
public int numericalCompareTo(long l) {
return val.compareTo(new BigDecimal(l));
}
/** @see java.lang.Object#hashCode() */
public int hashCode() {
return lexical.hashCode();
}
/** @see java.lang.Object#equals(java.lang.Object) */
public boolean equals(Object obj) {
// Check for null.
if (obj == null) return false;
try {
SPDecimalBaseImpl di = (SPDecimalBaseImpl)obj;
return lexical.equals(di.lexical);
} catch (ClassCastException ex) {
// obj was not an SPDecimalImpl.
return false;
}
}
/** Compares the binary representations of two SPDecimalBaseImpl objects. */
public static class SPDecimalBaseComparator implements SPComparator {
/** The singleton instance of this class. */
private static final SPDecimalBaseComparator INSTANCE = new SPDecimalBaseComparator();
/**
* @return The singleton instance of this class.
*/
public static SPDecimalBaseComparator getInstance() {
return INSTANCE;
}
/**
* @see org.mulgara.store.stringpool.SPComparator#comparePrefix(java.nio.ByteBuffer, java.nio.ByteBuffer, int)
* @return Always 0, since this cannot compare on prefixes alone.
*/
public int comparePrefix(ByteBuffer d1, ByteBuffer d2, int d2Size) {
return 0;
}
/**
* This comparator WILL compare between xsd:decimal and the extending types
* @see org.mulgara.store.stringpool.SPComparator#compare(ByteBuffer, int, ByteBuffer, int)
*/
public int compare(ByteBuffer d1, int subtypeId1, ByteBuffer d2, int subtypeId2) {
int c = decode(d1).compareTo(decode(d2));
if (c == 0) c = AbstractSPObject.compare(subtypeId1, subtypeId2);
return c;
}
}
}
/**
* This class represents extensions of xsd:decimal.
* The data format is as a long, followed by a byte marker set to 0. This is to
* distinguish the data format from xsd:decimal, which is stored as a string.
*/
class SPDecimalExtImpl extends SPDecimalImpl {
/** The long value containing the number. */
final long l;
/**
* Creates an xsd:decimal extension out of a long.
* @param subtypeId The ID for either the full or abbreviated URI.
* @param typeURI The full or abbreviated URI.
* @param l The long value to store.
*/
SPDecimalExtImpl(int subtypeId, URI typeURI, long l) {
super(subtypeId, typeURI);
this.l = l;
}
/**
* Creates an xsd:decimal extension out of a buffer.
* @param subtypeId The ID for either the full or abbreviated URI.
* @param typeURI The full or abbreviated URI.
* @param data The buffer containing the data encoding the value.
*/
SPDecimalExtImpl(int subtypeId, URI typeURI, ByteBuffer data) {
super(subtypeId, typeURI);
assert isLong(data);
l = data.getLong();
}
/**
* Creates an xsd:decimal extension out of a string.
* @param subtypeId The ID for either the full or abbreviated URI.
* @param typeURI The full or abbreviated URI.
* @param lexicalForm The string containing the value.
*/
SPDecimalExtImpl(int subtypeId, URI typeURI, String lexicalForm) {
super(subtypeId, typeURI);
if (lexicalForm.startsWith("+")) lexicalForm = lexicalForm.substring(1);
l = Long.valueOf(lexicalForm);
}
/** @see org.mulgara.store.stringpool.SPObject#getData() */
public ByteBuffer getData() {
ByteBuffer data = ByteBuffer.allocate(Constants.SIZEOF_LONG);
data.putLong(l);
data.flip();
return data;
}
/** @see org.mulgara.store.stringpool.SPObject#getSPComparator() */
public SPComparator getSPComparator() {
return SPDecimalExtComparator.getInstance();
}
/** @see org.mulgara.store.stringpool.SPObject#getLexicalForm() */
public String getLexicalForm() {
return Long.toString(l);
}
/** @see org.mulgara.store.stringpool.AbstractSPTypedLiteral#compareTo(org.mulgara.store.stringpool.SPObject) */
public int compareTo(SPObject o) {
// Compare types.
int c = super.compareTo(o);
if (c != 0) return c;
// The super will have returned a value already, but just in case we need to compare
// values between different types, we convert.
if (o instanceof SPDecimalBaseImpl) {
return BigDecimal.valueOf(l).compareTo(((SPDecimalBaseImpl)o).val);
}
// if this is an unparseable value, then hard code it to the bottom of the type ordering
if (o instanceof UnknownSPTypedLiteralImpl) return -1;
// Compare the longs.
SPDecimalExtImpl di = (SPDecimalExtImpl)o;
return compare(l, di.l);
}
/** @see java.lang.Object#hashCode() */
public int hashCode() {
return (int)(l * 7) | (int)(l >> 32) ^ subtypeId;
}
/** @see java.lang.Object#equals(java.lang.Object) */
public boolean equals(Object obj) {
// Check for null.
if (obj == null) return false;
try {
SPDecimalExtImpl di = (SPDecimalExtImpl)obj;
return l == di.l && subtypeId == di.subtypeId;
} catch (ClassCastException ex) {
// obj was not an SPDecimalExtImpl.
return false;
}
}
/**
* Tests a buffer to see if if contains a long value. It must be the correct length
* and it must have the correct byte in the last place.
* @param d The buffer to test.
* @return <code>true</code> if the buffer contains a long value.
*/
static final boolean isLong(ByteBuffer d) {
return d.limit() == Constants.SIZEOF_LONG;
}
/** Compares the binary representations of two SPDecimalExtImpl objects. */
public static class SPDecimalExtComparator implements SPComparator {
/** The singleton instance of this object. */
private static final SPDecimalExtComparator INSTANCE = new SPDecimalExtComparator();
/** @return The singleton instance of this object. */
public static SPDecimalExtComparator getInstance() {
return INSTANCE;
}
/**
* @return Always 0, since all data is needed for comparing on this class.
* @see org.mulgara.store.stringpool.SPComparator#comparePrefix(java.nio.ByteBuffer, java.nio.ByteBuffer, int)
*/
public int comparePrefix(ByteBuffer d1, ByteBuffer d2, int d2Size) {
return 0;
}
/**
* This comparator WILL compare between xsd:decimal and the extending types
* @see org.mulgara.store.stringpool.SPComparator#compare(ByteBuffer, int, ByteBuffer, int)
*/
public int compare(ByteBuffer d1, int subtypeId1, ByteBuffer d2, int subtypeId2) {
int c;
if (isLong(d1) && isLong(d2)) {
c = SPDecimalExtImpl.compare(d1.getLong(), d2.getLong());
} else {
c = decode(d1).compareTo(decode(d2));
}
if (c == 0) c = AbstractSPObject.compare(subtypeId1, subtypeId2);
return c;
}
}
/**
* Indicates if this object is a number.
* @return <code>true</code> since this object is a number.
*/
public boolean isNumber() {
return true;
}
/**
* @see org.mulgara.store.stringpool.AbstractSPObject#numericalCompare(org.mulgara.store.stringpool.SPObject)
*/
public int numericalCompare(SPObject o) {
if (!o.isNumber()) return compareTo(o);
return -((SPNumber)o).numericalCompareTo(l);
}
public int numericalCompareTo(BigDecimal n) {
return new BigDecimal(l).compareTo(n);
}
public int numericalCompareTo(double d) {
return Double.compare((double)l, d);
}
public int numericalCompareTo(long l) {
return this.l < l ? -1 : (this.l > l ? 1 : 0);
}
}