1 /*******************************************************************************
2 * Copyright (c) 2016 École Polytechnique de Montréal
4 * All rights reserved. This program and the accompanying materials are
5 * made available under the terms of the Eclipse Public License v1.0 which
6 * accompanies this distribution, and is available at
7 * http://www.eclipse.org/legal/epl-v10.html
8 *******************************************************************************/
10 package org
.eclipse
.tracecompass
.internal
.provisional
.statesystem
.core
.statevalue
;
12 import java
.nio
.BufferOverflowException
;
14 import org
.eclipse
.jdt
.annotation
.Nullable
;
15 import org
.eclipse
.tracecompass
.internal
.provisional
.datastore
.core
.serialization
.ISafeByteBufferReader
;
16 import org
.eclipse
.tracecompass
.internal
.provisional
.datastore
.core
.serialization
.ISafeByteBufferWriter
;
17 import org
.eclipse
.tracecompass
.internal
.statesystem
.core
.Activator
;
18 import org
.eclipse
.tracecompass
.statesystem
.core
.statevalue
.TmfStateValue
;
20 import com
.google
.common
.annotations
.VisibleForTesting
;
23 * This allows to define custom state values.
25 * Each sub-class should define a {@link CustomStateValueFactory} that has to be
26 * registered, for example by the analysis using this state value type, through
28 * {@link CustomStateValue#registerCustomFactory(byte, CustomStateValueFactory)}
31 * Note to implementers: These state values are meant to be used in data
32 * structures that save information on objects that varies in time, like a state
33 * system. It will often be made persistent on disk at some point, so it is
34 * suggested to make the child classes immutable.
36 * Data structures using these custom values will often keep the values in a
37 * transient state before they are sent to be persisted. For persistence, it
38 * will request the size of the value, then write its bytes to a buffer. Once
39 * the size is requested, the value is about to be saved, so it is important
40 * that its value and size do not change after that, as it may get in an
41 * incoherent state, or, worse throw runtime exceptions.
43 * @author Geneviève Bastien
46 public abstract class CustomStateValue
extends TmfStateValue
{
48 /* Minimum size of the state value */
49 private static final int MIN_SIZE
= Byte
.BYTES
;
52 * Custom value factory interface. Each custom state value should have a
53 * corresponding factory to re-create the object from a ByteBuffer
56 public interface CustomStateValueFactory
{
59 * Get the custom state value from a byte buffer
63 * @return the custom state value
65 CustomStateValue
readCustomValue(ISafeByteBufferReader buffer
);
68 private static final CustomStateValueFactory
[] CUSTOM_FACTORIES
= new CustomStateValueFactory
[256];
71 * Register the custom factory that will be reused to create instances of
72 * custom state value objects
75 * The ID of this custom type. For possible values of this ID,
76 * see {@link #getCustomTypeId()}
78 * The factory used to create a new custom type object of this ID
80 public static void registerCustomFactory(byte customId
, CustomStateValueFactory factory
) {
81 if (customId
>= 0 && customId
<= 20) {
82 throw new IllegalArgumentException("State value IDs between 0 and 20 are reserved for built-in state value types"); //$NON-NLS-1$
84 CustomStateValueFactory currentFactory
= CUSTOM_FACTORIES
[customId
- Byte
.MIN_VALUE
];
85 if (currentFactory
== null) {
86 CUSTOM_FACTORIES
[customId
- Byte
.MIN_VALUE
] = factory
;
87 } else if (currentFactory
!= factory
) {
88 throw new IllegalStateException("Already a custom factory registered for " + Byte
.toString(customId
)); //$NON-NLS-1$
93 * Unregisters the custom factory
96 * The ID of this custom type
99 protected static void unregisterCustomFactory(byte customId
) {
100 CUSTOM_FACTORIES
[customId
- Byte
.MIN_VALUE
] = null;
104 * Get the custom factory for a byte
107 * the custom factory key
108 * @return the custom factory or null if none is registered for the key
110 public static @Nullable CustomStateValueFactory
getCustomFactory(byte customId
) {
111 return CUSTOM_FACTORIES
[customId
- Byte
.MIN_VALUE
];
115 * Read a serialized value (obtained with the
116 * {@link #serialize(ISafeByteBufferWriter)} method) into a real
117 * {@link CustomStateValue} object.
120 * The byte buffer to read from
121 * @return The state value object
123 public static TmfStateValue
readSerializedValue(ISafeByteBufferReader buffer
) {
124 /* the first byte = the custom type id */
125 byte customType
= buffer
.get();
126 CustomStateValueFactory customFactory
= CustomStateValue
.getCustomFactory(customType
);
127 if (customFactory
== null) {
128 Activator
.getDefault().logWarning("Custom factory for type " + customType
+ " does not exist"); //$NON-NLS-1$ //$NON-NLS-2$
129 return TmfStateValue
.nullValue();
132 return customFactory
.readCustomValue(buffer
);
136 * Get custom type id. The value should be between {@link Byte#MIN_VALUE}
137 * and {@link Byte#MAX_VALUE}, but not between {@code 0} and {@code 20} that
138 * are reserved for built-in state value types.
140 * @return the custom type ID
142 protected abstract Byte
getCustomTypeId();
145 * Serialize this state value into the byte buffer. This method should only
146 * write the payload of the state value and should match what will be read
147 * by the factory when deserializing.
149 * This serialized value must contain all the payload of the state value
152 * The ByteBuffer to write the values to
154 protected abstract void serializeValue(ISafeByteBufferWriter buffer
);
157 * Get the serialized size of this state value. The size must not exceed
158 * {@link Short#MAX_VALUE - MIN_SIZE}, otherwise serialization might throw a
159 * {@link BufferOverflowException}
161 * @return The serialized size of this state value
163 protected abstract int getSerializedValueSize();
166 * Serialize this custom state value into a byte buffer. It calls the
167 * serialization of the state value itself and adds the specific fields to
168 * interpret that byte array.
170 * The format of the value is [custom type (byte)][payload]
172 * The total serialized size should never exceed {@link Short#MAX_VALUE}
175 * The ByteBuffer to write the values to
177 * @throws BufferOverflowException
178 * If the serialized size of the value ends up larger than the
179 * maximum of {@link Short#MAX_VALUE} and the implementation has
180 * no way of handling it
182 public final void serialize(ISafeByteBufferWriter buffer
) {
183 buffer
.put(getCustomTypeId());
184 serializeValue(buffer
);
188 * Get the serialized size of this state value. This size will be used to
189 * allow the buffer that will be sent to the
190 * {@link #serialize(ISafeByteBufferWriter)} method
192 * @return The size of the serialized value
194 public final int getSerializedSize() {
195 int size
= getSerializedValueSize();
196 if (size
> Short
.MAX_VALUE
- MIN_SIZE
) {
197 throw new ArrayIndexOutOfBoundsException("Serialized state value is larger than the maximum allowed size of " + (Short
.MAX_VALUE
- MIN_SIZE
) + ": " + size
); //$NON-NLS-1$ //$NON-NLS-2$
199 return size
+ MIN_SIZE
;
203 public final Type
getType() {
208 public boolean isNull() {