/*
 * Copyright (c) 2001, 2018, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.
 *
 * This code 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
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

package nsk.share.jdwp;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Hashtable;

import nsk.share.Failure;

/**
 * This class contains JDWP constants, types and parameters.
 */
public class JDWP {

    public static class Error {

        public static final int NONE                            = 0;
        public static final int INVALID_THREAD                  = 10;
        public static final int INVALID_THREAD_GROUP            = 11;
        public static final int INVALID_PRIORITY                = 12;
        public static final int THREAD_NOT_SUSPENDED            = 13;
        public static final int THREAD_SUSPENDED                = 14;
        public static final int INVALID_OBJECT                  = 20;
        public static final int INVALID_CLASS                   = 21;
        public static final int CLASS_NOT_PREPARED              = 22;
        public static final int INVALID_METHODID                = 23;
        public static final int INVALID_LOCATION                = 24;
        public static final int INVALID_FIELDID                 = 25;
        public static final int INVALID_FRAMEID                 = 30;
        public static final int NO_MORE_FRAMES                  = 31;
        public static final int OPAQUE_FRAME                    = 32;
        public static final int NOT_CURRENT_FRAME               = 33;
        public static final int TYPE_MISMATCH                   = 34;
        public static final int INVALID_SLOT                    = 35;
        public static final int DUPLICATE                       = 40;
        public static final int NOT_FOUND                       = 41;
        public static final int INVALID_MONITOR                 = 50;
        public static final int NOT_MONITOR_OWNER               = 51;
        public static final int INTERRUPT                       = 52;
        public static final int INVALID_CLASS_FORMAT            = 60;
        public static final int CIRCULAR_CLASS_DEFINITION       = 61;
        public static final int FAILS_VERIFICATION              = 62;
        public static final int ADD_METHOD_NOT_IMPLEMENTED      = 63;
        public static final int SCHEMA_CHANGE_NOT_IMPLEMENTED   = 64;
        public static final int INVALID_TYPESTATE               = 65;
        public static final int HIERARCHY_CHANGE_NOT_IMPLEMENTED= 66;
        public static final int DELETE_METHOD_NOT_IMPLEMENTED   = 67;
        public static final int UNSUPPORTED_VERSION             = 68;
        public static final int NAMES_DONT_MATCH                = 69;
        public static final int CLASS_MODIFIERS_CHANGE_NOT_IMPLEMENTED  = 70;
        public static final int METHOD_MODIFIERS_CHANGE_NOT_IMPLEMENTED = 71;
        public static final int NOT_IMPLEMENTED                 = 99;
        public static final int NULL_POINTER                    = 100;
        public static final int ABSENT_INFORMATION              = 101;
        public static final int INVALID_EVENT_TYPE              = 102;
        public static final int ILLEGAL_ARGUMENT                = 103;
        public static final int OUT_OF_MEMORY                   = 110;
        public static final int ACCESS_DENIED                   = 111;
        public static final int VM_DEATH                        = 112;
        public static final int INTERNAL                        = 113;
        public static final int UNATTACHED_THREAD               = 115;
        public static final int INVALID_TAG                     = 500;
        public static final int ALREADY_INVOKING                = 502;
        public static final int INVALID_INDEX                   = 503;
        public static final int INVALID_LENGTH                  = 504;
        public static final int INVALID_STRING                  = 506;
        public static final int INVALID_CLASS_LOADER            = 507;
        public static final int INVALID_ARRAY                   = 508;
        public static final int TRANSPORT_LOAD                  = 509;
        public static final int TRANSPORT_INIT                  = 510;
        public static final int NATIVE_METHOD                   = 511;
        public static final int INVALID_COUNT                   = 512;

    }

    public static class Flag {

        public static final byte NONE           = (byte)0;
        public static final byte REPLY_PACKET   = (byte)0x80;
        public static final byte EVENT_PACKET   = NONE;

    }

    public static class EventKind {

        public static final byte VM_INIT         = (byte)90;
        public static final byte VM_START        = VM_INIT;
        public static final byte VM_DISCONNECTED = (byte)100;
        public static final byte VM_DEATH        = (byte)99;

