at.spardat.xma.mdl.Atom Maven / Gradle / Ivy
/*******************************************************************************
* Copyright (c) 2003, 2007 s IT Solutions AT Spardat GmbH .
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* s IT Solutions AT Spardat GmbH - initial API and implementation
*******************************************************************************/
// @(#) $Id: Atom.java 2298 2008-01-30 08:49:08Z s3460 $
package at.spardat.xma.mdl;
import java.io.IOException;
import java.math.BigDecimal;
import java.sql.Timestamp;
import java.text.SimpleDateFormat;
import java.util.Date;
import at.spardat.enterprise.fmt.IFmt;
import at.spardat.enterprise.util.DateUtil;
import at.spardat.enterprise.util.NumberUtil;
import at.spardat.enterprise.util.StringUtil;
import at.spardat.enterprise.util.TimeStampUtil;
import at.spardat.enterprise.util.Types;
import at.spardat.xma.mdl.util.DNode;
import at.spardat.xma.mdl.util.Descriptive;
import at.spardat.xma.serializer.XmaInput;
import at.spardat.xma.serializer.XmaOutput;
import at.spardat.xma.test.TestUtil;
/**
* This class is an immutable implementation of the {@link IAtomic} interface.
* Sinces is immutable, this class provides a set of type specific constructors
* and a set of static objects that may be used for empty Atom of the
* various types.
*
* @author YSD, 12.04.2003 22:37:08
*/
public class Atom implements Comparable, Descriptive, IAtomic {
/**
* Empty Atom of type T_STRING
*/
public static final Atom EMPTY_STRING = new Atom (Types.T_STRING);
/**
* Empty Atom of type T_BCD
*/
public static final Atom EMPTY_BCD = new Atom (Types.T_BCD);
/**
* Empty Atom of type T_DATE
*/
public static final Atom EMPTY_DATE = new Atom (Types.T_DATE);
/**
* Empty Atom of type T_TIMESTAMP
*/
public static final Atom EMPTY_TIMESTAMP = new Atom (Types.T_TIMESTAMP);
/**
* Empty Atom of type T_BOOLEAN
*/
public static final Atom EMPTY_BOOLEAN = new Atom (Types.T_BOOLEAN);
/**
* Empty Atom of type T_DOM
*/
public static final Atom EMPTY_DOM = new Atom (Types.T_DOM);
/**
* One of the type codes in enterprise.util.Types.
*/
protected byte type_;
/**
* A string encoded value of a particular type. This instance is never null.
*/
protected String value_ = "";
/**
* Constructor for internal use. Do not call it!!!!
*/
public Atom () {
}
/**
* Internal constructor to get an empty Atom.
*
* @param type see {@link at.spardat.enterprise.util.Types Types}
*/
private Atom (byte type) {
type_ = type;
}
/**
* Constructs an Atom with type T_STRING and the provided value.
*
* @param s the value to set
* @exception IllegalArgumentException if argument null
*/
public Atom (String s) {
if (s == null) throw new IllegalArgumentException();
type_ = Types.T_STRING;
value_ = s;
if (s == null) value_ = "";
}
/**
* Constructs an atom by providing its instance variables.
*
* You may only use this method if you are working with formatters
* at.spardat.enterprise.fmt.IFmt and need to construct
* an Atom from an formatter-internal encoding.
*
* @param type the type code
* @param internalValue the internally stored string encoding of the type.
* @return newly created Atom
*/
public static Atom newInstance (byte type, String internalValue) {
Atom toReturn = new Atom (type);
toReturn.value_ = internalValue;
return toReturn;
}
/**
* Constructs an atom from various java types.
*
* @param o must be of the types String, Boolean, Folat, Double, Date,
* Byte, Short, Integer, Long
* @return Atom of type T_STRING, T_BOOLEAN, T_BCD or T_DATE
* @exception IllegalArgumentException if Object is of unsupported type
*/
public static Atom newInstance (Object o) {
if (o == null) throw new IllegalArgumentException();
if (o instanceof String) return new Atom ((String)o);
if (o instanceof Boolean) return new Atom (((Boolean)o).booleanValue());
if (o instanceof Double) return new Atom (((Double)o).doubleValue());
if (o instanceof Timestamp) return new Atom (Types.T_TIMESTAMP, (Timestamp)o);
if (o instanceof Date) return new Atom (Types.T_DATE, (Date)o);
if (o instanceof Byte) return new Atom ((int)(((Byte)o).byteValue())); // important: do not call the byte constructor; that just sets the type
if (o instanceof Short) return new Atom (((Short)o).shortValue());
if (o instanceof Integer) return new Atom (((Integer)o).intValue());
if (o instanceof Long) return new Atom (((Long)o).longValue());
if (o instanceof Float) return new Atom (((Float)o).doubleValue());
if (o instanceof BigDecimal) return new Atom (((BigDecimal)o));
if (o instanceof Atom) return (Atom)o;
throw new IllegalArgumentException(o.getClass().getName());
}
/**
* Randomly contructs an Atom and returns it.
*/
public static Atom newRandomInstance () {
int type = TestUtil.randomInt(0, 4);
switch (type) {
case 0:
if (TestUtil.draw(0.1)) return EMPTY_STRING;
else return new Atom (TestUtil.randomString(TestUtil.randomInt(0, 10)));
case 1:
if (TestUtil.draw(0.1)) return EMPTY_BOOLEAN;
if (TestUtil.draw(0.5)) return new Atom (true);
return new Atom(false);
case 2:
if (TestUtil.draw(0.1)) return EMPTY_BCD;
if (TestUtil.draw(0.2)) return new Atom (0.0);
else return new Atom (TestUtil.randomDouble(-100000.0, 100000.0) / Math.pow(10.0, TestUtil.randomInt(0, 5)));
case 3:
if (TestUtil.draw(0.1)) return EMPTY_DATE;
else return new Atom (Types.T_DATE, new java.util.Date());
case 4:
if (TestUtil.draw(0.1)) return EMPTY_TIMESTAMP;
else return new Atom (Types.T_TIMESTAMP, new java.util.Date());
}
return null;
}
/**
* Constructs a T_BCD Atom.
*
* @param value the numeric value to set.
* @param numNK number of digits after the comma to be used
* when the provided double is converted to a String
* (which has to be done since Atoms are
* string encoded). If numNK
* equals -1, the number of the resulting places
* after the comma is unknown.
*/
public Atom (double value, int numNK) {
type_ = Types.T_BCD;
if (Double.isInfinite(value) || Double.isNaN(value)) value_ = "";
else {
BigDecimal bigDec = new BigDecimal(value);
if (numNK != -1 && numNK >= 0) {
// round the value to numNK places after the comma
bigDec = bigDec.setScale(numNK, BigDecimal.ROUND_HALF_UP);
value_ = bigDec.toString();
} else {
value_ = NumberUtil.double2String(value);
}
}
}
/**
* Constructs a T_BCD Atom from a double. The resulting value
* of this is empty if value is NaN or Infinity.
* If the provided value cannot trivially be converted to
* a string, because its numeric (binary coded) value would
* result in a very long (decimally coded) String,
* digits after the comma are reduced so that the total number of
* significant digits does not exceed 15 digits.
*
* @param value the input double value
*/
public Atom (double value) {
type_ = Types.T_BCD;
value_ = NumberUtil.double2String(value);
}
/**
* Constructs an T_BCD Atom from an BigDecimal.
*
* @param value the BigDecimal providing the numeric value.
*/
public Atom (BigDecimal value) {
type_ = Types.T_BCD;
if (value == null) {
value_ = "";
} else {
value_ = value.toString();
}
}
/**
* Constructs a T_BCD Atom from an int. The resulting value
* of this is never empty.
*
* @param value
*/
public Atom (int value) {
type_ = Types.T_BCD;
value_ = String.valueOf (value);
}
/**
* Constructs a T_BCD Atom from a long. The resulting value
* of this is never empty.
*
* @param value
*/
public Atom (long value) {
type_ = Types.T_BCD;
value_ = String.valueOf (value);
}
/**
* Constructs a T_DATE or T_TIMESTAMP Atom.
*
* @param type may be T_DATE or T_TIMESTAMP
* @param date the value of the Atom
* @exception IllegalArgumentException if argument null
*/
public Atom (byte type, Date date) {
if (date == null) throw new IllegalArgumentException();
if (type == Types.T_DATE) {
type_ = Types.T_DATE;
value_ = DateUtil.date2Internal(date);
} else if (type == Types.T_TIMESTAMP) {
type_ = Types.T_TIMESTAMP;
if (date instanceof java.sql.Timestamp)
value_ = TimeStampUtil.timestamp2Internal((Timestamp)date);
else
value_ = TimeStampUtil.date2Internal(date);
} else throw new IllegalArgumentException();
}
/**
* Constructs a T_TIMESTAMP Atom.
*
* @param timeStamp the value of the Atom
* @exception IllegalArgumentException if argument null
*/
public Atom (Timestamp timeStamp) {
if (timeStamp == null) throw new IllegalArgumentException();
type_ = Types.T_TIMESTAMP;
value_ = TimeStampUtil.timestamp2Internal(timeStamp);
}
/**
* Constructs a T_BOOLEAN Atom with the provided value.
*
* @param value the boolean value to construct this with.
*/
public Atom (boolean value) {
type_ = Types.T_BOOLEAN;
value_ = value ? "J" : "N";
}
/**
* Converts this to a transport encoding string s that has the property
* that an Atom created with newTransportInstance(s) is equal to this.
*
* @return a string in a transport encoding. If the type of this is T_STRING,
* the value is delimited with double quotes.
* Other types than T_STRING
* start with a single character indicating the type and a type specific string
* encoding. The returned string (within the quotes) does not contain quotes,
* commas or semicolons.
*/
public String toTransportString () {
StringBuffer buf = new StringBuffer();
if (type_ == Types.T_STRING) {
// strings are encoded with enclosing double quotation marks;
buf.append('"');
buf.append(StringUtil.encode(value_, '%'));
buf.append('"');
} else {
// other types start with a type character, followed by a string encoding
char typeChar = (char)('A' + (type_-Types.FIRST));
buf.append(typeChar);
buf.append(value_);
}
return buf.toString();
}
/**
* Constructs an Atom from a String that has been produced using toTransportString.
*
* @param encoded as defined in method toTransportString
* @return newly created Atom.
* @exception IllegalArgumentException if the argument was not created by toTransportString.
*/
public static Atom newTransportInstance (String encoded) {
if (encoded == null || encoded.length() == 0) throw new IllegalArgumentException();
if (encoded.charAt(0) == '"') {
int len = encoded.length();
// string follows
if (encoded.charAt(len-1) != '"') throw new IllegalArgumentException();
return newInstance (Types.T_STRING, StringUtil.decode(encoded.substring(1, len-1), '%'));
} else {
// other types than string
byte type = (byte)(encoded.charAt(0)-'A' + Types.FIRST);
if (type < Types.FIRST || type > Types.LAST) throw new IllegalArgumentException();
return newInstance (type, encoded.substring(1));
}
}
/**
* Returns an empty Atom having the same type as this.
*/
public Atom clear() {
switch (type_) {
case Types.T_STRING: return EMPTY_STRING;
case Types.T_BCD: return EMPTY_BCD;
case Types.T_DATE: return EMPTY_DATE;
case Types.T_TIMESTAMP: return EMPTY_TIMESTAMP;
case Types.T_BOOLEAN: return EMPTY_BOOLEAN;
case Types.T_DOM: return EMPTY_DOM;
}
throw new InternalError ();
}
/**
* Returns true if this Atom holds a value.
*
* @return true if this has a non empty value.
*/
public boolean hasValue() {
return value_.length() > 0;
}
/**
* Returns the type constant.
*
* @return the type constant.
*/
public byte getType() {
return type_;
}
/**
* @see IAtomic#toString()
*/
public String toString() {
return toStringImpl (type_, value_);
}
/**
* Returns a string representation of an IAtomic given its type
* and its internal encoding.
*
* This method may not be called from outside the framework.
*/
public static String toStringImpl (byte type, String internal) {
if (type == Types.T_TIMESTAMP) {
if (internal == null || internal.length() == 0) return "";
SimpleDateFormat df = new SimpleDateFormat ("yyyyMMdd HH:mm:ss.SSS z");
return df.format(TimeStampUtil.internal2Date(internal));
} else return internal;
}
/**
* Maps this Atom to string representation using a IFmt object.
* The provided formatter is only used if it is type compatible
* with the type of this Atom. Otherwise, or if formatter is
* null, the internal String encoding is returned.
*
* @param formatter the IFmt formatter. May be null.
* @return non null String.
*/
public String toString (IFmt formatter) {
if (formatter == null) return value_;
if (formatter.mayBeAppliedTo(type_)) {
return formatter.format(value_);
} else
return value_;
}
/**
* Extracts a double from this. Returns 0.0 if getType() is not equal to T_BCD or !hasValue().
*
* @return the double value of this
*/
public double toDouble () {
if (type_ != Types.T_BCD || !hasValue()) return 0.0;
return Double.parseDouble(value_);
}
/**
* @see at.spardat.xma.mdl.IAtomic#toFloat()
*/
public float toFloat() {
if (type_ != Types.T_BCD || !hasValue()) return 0.0F;
return Float.parseFloat(value_);
}
/**
* Requires that this is either empty or contains an integer T_BCD value. If !hasValue()
* or the type of this is not T_BCD, zero is returned.
*
* @return the contained integer
* @exception NumberFormatException if this does not fit into an int
*/
public int toInt () {
if (type_ != Types.T_BCD || !hasValue()) return 0;
return Integer.parseInt(value_);
}
/**
* @see at.spardat.xma.mdl.IAtomic#toLong()
*/
public long toLong() {
if (type_ != Types.T_BCD || !hasValue()) return 0;
return Long.parseLong (value_);
}
/**
* @see at.spardat.xma.mdl.IAtomic#toByte()
*/
public byte toByte() {
if (type_ != Types.T_BCD || !hasValue()) return 0;
return Byte.parseByte (value_);
}
/**
* @see at.spardat.xma.mdl.IAtomic#toShort()
*/
public short toShort() {
if (type_ != Types.T_BCD || !hasValue()) return 0;
return Short.parseShort (value_);
}
/**
* @see at.spardat.xma.mdl.IAtomic#toBigDecimal()
*/
public BigDecimal toBigDecimal() {
if (type_ != Types.T_BCD || !hasValue()) return null;
return new BigDecimal (value_);
}
/**
* @see at.spardat.xma.mdl.IAtomic#toByte()
*/
public Byte toBYTE() {
if (type_ != Types.T_BCD || !hasValue()) return null;
return new Byte (value_);
}
/**
* @see at.spardat.xma.mdl.IAtomic#toSHORT()
*/
public Short toSHORT() {
if (type_ != Types.T_BCD || !hasValue()) return null;
return new Short(value_);
}
/**
* @see at.spardat.xma.mdl.IAtomic#toINTEGER()
*/
public Integer toINTEGER() {
if (type_ != Types.T_BCD || !hasValue()) return null;
return new Integer(value_);
}
/**
* @see at.spardat.xma.mdl.IAtomic#toLONG()
*/
public Long toLONG() {
if (type_ != Types.T_BCD || !hasValue()) return null;
return new Long(value_);
}
/**
* @see at.spardat.xma.mdl.IAtomic#toFLOAT()
*/
public Float toFLOAT() {
if (type_ != Types.T_BCD || !hasValue()) return null;
return new Float (value_);
}
/**
* @see at.spardat.xma.mdl.IAtomic#toDOUBLE()
*/
public Double toDOUBLE() {
if (type_ != Types.T_BCD || !hasValue()) return null;
return new Double(value_);
}
/**
* Returns a newly constructed java.util.Date object representing the value of this.
*
* @return null if the type is not equal to T_DATE or T_TIMESTAMP or !hasValue().
*/
public Date toDate () {
return toDateImpl (type_, value_);
}
/**
* Helper method to convert date- and timestamp-atomics to java.util.Date.
* This is a framework-internal method and is not intended to be called from outside.
*/
public static Date toDateImpl (byte type, String internal) {
if (internal == null || internal.length() == 0) return null;
if (type == Types.T_DATE) {
return DateUtil.internal2Gregorian (internal).getTime();
} else if (type == Types.T_TIMESTAMP) {
return TimeStampUtil.internal2Date(internal);
} else return null;
}
/**
* Returns true if getType() equals T_BOOLEAN and the stored value equals TRUE. If
* there is no value stored or the stored value is not TRUE, false is returned.
*
* @return boolean indicating the value in this.
*/
public boolean isTrue() {
if (type_ != Types.T_BOOLEAN) return false;
return value_.equals("J");
}
/**
* Returns the estimated length of the byte stream resulting from streaming an Atom.
*/
public static int streamedSize (Atom a) {
if (a == null) return 1;
return 3 + a.value_.length();
}
/**
* Reads an Atom from an input stream and returns it. The returned Atom
* may be null.
*/
public static Atom internalize (XmaInput i) throws IOException {
byte type = i.readByte();
if (type == Types.FIRST-1) return null;
Atom a = new Atom();
a.type_ = type;
a.value_ = i.readString();
return a;
}
/**
* Writes the state to an output stream. a may be null.
*/
public static void externalize (Atom a, XmaOutput o) throws IOException {
if (a == null) o.writeByte("typ", Types.FIRST-1);
else {
o.writeByte("typ", a.type_);
o.writeString("val", a.value_);
}
}
/**
* @see java.lang.Comparable#compareTo(java.lang.Object)
*/
public int compareTo (Object o) {
Atom a2 = (Atom)o;
// empty atoms are the lowest
if (value_.length() == 0) { // this is empty
if (a2.value_.length() == 0) return 0; // both are empty
return -1; // this is empty, bot not a2
} else {
// this is not empty
if (a2.value_.length() == 0) return 1; // but a2 is empty
}
// here, both are non empty
switch (type_) {
case Types.T_STRING:
return value_.compareTo(a2.value_);
case Types.T_BCD:
double d1 = toDouble(), d2 = a2.toDouble();
if (d1 < d2) return -1;
if (d1 > d2) return 1;
return 0;
case Types.T_DATE:
return toDate().compareTo(a2.toDate());
case Types.T_TIMESTAMP:
return toDate().compareTo(a2.toDate());
case Types.T_BOOLEAN:
boolean b1 = isTrue(), b2 = a2.isTrue();
if (b1 == b2) return 0;
if (b1) return 1;
else return -1;
case Types.T_DOM:
return value_.compareTo(a2.value_);
}
throw new InternalError ();
}
/**
* Returns a describing short string for a type code.
*/
public static String type2String (byte t) {
String type = "?";
switch (t) {
case Types.T_STRING: type = "STR"; break;
case Types.T_BCD: type = "BCD"; break;
case Types.T_DATE: type = "DAT"; break;
case Types.T_TIMESTAMP: type = "TST"; break;
case Types.T_BOOLEAN: type = "BOO"; break;
case Types.T_DOM: type = "DOM"; break;
}
return type;
}
/**
* @see at.spardat.xma.mdl.util.Descriptive#describe(at.spardat.xma.mdl.util.DNode)
*/
public void describe (DNode n) {
n.sb()
.app("type", type2String(type_)).comma()
.app("value", value_)
.eb();
}
/**
* @see java.lang.Object#equals(java.lang.Object)
*/
public boolean equals (Object obj) {
if (obj == null || !(obj instanceof Atom)) return false;
Atom a = (Atom)obj;
return type_ == a.type_ && value_.equals(a.value_);
}
/**
* Estimates the number of bytes this object consumes in memory.
*/
public int estimateMemory () {
return MemoryEstimator.sizeOfObject(2) + MemoryEstimator.sizeOf(value_);
}
/**
* @see at.spardat.xma.mdl.IAtomic#getEncodedValue()
*/
public String getEncodedValue () {
return value_;
}
/**
* @see java.lang.Object#hashCode()
*/
public int hashCode() {
return value_.hashCode() ^ type_;
}
// public static void main(String[] args) {
//
// Collator collator = Collator.getInstance(Locale.getDefault());
// //collator.setStrength(Collator.PRIMARY);
// int cmp = collator.compare("abc", "ABC");
// System.out.println("cmpVal: " + cmp);
// }
}