/*
* Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.f1x.v1;
import org.f1x.api.FixParserException;
import org.f1x.api.message.MessageParser;
import org.f1x.util.ByteArrayReference;
import org.f1x.util.parse.NumbersParser;
import org.f1x.util.parse.TimeOfDayParser;
import org.f1x.util.parse.TimestampParser;
public class DefaultMessageParser implements MessageParser {
//private static final GFLog LOGGER = GFLogFactory.getLog(DefaultMessageParser.class);
private static final char SOH = 1; // field separator
private final TimestampParser utcTimestampParser = TimestampParser.createUTCTimestampParser();
private final TimestampParser localTimestampParser = TimestampParser.createLocalTimestampParser();
private final ByteArrayReference charSequenceBuffer = new ByteArrayReference();
private byte[] buffer;
private int start;
private int offset; // next byte to read
private int limit;
private int tagNum;
private int valueOffset, valueLength;
public final void set (byte [] buffer, int offset, int length) {
this.buffer = buffer;
this.start = offset;
this.limit = offset + length;
reset();
}
@Override
public final boolean next() {
try {
final boolean result = _next();
if (result) {
if (valueLength == 0)
throw new FixParserException("Tag " + tagNum + " has empty value at position " + offset);
}
return result;
} catch (FixParserException e) {
throw new FixParserException("Parser error (at " + offset + "): " + e.getMessage());
}
}
private boolean _next () {
boolean isParsingTagNum = true;
tagNum = 0;
while (offset < limit) {
byte ch = buffer[offset++];
if (isParsingTagNum) {
if (ch >= '0' && ch <= '9') {
tagNum = 10*tagNum + (ch - '0');
} else
if (ch == '=') {
if (tagNum == 0)
throw new FixParserException("Unexpected '=' character instead of a tag number digit");
isParsingTagNum = false;
valueOffset = offset;
valueLength = 0;
} else {
throw new FixParserException("Unexpected character (0x" + Integer.toHexString(ch) + " where a tag number digit or '=' is expected");
}
} else {
if (ch == SOH)
return true;
valueLength++;
}
}
return false;
}
@Override
public int getTagNum() {
return tagNum;
}
@Override
public byte getByteValue() {
if (valueLength > 1)
throw new FixParserException("Value is not a single byte");
return buffer[valueOffset];
}
@Override
public int getIntValue() {
return NumbersParser.parseInt(buffer, valueOffset, valueLength);
}
@Override
public long getLongValue() {
return NumbersParser.parseLong(buffer, valueOffset, valueLength);
}
@Override
public double getDoubleValue() {
return NumbersParser.parseDouble(buffer, valueOffset, valueLength);
}
@Override
public CharSequence getCharSequenceValue() {
charSequenceBuffer.set(buffer, valueOffset, valueLength);
return charSequenceBuffer;
}
@Override
public void getByteSequence(ByteArrayReference seq) {
seq.set(buffer, valueOffset, valueLength);
}
@Override
public String getStringValue () {
return new String (buffer, valueOffset, valueLength);
}
@Override
public void getStringBuilder(StringBuilder appendable) {
charSequenceBuffer.set(buffer, valueOffset, valueLength);
appendable.append(charSequenceBuffer);
}
@Override
public boolean getBooleanValue () {
if (valueLength > 1)
throw new FixParserException("Field is not a character");
if (buffer[valueOffset] == 'Y') return true;
if (buffer[valueOffset] == 'N') return false;
throw new FixParserException("Field cannot be parsed as FIX boolean");
}
@Override
public long getUTCTimestampValue () {
return utcTimestampParser.getUTCTimestampValue(buffer, valueOffset, valueLength);
}
@Override
public long getUTCDateOnly() {
return utcTimestampParser.getUTCDateOnly(buffer, valueOffset, valueLength);
}
@Override
public long getLocalMktDate() {
return localTimestampParser.getUTCDateOnly(buffer, valueOffset, valueLength);
}
@Override
public int getLocalMktDate2() {
return localTimestampParser.getUTCDateOnly2(buffer, valueOffset, valueLength);
}
@Override
public int getUTCTimeOnly() {
return TimeOfDayParser.parseTimeOfDay(buffer, valueOffset, valueLength);
}
@Override
public boolean isValueEquals(byte[] constant) {
if (valueLength != constant.length)
return false;
for (int i=0; i < valueLength; i++)
if (buffer[valueOffset+i] != constant[i])
return false;
return true;
}
@Override
public final void reset() {
tagNum = valueOffset = valueLength = 0;
offset = start;
}
int getOffset() {
return offset; //TODO: Refactor this class so that we won't need this method
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder(128);
sb.append ("Buffer pos ");
sb.append (offset);
sb.append ('/');
sb.append (limit);
sb.append (" Current tag ");
sb.append (tagNum);
if (valueLength > 0) {
sb.append ('=');
sb.append (new String(buffer, valueOffset, valueLength));
}
return sb.toString();
}
}