Skip to content
Open
16 changes: 13 additions & 3 deletions quickfixj-core/src/main/java/quickfix/BooleanField.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@

package quickfix;

import java.lang.Boolean;
import quickfix.field.converter.BooleanConverter;

public class BooleanField extends Field<Boolean> {

Expand All @@ -28,11 +28,11 @@ public BooleanField(int field) {
}

public BooleanField(int field, Boolean data) {
super(field, data);
super(field, null != data ? data : Boolean.FALSE);
}

public BooleanField(int field, boolean data) {
super(field, data);
super(field, Boolean.valueOf(data));
}

public void setValue(Boolean value) {
Expand All @@ -54,4 +54,14 @@ public boolean valueEquals(Boolean value) {
public boolean valueEquals(boolean value) {
return getObject().equals(value);
}

@Override
protected String objectAsString() {
return BooleanConverter.convert(getValue());
}

@Override
protected void objectAsString(StringBuilder stringBuilder) {
stringBuilder.append(objectAsString());
}
}
73 changes: 65 additions & 8 deletions quickfixj-core/src/main/java/quickfix/DoubleField.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,30 +19,37 @@

package quickfix;

import quickfix.field.converter.DoubleConverter;

/**
* A double-values message field.
*/
public class DoubleField extends Field<Double> {

private static final Double ZERO = 0d;
private double value = 0d;
private int padding = 0;

public DoubleField(int field) {
super(field, 0d);
super(field, ZERO);
}

public DoubleField(int field, Double data) {
super(field, data);
checkForValidDouble(data);
value = data;
}

public DoubleField(int field, double data) {
super(field, data);
super(field, ZERO);
checkForValidDouble(data);
value = data;
}

public DoubleField(int field, double data, int padding) {
super(field, data);
super(field, ZERO);
checkForValidDouble(data);
value = data;
this.padding = padding;
}

Expand All @@ -53,29 +60,79 @@ public void setValue(Double value) {

public void setValue(double value) {
checkForValidDouble(value);
setObject(value);
this.value = value;
}

public double getValue() {
return getObject();
return value;
}

@Override
protected void setObject(Double object) {
super.setObject(object);
value = object;
}

@Override
public Double getObject() {
Double val = super.getObject();
if(null != val && value == val)
return val;
super.setObject(value);
return super.getObject();
}

public int getPadding() {
return padding;
}

public boolean valueEquals(Double value) {
return getObject().equals(value);
return null != value && valueEquals(value.doubleValue());
}

public boolean valueEquals(double value) {
return getObject().equals(value);
return this.value == value;
}

// QFJ-808: NaN or infinity values cannot be transmitted via FIX in a DoubleField
private void checkForValidDouble(Double value) throws NumberFormatException {
private void checkForValidDouble(double value) throws NumberFormatException {
if (Double.isInfinite(value) || Double.isNaN(value)) {
throw new NumberFormatException("Tried to set NaN or infinite value.");
}
}

@Override
void toString(StringBuilder buffer) {
if (null != super.data) {
buffer.append(super.data);
return;
}
buffer.append(NumbersCache.get(getTag())).append('=');
double val = value;
if(val < 0d) {
buffer.append('-');
val = Math.abs(val);
}
String str = NumbersCache.get(val);
if(null != str)
buffer.append(str);
else
buffer.append(DoubleConverter.convert(val));
}

@Override
protected String objectAsString() {
return DoubleConverter.convert(value);
}

@Override
protected void objectAsString(StringBuilder stringBuilder) {
stringBuilder.append(objectAsString());
}

@Override
public int hashCode() {
long bits = Double.doubleToLongBits(value);
return (int)(bits ^ (bits >>> 32));
}
}
93 changes: 74 additions & 19 deletions quickfixj-core/src/main/java/quickfix/Field.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,10 @@
package quickfix;

import org.quickfixj.CharsetSupport;
import quickfix.field.converter.DoubleConverter;

import java.io.Serializable;
import java.math.BigDecimal;

/**
* Base class for FIX message fields. This class should be
Expand All @@ -32,8 +34,7 @@
static final long serialVersionUID = 7098326013456432197L;
private int tag;
private T object;
private boolean isCalculated = false;
private String data;
protected String data;

public Field(int field, T object) {
this.tag = field;
Expand Down Expand Up @@ -66,7 +67,7 @@ public int getField() {
*/
protected void setObject(T object) {
this.object = object;
isCalculated = false;
data = null;
}

/**
Expand All @@ -89,22 +90,70 @@ public String toString() {
}

/*package*/ void toString(StringBuilder buffer) {
buffer.append(tag).append('=').append(objectAsString());
if(null != data) {
buffer.append(data);
return;
}
buffer.append(quickfix.NumbersCache.get(tag)).append('=');
if(object instanceof Number) {
if(object instanceof Integer)
appendNumber(buffer, ((Integer)object).longValue());
else if(object instanceof Double)
appendDouble(buffer, (Double) object);
else if(object instanceof Float)
buffer.append(((Float)object).floatValue());
else if(object instanceof BigDecimal)
buffer.append(((BigDecimal)object).toPlainString());
else
appendNumber(buffer, ((Number)object).longValue());
} else
objectAsString(buffer);
}

private static void appendNumber(StringBuilder buffer, long value) {
if(value < 0) {
buffer.append('-');
value = Math.abs(value);
}
buffer.append(quickfix.NumbersCache.get(value));
}

private static void appendDouble(StringBuilder buffer, double value) {
if(value < 0d) {
buffer.append('-');
value = Math.abs(value);
}
String str = quickfix.NumbersCache.get(value);
if(null != str)
buffer.append(str);
else
buffer.append(DoubleConverter.convert(value));
}

protected String objectAsString() {
return object.toString();
}

protected void objectAsString(StringBuilder stringBuilder) {
stringBuilder.append(objectAsString());
}

public boolean equals(Object object) {
return super.equals(object)
|| object instanceof Field
&& tag == ((Field<?>) object).getField()
&& getObject().equals(((Field<?>) object).getObject());
return super.equals(object) || (object instanceof Field &&
((Field<?>) object).equals(tag, getObject()));
}

private boolean equals(int tag, Object object) {
if(this.tag != tag)
return false;
Object thisObject = getObject();
if(thisObject == null)
return object == null;
return thisObject.equals(object);
}

public int hashCode() {
return object.hashCode();
return getObject().hashCode();
}

/**
Expand All @@ -129,21 +178,27 @@ public int hashCode() {
return (MessageUtils.checksum(CharsetSupport.getCharsetInstance(), data, false) + 1) & 0xFF;
}

private void calculate() {
if (isCalculated) {
return;
private static final ThreadLocal<StringBuilder> buffers = new ThreadLocal<StringBuilder>() {
@Override
protected StringBuilder initialValue() {
return new StringBuilder(256);
}
};

StringBuilder buffer = new StringBuilder();
toString(buffer);
data = buffer.toString();

isCalculated = true;
private void calculate() {
if(null == data) {
StringBuilder buffer = buffers.get();
try {
toString(buffer);
data = buffer.toString();
} finally {
buffer.setLength(0);
}
}
}

public void setTag(int tag) {
this.tag = tag;
isCalculated = false;
calculate();
data = null;
}
}
4 changes: 2 additions & 2 deletions quickfixj-core/src/main/java/quickfix/FileStore.java
Original file line number Diff line number Diff line change
Expand Up @@ -403,12 +403,12 @@ public boolean set(int sequence, String message) throws IOException {

private void storeSenderSequenceNumber() throws IOException {
senderSequenceNumberFile.seek(0);
senderSequenceNumberFile.writeUTF("" + cache.getNextSenderMsgSeqNum());
senderSequenceNumberFile.writeUTF(NumbersCache.get(cache.getNextSenderMsgSeqNum()));
}

private void storeTargetSequenceNumber() throws IOException {
targetSequenceNumberFile.seek(0);
targetSequenceNumberFile.writeUTF("" + cache.getNextTargetMsgSeqNum());
targetSequenceNumberFile.writeUTF(NumbersCache.get(cache.getNextTargetMsgSeqNum()));
}

String getHeaderFileName() {
Expand Down
74 changes: 74 additions & 0 deletions quickfixj-core/src/main/java/quickfix/NumbersCache.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
/*******************************************************************************
* Copyright (c) quickfixengine.org All rights reserved.
*
* This file is part of the QuickFIX FIX Engine
*
* This file may be distributed under the terms of the quickfixengine.org
* license as defined by quickfixengine.org and appearing in the file
* LICENSE included in the packaging of this file.
*
* This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING
* THE WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A
* PARTICULAR PURPOSE.
*
* See http://www.quickfixengine.org/LICENSE for licensing information.
*
* Contact ask@quickfixengine.org if any conditions of this licensing
* are not clear to you.
******************************************************************************/

package quickfix;

import java.util.ArrayList;

/**
* A cache for commonly used string representing numbers.
* Hold values from 0 to 999999 and from 1000 to 200 000 000 by step of 1000
*/
public final class NumbersCache {

private static final int littleNumbersLength = 1000000;
private static final int bigNumbersLength = 200000;
private static final int bigNumbersOffset = 1000;
private static final int bigNumbersMax = bigNumbersLength * bigNumbersOffset;

public static final ArrayList<String> littleNumbers;
public static final ArrayList<String> bigNumbers;

static {
littleNumbers = new ArrayList<String>(littleNumbersLength);
bigNumbers = new ArrayList<String>(bigNumbersLength);
for (int i = 0; i < littleNumbersLength; i++)
littleNumbers.add(Integer.toString(i));
for (long i = 0; i < bigNumbersLength;)
bigNumbers.add(Long.toString(++i * bigNumbersOffset));

}

/**
* Get the string representing the given number
*
* @param i the long to convert
* @return the String representing the long
*/
public static String get(long i) {
if (i < littleNumbersLength)
return littleNumbers.get((int)i);
if (i <= bigNumbersMax && i % bigNumbersOffset == 0)
return bigNumbers.get((int)(i/bigNumbersOffset)-1);
return String.valueOf(i);
}

/**
* Get the string representing the given double if it's an integer
*
* @param d the double to convert
* @return the String representing the double or null if the double is not an integer
*/
public static String get(double d) {
long l = (long)d;
if (d == (double)l)
return get(l);
return null;
}
}
Loading