/* This file is part of GNUnet. (C) 2011, 2012 Christian Grothoff (and other contributing authors) GNUnet is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3, or (at your option) any later version. GNUnet is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GNUnet; see the file COPYING. If not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ package org.gnunet.construct.parsers; import org.gnunet.construct.*; import org.gnunet.construct.ProtocolViolation; import java.lang.reflect.Field; import java.nio.ByteBuffer; import java.util.List; // unchecked casts are necessary @SuppressWarnings("unchecked") public class UnionParser implements Parser { private final Field targetField; private final List unionTagPath; private final ReflectUtil.NumField unionTagField; private final Class unionType; boolean optional; public UnionParser(boolean optional, Class unionType, List unionTagPath, Field f) { targetField = f; this.optional = optional; this.unionTagPath = unionTagPath; this.unionTagField = new ReflectUtil.NumField(unionTagPath.get(unionTagPath.size() - 1)); this.unionType = unionType; } @Override public int getSize(final Message src) { Class cls = ReflectUtil.justGet(src, targetField).getClass(); Parser parser = Construct.getParser(cls); return parser.getSize((Message)ReflectUtil.justGet(src, targetField)); } @Override public int parse(final ByteBuffer srcBuf, int frameOffset, Message frameObj, final Message dstObj, List frameSizePath) { if (optional) { int remaining = frameOffset + ReflectUtil.justGetInt(frameObj, frameSizePath) - srcBuf.position(); if (remaining <= 0) { if (!optional) { throw new ProtocolViolation("not optional"); } ReflectUtil.justSet(dstObj, targetField, null); return 0; } } long unionTag = unionTagField.get(ReflectUtil.followFieldPathToParent(unionTagPath, dstObj)); final Class cls; cls = MessageLoader.getUnionClass(unionType, (int) unionTag); ReflectUtil.justSet(dstObj, targetField, ReflectUtil.justInstantiate(cls)); final Message theUnion = (Message) ReflectUtil.justGet(dstObj, targetField); Parser parser = Construct.getParser(cls); return parser.parse(srcBuf, frameOffset, frameObj, theUnion, frameSizePath); } @Override public int write(final ByteBuffer dstBuf, final Message src) { final Class currentUnionClass = ReflectUtil.justGet(src, targetField).getClass(); final Parser p = Construct.getParser(currentUnionClass); return p.write(dstBuf, (Message) ReflectUtil.justGet(src, targetField)); } @SuppressWarnings("unchecked") public int getTag(Message m) { return MessageLoader.getUnionTag(unionType, (Class) ReflectUtil.justGet(m, targetField).getClass()); } @Override public void patch(Message m, int frameSize, List frameSizePath, Message frameObj) { final Class currentUnionClass = ReflectUtil.justGet(m, targetField).getClass(); final Parser p = Construct.getParser(currentUnionClass); p.patch((Message) ReflectUtil.justGet(m, targetField), frameSize, frameSizePath, frameObj); unionTagField.set(ReflectUtil.followFieldPathToParent(unionTagPath, m), getTag(m)); } @Override public int getStaticSize() { // we can't say anything about the static size // todo: in a more elaborate implementation, try all union members return 0; } }