Java tutorial
/** * Copyright (C) 2011-2012 Barchart, Inc. <http://www.barchart.com/> * * All rights reserved. Licensed under the OSI BSD License. * * http://www.opensource.org/licenses/bsd-license.php */ package com.barchart.feed.ddf.message.provider; import static com.barchart.util.common.ascii.ASCII.NUL; import static com.barchart.util.common.ascii.ASCII._0_; import static com.barchart.util.common.ascii.ASCII._A_; import static com.barchart.util.common.ascii.ASCII._J_; import static com.barchart.util.common.ascii.ASCII._K_; import static com.barchart.util.common.ascii.ASCII._T_; import static com.barchart.util.common.ascii.ASCII._Z_; import static com.barchart.util.common.ascii.ASCII.isDigit; import static com.barchart.util.common.ascii.ASCII.isLetterUpper; import java.nio.ByteBuffer; import org.joda.time.DateTime; import org.joda.time.DateTimeZone; import org.joda.time.ReadableDateTime; import org.w3c.dom.Element; import com.barchart.feed.ddf.message.api.DDF_MarketBook; import com.barchart.feed.ddf.symbol.provider.DDF_Symbology; import com.barchart.feed.ddf.util.ClockDDF; import com.barchart.feed.ddf.util.FeedClock; import com.barchart.feed.ddf.util.HelperDDF; import com.barchart.feed.ddf.util.HelperXML; import com.barchart.util.common.ascii.ASCII; /* TODO make public ? */ public class CodecHelper { // TODO make configurable static final boolean IS_FEED_TIME_STAMP_PRESENT = true; // /////////////////////////////////////////////// /** maximum size of ddf book snapshot on bid or ask side */ final static int DDF_BOOK_LIMIT = DDF_MarketBook.ENTRY_LIMIT; /** default ddf message delay */ static final int DDF_NO_DELAY = 0; /** default ddf book prices */ static final long[] DDF_NO_PRICES = new long[DDF_BOOK_LIMIT]; /** default ddf book sizes */ static final long[] DDF_NO_SIZES = new long[DDF_BOOK_LIMIT]; /** */ static final DX_XS_Session[] DDF_NO_SESSIONS = new DX_XS_Session[0]; /** */ static final int DDF_NO_COUNT = 0; /** ddf encoded time stamp first byte */ final static byte DDF_CENTURY = ASCII.DC4; /** ddf time stamp encoding/decoding mask */ final static int DDF_TIME_STAMP_MASK = 0x40; // /////////////////////////////////////////////// // feed time stamp - "magic 9 bytes" or "current default" private static final FeedClock clock = ClockDDF.clock; static final long decodeFeedTimeStamp(final DateTimeZone zone, final ByteBuffer buffer) { if (buffer.position() == buffer.limit()) { // no suffix; return current return clock.millis(); } // buffer.mark(); final byte timeStampStart = buffer.get(); buffer.reset(); if (timeStampStart == DDF_CENTURY) { // magic 9 bytes final long time = decodeMillisUTC(zone, buffer); /* No longer setting DDF time on every message, revert to only using * DDF_ControlTimestamp messages for current time. This was causing * real time data to look delayed */ // TODO This will still make delayed data look like realtime data // clock.set(time); return time; } else { // unknown suffix; return current return clock.millis(); } } static final void encodeFeedTimeStamp(final long millisUTC, final DateTimeZone zone, final ByteBuffer buffer) { if (millisUTC == HelperDDF.DDF_EMPTY || millisUTC == HelperDDF.DDF_CLEAR) { return; } if (IS_FEED_TIME_STAMP_PRESENT) { CodecHelper.encodeMillisUTC(millisUTC, zone, buffer); } } static final byte[] read(final ByteBuffer buffer, final byte marker) { final byte[] source = buffer.array(); final int start = buffer.position(); int index = start; while (index < source.length && source[index] != marker) { index++; } buffer.position(index + 1); final int size = index - start; final byte[] target = new byte[size]; System.arraycopy(source, start, target, 0, size); return target; } static final byte find(final ByteBuffer buffer, final byte marker) { return find(buffer.array(), buffer.position(), marker); } static final byte find(final byte[] source, final int start, final byte marker) { int index = start; while (index < source.length && source[index] != marker) { index++; } if (index == start || index == source.length) { return NUL; } else { return source[index - 1]; } } // final static void checkDigit(final int value) { if (value < 0 || value >= 10) { throw new IllegalArgumentException(); } } final static void encodeUnsigned_1(final int value, final ByteBuffer buffer) { checkDigit(value); buffer.put((byte) (_0_ + value)); } final static void encodeUnsigned_1_book(final int value, final ByteBuffer buffer) { if (0 <= value && value < 10) { buffer.put((byte) (_0_ + value)); return; } if (10 <= value && value < (_Z_ - _A_)) { buffer.put((byte) (_A_ + value)); return; } // FIXME silent error buffer.put(_0_); } final static byte decodeUnsigned_1(final ByteBuffer buffer) { final int value = buffer.get() - _0_; checkDigit(value); return (byte) value; } final static byte decodeUnsigned_1_book(final ByteBuffer buffer) { final byte alpha = buffer.get(); if (isDigit(alpha)) { return (byte) (alpha - _0_); } if (isLetterUpper(alpha)) { return (byte) (10 + alpha - _A_); } // FIXME silent error return 0; } final static void encodeUnsigned_2(final int value, final ByteBuffer buffer) { if (value < 0 || value >= 100) { throw new IllegalArgumentException(); } final int ones = value % 10; final int tens = value / 10; buffer.put((byte) (_0_ + tens)); // first byte buffer.put((byte) (_0_ + ones)); // second byte } final static byte decodeUnsigned_2(final ByteBuffer buffer) { final int tens = buffer.get() - _0_; checkDigit(tens); final int ones = buffer.get() - _0_; checkDigit(ones); return (byte) (tens * 10 + ones); } static final int bookBidIndexFrom(final byte code) { if (code < _K_ || _T_ < code) { throw new IllegalArgumentException(); } return (code - _K_); } static final byte bookBidCodeFrom(final int index) { if (index < 0 || DDF_BOOK_LIMIT <= index) { throw new IllegalArgumentException(); } return (byte) (_K_ + index); } static final int bookAskIndexFrom(final byte code) { if (code < _A_ || _J_ < code) { throw new IllegalArgumentException(); } return (_J_ - code); } static final byte bookAskCodeFrom(final int index) { if (index < 0 || DDF_BOOK_LIMIT <= index) { throw new IllegalArgumentException(); } return (byte) (_J_ - index); } static final byte[][] xmlDecSymbol(final Element tag, final String attribute, final boolean isThrow) { final String string = tag.getAttribute(attribute); if (string.length() > 0) { try { return DDF_Symbology.symbolArrayFromSymbolString(string); } catch (final Exception e) { // below } } if (isThrow) { throw new IllegalArgumentException("attribute not valid : " + attribute); } return null; } // byte array static final boolean isXmlBook(final Element root) { return HelperXML.isXmlNameMatch(root, XmlTagBook.TAG); } static final boolean isXmlCuvol(final Element root) { return HelperXML.isXmlNameMatch(root, XmlTagCuvol.TAG); } static final boolean isXmlQuote(final Element root) { return HelperXML.isXmlNameMatch(root, XmlTagQuote.TAG); } final static byte encodeTimeStampByte(final int timeField) { return (byte) (timeField | DDF_TIME_STAMP_MASK); } final static int decodeTimeStampByte(final byte timeByte) { return timeByte & (~DDF_TIME_STAMP_MASK); } /** time zone information is discarded */ static final void encodeTimeStamp(final ReadableDateTime dateTime, final ByteBuffer buffer) { // base fields buffer.put(DDF_CENTURY); // century buffer.put(encodeTimeStampByte(dateTime.getYearOfCentury())); // year buffer.put(encodeTimeStampByte(dateTime.getMonthOfYear())); // month buffer.put(encodeTimeStampByte(dateTime.getDayOfMonth())); // day buffer.put(encodeTimeStampByte(dateTime.getHourOfDay())); // hours buffer.put(encodeTimeStampByte(dateTime.getMinuteOfHour())); // minutes buffer.put(encodeTimeStampByte(dateTime.getSecondOfMinute())); // seconds // milliseconds final int millisOfSecond = dateTime.getMillisOfSecond(); buffer.put((byte) (millisOfSecond & 0xFF)); // low byte buffer.put((byte) ((millisOfSecond >>> 8) & 0xFF)); // high byte } /** time zone information is provided */ static final DateTime decodeTimeStamp(final DateTimeZone zone, final ByteBuffer buffer) { // base fields buffer.get(); // DDF_CENTURY final int yearOfEra = 2000 + decodeTimeStampByte(buffer.get()); final int monthOfYear = decodeTimeStampByte(buffer.get()); final int dayOfMonth = decodeTimeStampByte(buffer.get()); final int hourOfDay = decodeTimeStampByte(buffer.get()); final int minuteOfHour = decodeTimeStampByte(buffer.get()); final int secondOfMinute = decodeTimeStampByte(buffer.get()); // milliseconds final byte lo = buffer.get(); final byte hi = buffer.get(); final int millisOfSecond = ((hi & 0xFF) << 8) | (lo & 0xFF); // will throw RTE if any field is out of range return new DateTime(// yearOfEra, monthOfYear, dayOfMonth, // hourOfDay, minuteOfHour, secondOfMinute, // millisOfSecond, zone); } static final void encodeMillisUTC(final long millisUTC, final DateTimeZone zone, final ByteBuffer buffer) { final DateTime dateTime = new DateTime(millisUTC, zone); encodeTimeStamp(dateTime, buffer); } static final long decodeMillisUTC(final DateTimeZone zone, final ByteBuffer buffer) { final DateTime dateTime = decodeTimeStamp(zone, buffer); return dateTime.getMillis(); } // static final void check(final byte left, final byte right) { if (left == right) { return; } else { throw new RuntimeException("no match;" + " left=" + left + " right=" + right); } } }