        public static final byte THREAD_START    = (byte)6;
        public static final byte THREAD_END      = (byte)7;
        public static final byte THREAD_DEATH    = THREAD_END;

        public static final byte CLASS_PREPARE   = (byte)8;
        public static final byte CLASS_LOAD      = (byte)10;
        public static final byte CLASS_UNLOAD    = (byte)9;

        public static final byte METHOD_ENTRY    = (byte)40;
        public static final byte METHOD_EXIT     = (byte)41;

        public static final byte FIELD_ACCESS    = (byte)20;
        public static final byte FIELD_MODIFICATION = (byte)21;

        public static final byte EXCEPTION       = (byte)4;
        public static final byte EXCEPTION_CATCH = (byte)30;

        public static final byte FRAME_POP       = (byte)3;

        public static final byte BREAKPOINT      = (byte)2;

        public static final byte SINGLE_STEP     = (byte)1;

        public static final byte USER_DEFINED    = (byte)5;

    }

    public static class EventModifierKind {

        public static final byte COUNT          = (byte)1;
        public static final byte CONDITIONAL    = (byte)2;
        public static final byte THREAD_ONLY    = (byte)3;
        public static final byte CLASS_ONLY     = (byte)4;
        public static final byte CLASS_MATCH    = (byte)5;
        public static final byte CLASS_EXCLUDE  = (byte)6;
        public static final byte LOCATION_ONLY  = (byte)7;
        public static final byte EXCEPTION_ONLY = (byte)8;
        public static final byte FIELD_ONLY     = (byte)9;
        public static final byte STEP           = (byte)10;
        public static final byte INSTANCE_ONLY  = (byte)11;
    };

    public static class ThreadStatus {

        public static final int ZOMBIE          = 0;
        public static final int RUNNING         = 1;
        public static final int SLEEPING        = 2;
        public static final int MONITOR         = 3;
        public static final int WAIT            = 4;

    }

    public static class SuspendStatus {

        public static final int SUSPEND_STATUS_SUSPENDED = 0x1;

    }

    public static class ClassStatus {

        public static final int PREPARED        = 2;
        public static final int VERIFIED        = 1;
        public static final int INITIALIZED     = 4;
        public static final int ERROR           = 8;

    }

    public static class TypeTag {

        public static final byte CLASS           = (byte)1;
        public static final byte INTERFACE       = (byte)2;
        public static final byte ARRAY           = (byte)3;

    }

    public static class Tag {

        public static final byte ARRAY           = (byte)91;
        public static final byte BYTE            = (byte)66;
        public static final byte CHAR            = (byte)67;
        public static final byte OBJECT          = (byte)76;
        public static final byte FLOAT           = (byte)70;
        public static final byte DOUBLE          = (byte)68;
        public static final byte INT             = (byte)73;
        public static final byte LONG            = (byte)74;
        public static final byte SHORT           = (byte)83;
        public static final byte VOID            = (byte)86;
        public static final byte BOOLEAN         = (byte)90;
        public static final byte STRING          = (byte)115;
        public static final byte THREAD          = (byte)116;
        public static final byte THREAD_GROUP    = (byte)103;
        public static final byte CLASS_LOADER    = (byte)108;
        public static final byte CLASS_OBJECT    = (byte)99;

    }

    public static class StepDepth {

        public static final int INTO            = 0;
        public static final int OVER            = 1;
        public static final int OUT             = 2;

    }

    public static class StepSize {

        public static final int MIN             = 0;
        public static final int LINE            = 1;

    }

    public static class SuspendPolicy {

        public static final byte NONE            = (byte)0;
        public static final byte EVENT_THREAD    = (byte)1;
        public static final byte ALL             = (byte)2;

    }

    public static class InvokeOptions {

        public static final int INVOKE_SINGLE_THREADED     = 0x01;
        public static final int INVOKE_NONVIRTUAL          = 0x02;

    }

    public static class TypeSize {

        // VM independent type sizes

        public static final int BYTE              = 1;
        public static final int BOOLEAN           = 1;
        public static final int CHAR              = 2;
        public static final int SHORT             = 2;
        public static final int FLOAT             = 4;
        public static final int INT               = 4;
        public static final int LONG              = 8;
        public static final int DOUBLE            = 8;

