/* * Copyright 2012 Michael Bischoff * * 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 de.jpaw.bonaparte.core; import java.io.UnsupportedEncodingException; import java.math.BigDecimal; import java.util.List; import java.util.Map; import de.jpaw.bonaparte.pojos.meta.NumericElementaryDataItem; import de.jpaw.bonaparte.pojos.meta.ObjectReference; import de.jpaw.bonaparte.util.BigDecimalTools; import de.jpaw.util.ByteArray; /** * The CompactByteArrayParser class. * * @author Michael Bischoff * @version $Revision$ * * Implementation of the MessageParser for the binary compact protocol, using byte arrays. */ public class CompactByteArrayParser extends AbstractCompactParser<MessageParserException> { private int parseIndex; private int messageLength; private byte [] inputdata; /** Quick conversion utility method, for use by code generators. (null safe) */ public static <T extends BonaPortable> T unmarshal(byte [] x, ObjectReference di, Class<T> expectedClass) throws MessageParserException { if (x == null || x.length == 0) return null; return new CompactByteArrayParser(x, 0, -1).readObject(di, expectedClass); } /** Quick conversion utility method, for use by code generators. (null safe) */ public static Object unmarshalElement(byte [] x, ObjectReference di) throws MessageParserException { if (x == null || x.length == 0) return null; return new CompactByteArrayParser(x, 0, -1).readElement(di); } /** Quick conversion utility method, for use by code generators. (null safe) */ public static List<Object> unmarshalArray(byte [] x, ObjectReference di) throws MessageParserException { if (x == null || x.length == 0) return null; return new CompactByteArrayParser(x, 0, -1).readArray(di); } /** Quick conversion utility method, for use by code generators. (null safe) */ public static Map<String, Object> unmarshalJson(byte [] x, ObjectReference di) throws MessageParserException { if (x == null || x.length == 0) return null; return new CompactByteArrayParser(x, 0, -1).readJson(di); } /** Assigns a new source to subsequent parsing operations. */ public final void setSource(byte [] src, int offset, int length) { inputdata = src; parseIndex = offset; messageLength = length < 0 ? src.length : length; clearCache(); } /** Assigns a new source to subsequent parsing operations. */ public final void setSource(byte [] src) { inputdata = src; parseIndex = 0; messageLength = src.length; } /** Create a processor for parsing a buffer. */ public CompactByteArrayParser(byte [] buffer, int offset, int length) { super(); inputdata = buffer; parseIndex = offset; messageLength = length < 0 ? inputdata.length : length; // -1 means full array size / until end of data currentClass = "N/A"; } @Override protected int getParseIndex() { return parseIndex; } /************************************************************************************************** * Deserialization goes here. Code below does not use the ByteBuilder class, * but reads from the byte[] directly **************************************************************************************************/ @Override protected MessageParserException newMPE(int errorCode, String msg) { return new MessageParserException(errorCode, msg, parseIndex, currentClass); } @Override protected BonaPortable createObject(String classname) throws MessageParserException { // same method - overloading required for possible exception mapping return BonaPortableFactory.createObject(classname); } @Override protected BigDecimal checkAndScale(BigDecimal num, NumericElementaryDataItem di) throws MessageParserException { return BigDecimalTools.checkAndScale(num, di, parseIndex, currentClass); } // special method only in the ByteArray version protected void require(int length) throws MessageParserException { if (parseIndex + length > messageLength) { throw newMPE(MessageParserException.PREMATURE_END, null); } } @Override protected boolean atEnd() throws MessageParserException { return parseIndex >= messageLength; } @Override protected int needToken() throws MessageParserException { if (parseIndex >= messageLength) { throw newMPE(MessageParserException.PREMATURE_END, null); } return inputdata[parseIndex++] & 0xff; } @Override protected void needToken(int c) throws MessageParserException { if (parseIndex >= messageLength) { throw newMPE(MessageParserException.PREMATURE_END, String.format("(expected 0x%02x)", c)); } int d = inputdata[parseIndex++] & 0xff; if (c != d) { throw newMPE(MessageParserException.UNEXPECTED_CHARACTER, String.format("(expected 0x%02x, got 0x%02x)", c, d)); } } @Override protected void pushback(int c) { // ignore c, just decrement the position --parseIndex; } @Override protected char readChar() throws MessageParserException { require(2); char cc = (char)(((inputdata[parseIndex] & 0xff) << 8) | (inputdata[parseIndex+1] & 0xff)); parseIndex += 2; return cc; } @Override protected int readFixed2ByteInt() throws MessageParserException { require(2); int nn = inputdata[parseIndex++] << 8; return nn | inputdata[parseIndex++] & 0xff; } @Override protected int readFixed3ByteInt() throws MessageParserException { require(3); int nn = inputdata[parseIndex++] << 16; // does sign-extend as required nn |= (inputdata[parseIndex++] & 0xff) << 8; nn |= inputdata[parseIndex++] & 0xff; return nn; } @Override protected int readFixed4ByteInt() throws MessageParserException { require(4); int nn = (inputdata[parseIndex++] & 0xff) << 24; nn |= (inputdata[parseIndex++] & 0xff) << 16; nn |= (inputdata[parseIndex++] & 0xff) << 8; nn |= inputdata[parseIndex++] & 0xff; return nn; } @Override protected long readFixed6ByteLong() throws MessageParserException { require(6); int nn1 = inputdata[parseIndex++] << 8; nn1 |= inputdata[parseIndex++] & 0xff; int nn2 = readFixed4ByteInt(); return ((long)nn1 << 32) | (nn2 & 0xffffffffL); } @Override protected long readFixed8ByteLong() throws MessageParserException { int nn1 = readFixed4ByteInt(); int nn2 = readFixed4ByteInt(); return ((long)nn1 << 32) | (nn2 & 0xffffffffL); } @Override protected byte [] readBytes(int len) throws MessageParserException { if (len == 0) return EMPTY_BYTE_ARRAY; require(len); byte [] data = new byte [len]; System.arraycopy(inputdata, parseIndex, data, 0, len); parseIndex += len; return data; } @Override protected ByteArray readByteArray(int len) throws MessageParserException { if (len > 0) { ByteArray result = new ByteArray(inputdata, parseIndex, len); parseIndex += len; return result; } return ByteArray.ZERO_BYTE_ARRAY; } // faster implementation: expect that new String (char []) is faster than anything with charset encoder, because the loop in this code will be inline @Override protected String readISO(int len) throws MessageParserException { require(len); char data [] = new char [len]; for (int i = 0; i < len; ++i) data[i] = (char) (0xff & (char)inputdata[parseIndex++]); return new String(data); // byte data [] = readBytes(len); // return new String(data, "ISO-8859-1"); } // read len characters @Override protected String readUTF16(int len) throws MessageParserException { len *= 2; require(len); try { String result = new String(inputdata, parseIndex, len, CHARSET_UTF16); parseIndex += len; return result; } catch (UnsupportedEncodingException e) { throw new RuntimeException(e); // should never happen, CHARSET_UTF16 is guaranteed to exist } } // read len bytes @Override protected String readUTF8(int len) throws MessageParserException { require(len); try { String result = new String(inputdata, parseIndex, len, CHARSET_UTF8); parseIndex += len; return result; } catch (UnsupportedEncodingException e) { throw new RuntimeException(e); // should never happen, CHARSET_UTF16 is guaranteed to exist } } @Override protected void skipBytes(int howMany) throws MessageParserException { if (parseIndex + howMany >= messageLength) { throw newMPE(MessageParserException.PREMATURE_END, String.format("(while skipping %d characters from pos %d (0x%04x))", howMany, parseIndex, parseIndex)); } parseIndex += howMany; } }