ss: Add support for custom state values
[deliverable/tracecompass.git] / statesystem / org.eclipse.tracecompass.statesystem.core / src / org / eclipse / tracecompass / internal / provisional / statesystem / core / statevalue / CustomStateValue.java
CommitLineData
d69a6555
GB
1/*******************************************************************************
2 * Copyright (c) 2016 École Polytechnique de Montréal
3 *
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 *******************************************************************************/
9
10package org.eclipse.tracecompass.internal.provisional.statesystem.core.statevalue;
11
12import java.nio.BufferOverflowException;
13
14import org.eclipse.jdt.annotation.Nullable;
15import org.eclipse.tracecompass.internal.statesystem.core.Activator;
16import org.eclipse.tracecompass.statesystem.core.statevalue.TmfStateValue;
17
18import com.google.common.annotations.VisibleForTesting;
19
20/**
21 * This allows to define custom state values.
22 *
23 * Each sub-class should define a {@link CustomStateValueFactory} that has to be
24 * registered, for example by the analysis using this state value type, through
25 * the
26 * {@link CustomStateValue#registerCustomFactory(byte, CustomStateValueFactory)}
27 * method.
28 *
29 * Note to implementers: These state values are meant to be used in data
30 * structures that save information on objects that varies in time, like a state
31 * system. It will often be made persistent on disk at some point, so it is
32 * suggested to make the child classes immutable.
33 *
34 * Data structures using these custom values will often keep the values in a
35 * transient state before they are sent to be persisted. For persistence, it
36 * will request the size of the value, then write its bytes to a buffer. Once
37 * the size is requested, the value is about to be saved, so it is important
38 * that its value and size do not change after that, as it may get in an
39 * incoherent state, or, worse throw runtime exceptions.
40 *
41 * @author Geneviève Bastien
42 * @since 2.0
43 */
44public abstract class CustomStateValue extends TmfStateValue {
45
46 /* Minimum size of the state value */
47 private static final int MIN_SIZE = Byte.BYTES;
48
49 /**
50 * Custom value factory interface. Each custom state value should have a
51 * corresponding factory to re-create the object from a ByteBuffer
52 */
53 @FunctionalInterface
54 public interface CustomStateValueFactory {
55
56 /**
57 * Get the custom state value from a byte buffer
58 *
59 * @param buffer
60 * the byte buffer
61 * @return the custom state value
62 */
63 CustomStateValue readCustomValue(ISafeByteBufferReader buffer);
64 }
65
66 private static final CustomStateValueFactory[] CUSTOM_FACTORIES = new CustomStateValueFactory[256];
67
68 /**
69 * Register the custom factory that will be reused to create instances of
70 * custom state value objects
71 *
72 * @param customId
73 * The ID of this custom type. For possible values of this ID,
74 * see {@link #getCustomTypeId()}
75 * @param factory
76 * The factory used to create a new custom type object of this ID
77 */
78 public static void registerCustomFactory(byte customId, CustomStateValueFactory factory) {
79 if (customId >= 0 && customId <= 20) {
80 throw new IllegalArgumentException("State value IDs between 0 and 20 are reserved for built-in state value types"); //$NON-NLS-1$
81 }
82 CustomStateValueFactory currentFactory = CUSTOM_FACTORIES[customId - Byte.MIN_VALUE];
83 if (currentFactory == null) {
84 CUSTOM_FACTORIES[customId - Byte.MIN_VALUE] = factory;
85 } else if (currentFactory != factory) {
86 throw new IllegalStateException("Already a custom factory registered for " + Byte.toString(customId)); //$NON-NLS-1$
87 }
88 }
89
90 /**
91 * Unregisters the custom factory
92 *
93 * @param customId
94 * The ID of this custom type
95 */
96 @VisibleForTesting
97 protected static void unregisterCustomFactory(byte customId) {
98 CUSTOM_FACTORIES[customId - Byte.MIN_VALUE] = null;
99 }
100
101 /**
102 * Get the custom factory for a byte
103 *
104 * @param customId
105 * the custom factory key
106 * @return the custom factory or null if none is registered for the key
107 */
108 public static @Nullable CustomStateValueFactory getCustomFactory(byte customId) {
109 return CUSTOM_FACTORIES[customId - Byte.MIN_VALUE];
110 }
111
112 /**
113 * Read a serialized value (obtained with the
114 * {@link #serialize(ISafeByteBufferWriter)} method) into a real
115 * {@link CustomStateValue} object.
116 *
117 * @param buffer
118 * The byte buffer to read from
119 * @return The state value object
120 */
121 public static TmfStateValue readSerializedValue(ISafeByteBufferReader buffer) {
122 /* the first byte = the custom type id */
123 byte customType = buffer.get();
124 CustomStateValueFactory customFactory = CustomStateValue.getCustomFactory(customType);
125 if (customFactory == null) {
126 Activator.getDefault().logWarning("Custom factory for type " + customType + " does not exist"); //$NON-NLS-1$ //$NON-NLS-2$
127 return TmfStateValue.nullValue();
128 }
129
130 return customFactory.readCustomValue(buffer);
131 }
132
133 /**
134 * Get custom type id. The value should be between {@link Byte#MIN_VALUE}
135 * and {@link Byte#MAX_VALUE}, but not between {@code 0} and {@code 20} that
136 * are reserved for built-in state value types.
137 *
138 * @return the custom type ID
139 */
140 protected abstract Byte getCustomTypeId();
141
142 /**
143 * Serialize this state value into the byte buffer. This method should only
144 * write the payload of the state value and should match what will be read
145 * by the factory when deserializing.
146 *
147 * This serialized value must contain all the payload of the state value
148 *
149 * @param buffer
150 * The ByteBuffer to write the values to
151 */
152 protected abstract void serializeValue(ISafeByteBufferWriter buffer);
153
154 /**
155 * Get the serialized size of this state value. The size must not exceed
156 * {@link Short#MAX_VALUE - MIN_SIZE}, otherwise serialization might throw a
157 * {@link BufferOverflowException}
158 *
159 * @return The serialized size of this state value
160 */
161 protected abstract int getSerializedValueSize();
162
163 /**
164 * Serialize this custom state value into a byte buffer. It calls the
165 * serialization of the state value itself and adds the specific fields to
166 * interpret that byte array.
167 *
168 * The format of the value is [custom type (byte)][payload]
169 *
170 * The total serialized size should never exceed {@link Short#MAX_VALUE}
171 *
172 * @param buffer
173 * The ByteBuffer to write the values to
174 *
175 * @throws BufferOverflowException
176 * If the serialized size of the value ends up larger than the
177 * maximum of {@link Short#MAX_VALUE} and the implementation has
178 * no way of handling it
179 */
180 public final void serialize(ISafeByteBufferWriter buffer) {
181 buffer.put(getCustomTypeId());
182 serializeValue(buffer);
183 }
184
185 /**
186 * Get the serialized size of this state value. This size will be used to
187 * allow the buffer that will be sent to the
188 * {@link #serialize(ISafeByteBufferWriter)} method
189 *
190 * @return The size of the serialized value
191 */
192 public final int getSerializedSize() {
193 int size = getSerializedValueSize();
194 if (size > Short.MAX_VALUE - MIN_SIZE) {
195 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$
196 }
197 return size + MIN_SIZE;
198 }
199
200 @Override
201 public final Type getType() {
202 return Type.CUSTOM;
203 }
204
205 @Override
206 public boolean isNull() {
207 return false;
208 }
209
210}
This page took 0.03402 seconds and 5 git commands to generate.