        public static final int TAG               = 1;
        public static final int LOCATION_INDEX    = 8;

        // basic VM specific type sizes

        public static int OBJECT_ID         = 8;
        public static int METHOD_ID         = 4;
        public static int FIELD_ID          = 4;
        public static int FRAME_ID          = 4;

        // derivative VM specific type sizes

        public static int TAGGED_OBJECT_ID  = TAG + OBJECT_ID;

        public static int THREAD_ID         = OBJECT_ID;
        public static int THREAD_GROUP_ID   = OBJECT_ID;
        public static int STRING_ID         = OBJECT_ID;
        public static int CLASS_LOADER_ID   = OBJECT_ID;
        public static int CLASS_OBJECT_ID   = OBJECT_ID;
        public static int REFERENCE_TYPE_ID = OBJECT_ID;

        public static int CLASS_ID          = REFERENCE_TYPE_ID;
        public static int INTERFACE_ID      = REFERENCE_TYPE_ID;
        public static int ARRAY_ID          = REFERENCE_TYPE_ID;

        public static int LOCATION          = TAG + CLASS_ID + METHOD_ID + LOCATION_INDEX;

        /**
         * Calculate type sizes based on VM dependent basic type sizes.
         */
        public static void CalculateSizes() {

            TAGGED_OBJECT_ID  = TAG + OBJECT_ID;

            THREAD_ID         = OBJECT_ID;
            THREAD_GROUP_ID   = OBJECT_ID;
            STRING_ID         = OBJECT_ID;
            CLASS_LOADER_ID   = OBJECT_ID;
            CLASS_OBJECT_ID   = OBJECT_ID;
            REFERENCE_TYPE_ID = OBJECT_ID;

            CLASS_ID          = REFERENCE_TYPE_ID;
            INTERFACE_ID      = REFERENCE_TYPE_ID;
            ARRAY_ID          = REFERENCE_TYPE_ID;

            LOCATION          = TAG + CLASS_ID + METHOD_ID + LOCATION_INDEX;
        }

    }

    public static class ModifierFlag {

        public static final int PUBLIC                = 0x0001;
        public static final int PRIVATE               = 0x0002;
        public static final int PROTECTED             = 0x0004;
        public static final int STATIC                = 0x0008;
        public static final int FINAL                 = 0x0010;
        public static final int SUPER                 = 0x0020;
        public static final int VOLATILE              = 0x0040;
        public static final int TRANSIENT             = 0x0080;
        public static final int SYNCHRONIZED          = 0x0020;
        public static final int NATIVE                = 0x0100;
        public static final int INTERFACE             = 0x0200;
        public static final int ABSTRACT              = 0x0400;
        public static final int SYNTHETIC             = 0xF0000000;

        public static final int CLASS_MASK            = PUBLIC | FINAL | SUPER | INTERFACE | ABSTRACT;
        public static final int FIELD_MASK            = PUBLIC | PRIVATE | PROTECTED | STATIC | FINAL | VOLATILE | TRANSIENT;
        public static final int METHOD_MASK           = PUBLIC | PRIVATE | PROTECTED | STATIC | FINAL | SYNCHRONIZED | NATIVE | ABSTRACT;

    }

    public static class CommandSet {

        public static final byte VirtualMachine        = (byte)0x01;
        public static final byte ReferenceType         = (byte)0x02;
        public static final byte ClassType             = (byte)0x03;
        public static final byte ArrayType             = (byte)0x04;
        public static final byte InterfaceType         = (byte)0x05;
        public static final byte Method                = (byte)0x06;
        public static final byte Field                 = (byte)0x08;
        public static final byte ObjectReference       = (byte)0x09;
        public static final byte StringReference       = (byte)0x0A;
        public static final byte ThreadReference       = (byte)0x0B;
        public static final byte ThreadGroupReference  = (byte)0x0C;
        public static final byte ArrayReferemce        = (byte)0x0D;
        public static final byte ClassLoaderReference  = (byte)0x0E;
        public static final byte EventRequest          = (byte)0x0F;
        public static final byte StackFrame            = (byte)0x10;
        public static final byte ClassObjectReference  = (byte)0x11;
        public static final byte Event                 = (byte)0x40;

    }

    // command names, used only for debug output
    public static HashMap<Integer, String> commandNames = new HashMap<Integer, String>();

    static
    {
        commandNames.put(Command.ObjectReference.ReferringObjects, "ObjectReference.ReferringObjects");
        commandNames.put(Command.ReferenceType.Instances, "ReferenceType.Instances");
        commandNames.put(Command.ReferenceType.ClassFileVersion, "ReferenceType.ClassFileVersion");
        commandNames.put(Command.ReferenceType.ConstantPool, "ReferenceType.ConstantPool");
        commandNames.put(Command.ThreadReference.OwnedMonitorsStackDepthInfo, "ThreadReference.OwnedMonitorsStackDepthInfo");
        commandNames.put(Command.ThreadReference.ForceEarlyReturn, "ThreadReference.ForceEarlyReturn");
        commandNames.put(Command.VirtualMachine.InstanceCounts, "VirtualMachine.InstanceCounts");
    }

    public static class Command {

        public static class VirtualMachine {

            public static final int Version             = 0x0101;
            public static final int ClassesBySignature  = 0x0102;
            public static final int AllClasses          = 0x0103;
            public static final int AllThreads          = 0x0104;
            public static final int TopLevelThreadGroups  = 0x0105;
            public static final int Dispose             = 0x0106;
            public static final int IDSizes             = 0x0107;
            public static final int Suspend             = 0x0108;
            public static final int Resume              = 0x0109;
            public static final int Exit                = 0x010A;
            public static final int CreateString        = 0x010B;
            public static final int Capabilities        = 0x010C;
            public static final int ClassPaths          = 0x010D;
            public static final int DisposeObjects      = 0x010E;
            public static final int HoldEvents          = 0x010F;
            public static final int ReleaseEvents       = 0x0110;

            // since JDK-1.4
            public static final int CapabilitiesNew     = 0x0111;
            public static final int RedefineClasses     = 0x0112;
            public static final int SetDefaultStratum   = 0x0113;

            // since JDK-1.5
            public static final int AllClassesWithGeneric = 0x0114;

            // since JDK-1.6
            public static final int InstanceCounts      = 0x0115;
        }

        public static class ReferenceType {

            public static final int Signature           = 0x0201;
            public static final int ClassLoader         = 0x0202;
            public static final int Modifiers           = 0x0203;
            public static final int Fields              = 0x0204;
            public static final int Methods             = 0x0205;
            public static final int GetValues           = 0x0206;
            public static final int SourceFile          = 0x0207;
            public static final int NestedTypes         = 0x0208;
            public static final int Status              = 0x0209;
            public static final int Interfaces          = 0x020A;
            public static final int ClassObject         = 0x020B;

            // since JDK-1.4
            public static final int SourceDebugExtension = 0x020C;

            // since JDK-1.5
            public static final int SignatureWithGeneric = 0x020D;
            public static final int FieldsWithGeneric = 0x020E;
            public static final int MethodsWithGeneric = 0x020F;

            // since JDK-1.6
            public static final int Instances = 0x0210;
            public static final int ClassFileVersion = 0x0211;
            public static final int ConstantPool = 0x0212;
        }

        public static class ClassType {

            public static final int Superclass          = 0x0301;
            public static final int SetValues           = 0x0302;
            public static final int InvokeMethod        = 0x0303;
            public static final int NewInstance         = 0x0304;

        }

        public static class ArrayType {

            public static final int NewInstance         = 0x0401;

        }

        public static class InterfaceType {

        }

        public static class Method {

            public static final int LineTable           = 0x0601;
            public static final int VariableTable       = 0x0602;
            public static final int Bytecodes           = 0x0603;

            // since JDK-1.4
            public static final int IsObsolete          = 0x0604;

            // since JDK-1.5
            public static final int VariableTableWithGeneric = 0x0605;

        }

        public static class Field {

        }

        public static class ObjectReference {

            public static final int ReferenceType       = 0x0901;
            public static final int GetValues           = 0x0902;
            public static final int SetValues           = 0x0903;
            public static final int MonitorInfo         = 0x0905;
            public static final int InvokeMethod        = 0x0906;
            public static final int DisableCollection   = 0x0907;
            public static final int EnableCollection    = 0x0908;
            public static final int IsCollected         = 0x0909;

            // since JDK-1.6
            public static final int ReferringObjects         = 0x090A;
        }

        public static class StringReference {

            public static final int Value               = 0x0A01;

        }

        public static class ThreadReference {

            public static final int Name                = 0x0B01;
            public static final int Suspend             = 0x0B02;
            public static final int Resume              = 0x0B03;
            public static final int Status              = 0x0B04;
            public static final int ThreadGroup         = 0x0B05;
            public static final int Frames              = 0x0B06;
            public static final int FrameCount          = 0x0B07;
            public static final int OwnedMonitors       = 0x0B08;
            public static final int CurrentContendedMonitor = 0x0B09;
            public static final int Stop                = 0x0B0A;
            public static final int Interrupt           = 0x0B0B;
            public static final int SuspendCount        = 0x0B0C;
            public static final int PopTopFrame         = 0x0B0D;

            // since JDK-1.6
            public static final int OwnedMonitorsStackDepthInfo = 0x0B0D;
            public static final int ForceEarlyReturn = 0x0B0E;
        }

        public static class ThreadGroupReference {

            public static final int Name                = 0x0C01;
            public static final int Parent              = 0x0C02;
            public static final int Children            = 0x0C03;

        }

        public static class ArrayReference {

            public static final int Length              = 0x0D01;
            public static final int GetValues           = 0x0D02;
            public static final int SetValues           = 0x0D03;

        }

        public static class ClassLoaderReference {

            public static final int VisibleClasses      = 0x0E01;

        }

        public static class EventRequest {

            public static final int Set                 = 0x0F01;
            public static final int Clear               = 0x0F02;
            public static final int ClearAllBreakpoints = 0x0F03;

        }

        public static class StackFrame {

            public static final int GetValues           = 0x1001;
            public static final int SetValues           = 0x1002;
            public static final int ThisObject          = 0x1003;

            // since JDK-1.4
            public static final int PopFrames           = 0x1004;

        }

        public static class ClassObjectReference {

            public static final int ReflectedType       = 0x1101;

        }

        public static class Event {

            public static final int Composite           = 0x4064;

        }

    } // end of class Command

    public static class Capability {

        // common capabilities
        public static final int CAN_WATCH_FIELD_MODIFICATION        = 0;
        public static final int CAN_WATCH_FIELD_ACCESS              = 1;
        public static final int CAN_GET_BYTECODES                   = 2;
        public static final int CAN_GET_SYNTHETIC_ATTRIBUTE         = 3;
        public static final int CAN_GET_OWNED_MONITOR_INFO          = 4;
        public static final int CAN_GET_CURRENT_CONTENDED_MONITOR   = 5;
        public static final int CAN_GET_MONITOR_INFO                = 6;

        // new capabilities (since JDWP version 1.4)
        public static final int CAN_REDEFINE_CLASSES                = 7;
        public static final int CAN_ADD_METHODR_INFO                = 8;
        public static final int CAN_UNRESTRICTEDLY_REDEFINE_CLASSES = 9;
        public static final int CAN_POP_FRAMES                      = 10;
        public static final int CAN_USE_INSTANCE_FILTER             = 11;
        public static final int CAN_GET_SOURCE_DEBUG_EXTENSION      = 12;
        public static final int CAN_REQUEST_VMDEATH_EVENT           = 13;
        public static final int CAN_SET_DEFAULT_STRATUM             = 14;
    }

    public static class Location extends ByteBuffer {

        public static int TAG_OFFSET = 0;
        public static int CLASS_ID_OFFSET = TAG_OFFSET + JDWP.TypeSize.TAG;
        public static int METHOD_ID_OFFSET = CLASS_ID_OFFSET + JDWP.TypeSize.CLASS_ID;
        public static int INDEX_OFFSET = METHOD_ID_OFFSET + JDWP.TypeSize.METHOD_ID;

        private static void calculateOffsets() {
            CLASS_ID_OFFSET = TAG_OFFSET + JDWP.TypeSize.TAG;
            METHOD_ID_OFFSET = CLASS_ID_OFFSET + JDWP.TypeSize.CLASS_ID;
            INDEX_OFFSET = METHOD_ID_OFFSET + JDWP.TypeSize.METHOD_ID;
        }

        public Location(byte typeTag, long classID, long methodID, long index) {
            this();
            // 1 byte type tag
            putTag(typeTag);
            // classID
            putClassID(classID);
            // methodID
            putMethodID(methodID);
            // 8 bytes index
            putIndex(index);
        }

        public Location() {
            super(JDWP.TypeSize.LOCATION, 0);
            addBytes((byte)0, TypeSize.LOCATION);

            // calculate offsets for VM-dependent type sizes
            calculateOffsets();
        }

        public final byte getTag() {
            try {
                return getByte(TAG_OFFSET);
            } catch (BoundException e) {
                throw new Failure("Unable to get tag from location:\n\t" + e);
            }
        }

        public final long getClassID() {
            try {
                return getID(CLASS_ID_OFFSET, JDWP.TypeSize.CLASS_ID);
            } catch (BoundException e) {
                throw new Failure("Unable to get classID from location:\n\t" + e);
            }
        }

        public final long getMethodID() {
            try {
                return getID(METHOD_ID_OFFSET, JDWP.TypeSize.METHOD_ID);
            } catch (BoundException e) {
                throw new Failure("Unable to get methodID from location:\n\t" + e);
            }
        }

        public final long getIndex() {
            try {
                return getID(INDEX_OFFSET, JDWP.TypeSize.LOCATION_INDEX);
            } catch (BoundException e) {
                throw new Failure("Unable to get code index from location:\n\t" + e);
            }
        }

        public final void putTag(byte tag) {
            try {
                putByte(TAG_OFFSET, tag);
            } catch (BoundException e) {
                throw new Failure("Unable to put tag into location:\n\t" + e);
            }
        }

        public final void putClassID(long classID) {
            try {
                putID(CLASS_ID_OFFSET, classID, JDWP.TypeSize.CLASS_ID);
            } catch (BoundException e) {
                throw new Failure("Unable to put classID into location:\n\t" + e);
            }
        }

        public final void putMethodID(long methodID) {
            try {
                putID(METHOD_ID_OFFSET, methodID, JDWP.TypeSize.METHOD_ID);
            } catch (BoundException e) {
                throw new Failure("Unable to put methodID into location:\n\t" + e);
            }
        }

        public final void putIndex(long index) {
            try {
                putID(INDEX_OFFSET, index, JDWP.TypeSize.LOCATION_INDEX);
            } catch (BoundException e) {
                throw new Failure("Unable to put code index into location:\n\t" + e);
            }
        }

        public String toString() {
            return "Location("
            + "tag=" + getTag() + ", "
            + "classID=" + getClassID() + ", "
            + "methodID=" + getMethodID() + ", "
            + "index=" + getIndex()
            + ")";
        }

    } // end of class Location

    public static class UntaggedValue {

        public Object value = null;

        public UntaggedValue() {
        }

        public UntaggedValue(Object value) {
            this.value = value;
        }

        public Object getValue() {
            return value;
        }

        public int length(byte tag) {
            int valueSize = 0;
            try {
                switch (tag) {
                    case JDWP.Tag.BYTE: {
                        valueSize = JDWP.TypeSize.BYTE;
                    } break;
                    case JDWP.Tag.CHAR: {
                        valueSize = JDWP.TypeSize.CHAR;
                    } break;
                    case JDWP.Tag.FLOAT: {
                        valueSize = JDWP.TypeSize.FLOAT;
                    } break;
                    case JDWP.Tag.DOUBLE: {
                        valueSize = JDWP.TypeSize.DOUBLE;
                    } break;
                    case JDWP.Tag.INT: {
                        valueSize = JDWP.TypeSize.INT;
                    } break;
                    case JDWP.Tag.SHORT: {
                        valueSize = JDWP.TypeSize.SHORT;
                    } break;
                    case JDWP.Tag.BOOLEAN: {
                        valueSize = JDWP.TypeSize.BYTE;
                    } break;
                    case JDWP.Tag.LONG: {
                        valueSize = JDWP.TypeSize.LONG;
                    } break;
                    case JDWP.Tag.VOID: {
                        valueSize = 0;
                    } break;
                    case JDWP.Tag.ARRAY:
                    case JDWP.Tag.OBJECT:
                    case JDWP.Tag.STRING:
                    case JDWP.Tag.THREAD:
                    case JDWP.Tag.THREAD_GROUP:
                    case JDWP.Tag.CLASS_LOADER:
                    case JDWP.Tag.CLASS_OBJECT: {
                        valueSize = JDWP.TypeSize.OBJECT_ID;
                    } break;
                    default: {
                        throw new Failure("Unknown tag found while putting value into packet: " + tag);
                    }
                }
            } catch (ClassCastException e) {
                throw new Failure("Wrong tag " + tag + " found while putting value to packet: " + value);
            }
            return JDWP.TypeSize.TAG + valueSize;
        }

        public void addValueTo(Packet packet, byte tag) {
            if (value == null) {
                throw new Failure("Unable to put null value into packet: " + this);
            }
            try {
                switch (tag) {
                    case JDWP.Tag.BYTE: {
                        byte castedValue = ((Byte)value).byteValue();
                        packet.addByte(castedValue);
                    } break;
                    case JDWP.Tag.CHAR: {
                        char castedValue = ((Character)value).charValue();
                        packet.addChar(castedValue);
                    } break;
                    case JDWP.Tag.FLOAT: {
                        float castedValue = ((Float)value).floatValue();
                        packet.addFloat(castedValue);
                    } break;
                    case JDWP.Tag.DOUBLE: {
                        double castedValue = ((Double)value).doubleValue();
                        packet.addDouble(castedValue);
                    } break;
                    case JDWP.Tag.INT: {
                        int castedValue = ((Integer)value).intValue();
                        packet.addInt(castedValue);
                    } break;
                    case JDWP.Tag.SHORT: {
                        short castedValue = ((Short)value).shortValue();
                        packet.addShort(castedValue);
                    } break;
                    case JDWP.Tag.BOOLEAN: {
                        boolean castedValue = ((Boolean)value).booleanValue();
                        packet.addByte((byte)(castedValue? 1 : 0));
                    } break;
                    case JDWP.Tag.LONG: {
                        long castedValue = ((Long)value).longValue();
                        packet.addLong(castedValue);
                    } break;
                    case JDWP.Tag.VOID: {
                    } break;
                    case JDWP.Tag.ARRAY:
                    case JDWP.Tag.OBJECT:
                    case JDWP.Tag.STRING:
                    case JDWP.Tag.THREAD:
                    case JDWP.Tag.THREAD_GROUP:
                    case JDWP.Tag.CLASS_LOADER:
                    case JDWP.Tag.CLASS_OBJECT: {
                        long castedValue = ((Long)value).longValue();
                        packet.addObjectID(castedValue);
                    } break;
                    default: {
                        throw new Failure("Unknown tag found while putting value into packet: " + tag);
                    }
                }
            } catch (ClassCastException e) {
                throw new Failure("Wrong tag " + tag + " found while putting value to packet: " + value);
            }
        }

        public void getValueFrom(Packet packet, byte tag) throws BoundException {
            switch (tag) {
                case JDWP.Tag.BYTE: {
                    byte castedValue = packet.getByte();
                    value = new Byte(castedValue);
                } break;
                case JDWP.Tag.CHAR: {
                    char castedValue = packet.getChar();
                    value = new Character(castedValue);
                } break;
                case JDWP.Tag.FLOAT: {
                    float castedValue = packet.getFloat();
                    value = new Float(castedValue);
                } break;
                case JDWP.Tag.DOUBLE: {
                    double castedValue = packet.getDouble();
                    value = new Double(castedValue);
                } break;
                case JDWP.Tag.INT: {
                    int castedValue = packet.getInt();
                    value = new Integer(castedValue);
                } break;
                case JDWP.Tag.SHORT: {
                    short castedValue = packet.getShort();
                    value = new Short(castedValue);
                } break;
                case JDWP.Tag.BOOLEAN: {
                    byte castedValue = packet.getByte();
                    value = new Boolean(castedValue != 0);
                } break;
                case JDWP.Tag.LONG: {
                    long castedValue = packet.getLong();
                    value = new Long(castedValue);
                } break;
                case JDWP.Tag.VOID: {
                    value = new Long(0);
                } break;
                case JDWP.Tag.ARRAY:
                case JDWP.Tag.OBJECT:
                case JDWP.Tag.STRING:
                case JDWP.Tag.THREAD:
                case JDWP.Tag.THREAD_GROUP:
                case JDWP.Tag.CLASS_LOADER:
                case JDWP.Tag.CLASS_OBJECT: {
                    long castedValue = packet.getObjectID();
                    value = new Long(castedValue);
                } break;
                default: {
                    throw new Failure("Unknown tag found while reading value from packet: " + tag);
                }
            }
        }

        public String toString(byte tag) {
            if (value == null) {
                return "null";
            }
            String type = null;
            try {
                switch (tag) {
                    case JDWP.Tag.BYTE: {
                        type = "BYTE";
                    } break;
                    case JDWP.Tag.CHAR: {
                        type = "CHAR";
                    } break;
                    case JDWP.Tag.FLOAT: {
                        type = "FLOAT";
                    } break;
                    case JDWP.Tag.DOUBLE: {
                        type = "DOUBLE";
                    } break;
                    case JDWP.Tag.INT: {
                        type = "INT";
                    } break;
                    case JDWP.Tag.SHORT: {
                        type = "SHORT";
                    } break;
                    case JDWP.Tag.BOOLEAN: {
                        type = "BOOLEAN";
                    } break;
                    case JDWP.Tag.LONG: {
                        type = "LONG";
                    } break;
                    case JDWP.Tag.VOID: {
                        type = "VOID";
                    } break;
                    case JDWP.Tag.ARRAY: {
                        type = "ARRAY_ID";
                    } break;
                    case JDWP.Tag.OBJECT: {
                        type = "OBJECT_ID";
                    } break;
                    case JDWP.Tag.STRING: {
                        type = "STRING_ID";
                    } break;
                    case JDWP.Tag.THREAD: {
                        type = "THREAD_ID";
                    } break;
                    case JDWP.Tag.THREAD_GROUP: {
                        type = "THREAD_GROUP_ID";
                    } break;
                    case JDWP.Tag.CLASS_LOADER: {
                        type = "CLASS_LOADER_ID";
                    } break;
                    case JDWP.Tag.CLASS_OBJECT: {
                        type = "CLASS_OBJECT_ID";
                    } break;
                    default: {
                        throw new Failure("Unknown tag found while converting value into string: " + tag);
                    }
                }
                return "(" + type + ")" + value;
            } catch (ClassCastException e) {
                throw new Failure("Wrong tag " + tag + " found while putting value to packet: " + value);
            }
        }

    } // end of class Value

    public static class Value extends UntaggedValue {

        public static final int TAG_OFFSET = 0;
        public static final int VALUE_OFFSET = TAG_OFFSET + TypeSize.TAG;

        public byte tag = 0;

        public Value() {
        }

        public Value(byte tag, Object value) {
            super(value);
            this.tag = tag;
        }

        public byte getTag() {
            return tag;
        }

        public int length() {
            return super.length(tag);
        }

        public void addValueTo(Packet packet) {
            if (value == null) {
                throw new Failure("Unable to put null value into packet: " + this);
            }
            packet.addByte(tag);
            super.addValueTo(packet, tag);
        }

        public void getValueFrom(Packet packet) throws BoundException {
            tag = packet.getByte();
            super.getValueFrom(packet, tag);
        }

        public String toString() {
            return super.toString(tag);
        }

    } // end of class Value

} // end of class JDWP
