ss: Add support for custom state values
authorGenevieve Bastien <gbastien+lttng@versatic.net>
Thu, 7 Apr 2016 18:51:37 +0000 (14:51 -0400)
committerGenevieve Bastien <gbastien+lttng@versatic.net>
Tue, 17 May 2016 18:49:09 +0000 (14:49 -0400)
This patch allows users to register their own state types and
they will be written to intervals.

Change-Id: If3228f4819039689f6aa3b18b8959c42a2ab7d87
Signed-off-by: Matthew Khouzam <matthew.khouzam@ericsson.com>
Signed-off-by: Genevieve Bastien <gbastien+lttng@versatic.net>
Reviewed-on: https://git.eclipse.org/r/69253
Reviewed-by: Hudson CI
Reviewed-by: Jean-Christian Kouame <jean-christian.kouame@ericsson.com>
Tested-by: Jean-Christian Kouame <jean-christian.kouame@ericsson.com>
Reviewed-by: Alexandre Montplaisir <alexmonthy@efficios.com>
statesystem/org.eclipse.tracecompass.statesystem.core.tests/src/org/eclipse/tracecompass/statesystem/core/tests/backend/StateHistoryBackendTestBase.java
statesystem/org.eclipse.tracecompass.statesystem.core.tests/src/org/eclipse/tracecompass/statesystem/core/tests/statevalue/CustomStateValueTest.java [new file with mode: 0644]
statesystem/org.eclipse.tracecompass.statesystem.core.tests/stubs/org/eclipse/tracecompass/statesystem/core/tests/stubs/statevalues/CustomStateValueStub.java [new file with mode: 0644]
statesystem/org.eclipse.tracecompass.statesystem.core.tests/stubs/org/eclipse/tracecompass/statesystem/core/tests/stubs/statevalues/package-info.java [new file with mode: 0644]
statesystem/org.eclipse.tracecompass.statesystem.core/META-INF/MANIFEST.MF
statesystem/org.eclipse.tracecompass.statesystem.core/src/org/eclipse/tracecompass/internal/provisional/statesystem/core/statevalue/CustomStateValue.java [new file with mode: 0644]
statesystem/org.eclipse.tracecompass.statesystem.core/src/org/eclipse/tracecompass/internal/provisional/statesystem/core/statevalue/package-info.java [new file with mode: 0644]
statesystem/org.eclipse.tracecompass.statesystem.core/src/org/eclipse/tracecompass/internal/statesystem/core/backend/historytree/HTInterval.java
statesystem/org.eclipse.tracecompass.statesystem.core/src/org/eclipse/tracecompass/internal/statesystem/core/statevalue/package-info.java [new file with mode: 0644]

index e8ec0d8c4f7a7f938e10869a74139ab28f395f42..8f0db65c9fe6369a26805c714185ab32c71e9fde 100644 (file)
@@ -27,6 +27,7 @@ import org.eclipse.tracecompass.statesystem.core.interval.ITmfStateInterval;
 import org.eclipse.tracecompass.statesystem.core.interval.TmfStateInterval;
 import org.eclipse.tracecompass.statesystem.core.statevalue.ITmfStateValue;
 import org.eclipse.tracecompass.statesystem.core.statevalue.TmfStateValue;
+import org.eclipse.tracecompass.statesystem.core.tests.stubs.statevalues.CustomStateValueStub;
 import org.junit.Test;
 
 import com.google.common.collect.ImmutableList;
@@ -269,6 +270,10 @@ public abstract class StateHistoryBackendTestBase {
         int longQuark = 1;
         int doubleQuark = 2;
         int strQuark = 3;
+        int customQuark = 4;
+        /* Register custom factory and create a custom state value */
+        CustomStateValueStub.registerFactory();
+        ITmfStateValue customVal = new CustomStateValueStub(10, "a string");
 
         try {
             IStateHistoryBackend backend = getBackendForBuilding(startTime);
@@ -306,6 +311,14 @@ public abstract class StateHistoryBackendTestBase {
             assertEquals("String interval end time", startTime + timeStep, interval.getEndTime());
             assertEquals("String interval value", STR_VAL1, interval.getStateValue());
 
+            /* Custom state values */
+            backend.insertPastState(startTime, startTime + timeStep, customQuark, customVal);
+            interval = backend.doSingularQuery(startTime, customQuark);
+
+            assertEquals("Custom interval start time", startTime, interval.getStartTime());
+            assertEquals("Custom interval end time", startTime + timeStep, interval.getEndTime());
+            assertEquals("Custom interval value", customVal, interval.getStateValue());
+
             /*
              * Add other intervals for the int quark and query at different
              * times
@@ -343,7 +356,12 @@ public abstract class StateHistoryBackendTestBase {
         int longQuark = 1;
         int doubleQuark = 2;
         int strQuark = 3;
-        int nbAttribs = 4;
+        int customQuark = 4;
+        int nbAttribs = 5;
+        /* Register custom factory and create custom state values */
+        CustomStateValueStub.registerFactory();
+        ITmfStateValue customVal = new CustomStateValueStub(10, "a string");
+        ITmfStateValue customVal2 = new CustomStateValueStub(Short.MAX_VALUE, "another string");
 
         try {
             IStateHistoryBackend backend = getBackendForBuilding(startTime);
@@ -357,10 +375,12 @@ public abstract class StateHistoryBackendTestBase {
                     new TmfStateInterval(startTime, startTime + timeStep, longQuark, LONG_VAL1),
                     new TmfStateInterval(startTime, startTime + timeStep, doubleQuark, DOUBLE_VAL1),
                     new TmfStateInterval(startTime, startTime + timeStep, strQuark, STR_VAL1),
+                    new TmfStateInterval(startTime, startTime + timeStep, customQuark, customVal),
                     new TmfStateInterval(nextStart, endTime, intQuark, INT_VAL2),
                     new TmfStateInterval(nextStart, endTime, longQuark, LONG_VAL2),
                     new TmfStateInterval(nextStart, endTime, doubleQuark, DOUBLE_VAL2),
-                    new TmfStateInterval(nextStart, endTime, strQuark, STR_VAL2)));
+                    new TmfStateInterval(nextStart, endTime, strQuark, STR_VAL2),
+                    new TmfStateInterval(nextStart, endTime, customQuark, customVal2)));
 
             backend.finishedBuilding(endTime);
 
@@ -390,6 +410,10 @@ public abstract class StateHistoryBackendTestBase {
             interval = intervals.get(strQuark);
             assertNotNull(interval);
             assertEquals("String value after read", STR_VAL1, interval.getStateValue());
+            /* Custom */
+            interval = intervals.get(customQuark);
+            assertNotNull(interval);
+            assertEquals("String value after read", customVal, interval.getStateValue());
 
             /* Do a full query at the end and verify the values */
             backendQuery.doQuery(intervals, endTime);
@@ -406,6 +430,10 @@ public abstract class StateHistoryBackendTestBase {
             interval = intervals.get(strQuark);
             assertNotNull(interval);
             assertEquals("String value after read", STR_VAL2, interval.getStateValue());
+            /* Custom */
+            interval = intervals.get(customQuark);
+            assertNotNull(interval);
+            assertEquals("String value after read", customVal2, interval.getStateValue());
 
         } catch (TimeRangeException | IOException | StateSystemDisposedException e) {
             fail(e.getMessage());
diff --git a/statesystem/org.eclipse.tracecompass.statesystem.core.tests/src/org/eclipse/tracecompass/statesystem/core/tests/statevalue/CustomStateValueTest.java b/statesystem/org.eclipse.tracecompass.statesystem.core.tests/src/org/eclipse/tracecompass/statesystem/core/tests/statevalue/CustomStateValueTest.java
new file mode 100644 (file)
index 0000000..51fc7a2
--- /dev/null
@@ -0,0 +1,110 @@
+/*******************************************************************************
+ * Copyright (c) 2016 École Polytechnique de Montréal
+ *
+ * All rights reserved. This program and the accompanying materials are
+ * made available under the terms of the Eclipse Public License v1.0 which
+ * accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+
+package org.eclipse.tracecompass.statesystem.core.tests.statevalue;
+
+import static org.junit.Assert.assertEquals;
+
+import java.nio.ByteBuffer;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.tracecompass.internal.provisional.statesystem.core.statevalue.CustomStateValue;
+import org.eclipse.tracecompass.internal.provisional.statesystem.core.statevalue.ISafeByteBufferReader;
+import org.eclipse.tracecompass.internal.provisional.statesystem.core.statevalue.ISafeByteBufferWriter;
+import org.eclipse.tracecompass.internal.provisional.statesystem.core.statevalue.SafeByteBufferFactory;
+import org.eclipse.tracecompass.statesystem.core.statevalue.ITmfStateValue;
+import org.eclipse.tracecompass.statesystem.core.statevalue.ITmfStateValue.Type;
+import org.eclipse.tracecompass.statesystem.core.statevalue.TmfStateValue;
+import org.eclipse.tracecompass.statesystem.core.tests.stubs.statevalues.CustomStateValueStub;
+import org.junit.Test;
+
+/**
+ * Test the {@link CustomStateValue} class
+ *
+ * @author Geneviève Bastien
+ */
+public class CustomStateValueTest extends StateValueTestBase {
+
+    private static final CustomStateValue VALUE = new CustomStateValueStub(3, "MyString");
+
+    @Override
+    protected @NonNull ITmfStateValue getStateValueFixture() {
+        return VALUE;
+    }
+
+    @Override
+    protected @NonNull Type getStateValueType() {
+        return Type.CUSTOM;
+    }
+
+    /**
+     * Test serialization/unserialization with the factory present
+     */
+    @Test
+    public void testSerialization() {
+        CustomStateValueStub.registerFactory();
+        ByteBuffer buffer = ByteBuffer.allocate(1024);
+        ISafeByteBufferWriter safeBufferWriter = SafeByteBufferFactory.wrapWriter(buffer, VALUE.getSerializedSize());
+        VALUE.serialize(safeBufferWriter);
+
+        // Reset buffer
+        buffer.flip();
+        ISafeByteBufferReader safeBufferReader = SafeByteBufferFactory.wrapReader(buffer, VALUE.getSerializedSize());
+        TmfStateValue read = CustomStateValue.readSerializedValue(safeBufferReader);
+        assertEquals("Custom state value: no factory", VALUE, read);
+    }
+
+    /**
+     * Test serialization/unserialization if no factory is available
+     */
+    @Test
+    public void testNoFactoryAvailable() {
+        CustomStateValueStub.unregisterFactory();
+        ByteBuffer buffer = ByteBuffer.allocate(1024);
+        ISafeByteBufferWriter safeBufferWriter = SafeByteBufferFactory.wrapWriter(buffer, VALUE.getSerializedSize());
+        VALUE.serialize(safeBufferWriter);
+
+        // Reset buffer
+        buffer.flip();
+        ISafeByteBufferReader safeBufferReader = SafeByteBufferFactory.wrapReader(buffer, VALUE.getSerializedSize());
+        TmfStateValue read = CustomStateValue.readSerializedValue(safeBufferReader);
+        assertEquals("Custom state value: no factory", TmfStateValue.nullValue(), read);
+    }
+
+    /**
+     * Test the exception when asking a size too large
+     */
+    @Test(expected = ArrayIndexOutOfBoundsException.class)
+    public void testSerializedTooLarge() {
+        String alphabet = "abcdefghijklmnopqrstuvwxyz";
+        StringBuilder builder = new StringBuilder();
+        while (builder.length() < Short.MAX_VALUE) {
+            builder.append(alphabet);
+        }
+        CustomStateValue sv = new CustomStateValueStub(3, builder.toString());
+        sv.getSerializedSize();
+    }
+
+    /**
+     * Test the illegal argument exception with custom forbidden custom IDs
+     */
+    @Test(expected = IllegalArgumentException.class)
+    public void testWrongCustomId() {
+        CustomStateValue.registerCustomFactory((byte) 0, CustomStateValueStub.FACTORY);
+    }
+
+    /**
+     * Test the illegal argument exception with custom forbidden custom IDs
+     */
+    @Test(expected = IllegalArgumentException.class)
+    public void testWrongCustomId2() {
+        CustomStateValue.registerCustomFactory((byte) 20, CustomStateValueStub.FACTORY);
+    }
+
+}
diff --git a/statesystem/org.eclipse.tracecompass.statesystem.core.tests/stubs/org/eclipse/tracecompass/statesystem/core/tests/stubs/statevalues/CustomStateValueStub.java b/statesystem/org.eclipse.tracecompass.statesystem.core.tests/stubs/org/eclipse/tracecompass/statesystem/core/tests/stubs/statevalues/CustomStateValueStub.java
new file mode 100644 (file)
index 0000000..a92218c
--- /dev/null
@@ -0,0 +1,128 @@
+/*******************************************************************************
+ * Copyright (c) 2016 École Polytechnique de Montréal
+ *
+ * All rights reserved. This program and the accompanying materials are
+ * made available under the terms of the Eclipse Public License v1.0 which
+ * accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+
+package org.eclipse.tracecompass.statesystem.core.tests.stubs.statevalues;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.tracecompass.internal.provisional.statesystem.core.statevalue.CustomStateValue;
+import org.eclipse.tracecompass.internal.provisional.statesystem.core.statevalue.ISafeByteBufferWriter;
+import org.eclipse.tracecompass.internal.provisional.statesystem.core.statevalue.SafeByteBufferFactory;
+import org.eclipse.tracecompass.statesystem.core.exceptions.StateValueTypeException;
+import org.eclipse.tracecompass.statesystem.core.statevalue.ITmfStateValue;
+
+/**
+ * A custom state value stub containing an integer field and a string.
+ *
+ * Before using this stub in a unit test when there is a need to
+ * serialize/unserialize this class, the factory should be registered by calling
+ * the {@link #registerFactory()} method
+ *
+ * @author Geneviève Bastien
+ */
+public class CustomStateValueStub extends CustomStateValue {
+
+    /**
+     * The factory to rebuild the state value
+     */
+    public static final CustomStateValueFactory FACTORY = (b) -> {
+        int val = b.getInt();
+        String str = b.getString();
+        return new CustomStateValueStub(val, str);
+    };
+
+    /** Custom type ID */
+    private static final byte CUSTOM_TYPE_ID = 87;
+
+    private final int fIntField;
+    private final String fStringField;
+
+    /**
+     * Constructor
+     *
+     * @param val
+     *            the integer value
+     * @param str
+     *            the string value
+     */
+    public CustomStateValueStub(int val, String str) {
+        fIntField = val;
+        fStringField = str;
+    }
+
+    /**
+     * Registers the factory for this custom state value type
+     */
+    public static void registerFactory() {
+        CustomStateValue.registerCustomFactory(CUSTOM_TYPE_ID, FACTORY);
+    }
+
+    /**
+     * Registers the factory for this custom state value type
+     */
+    public static void unregisterFactory() {
+        CustomStateValue.unregisterCustomFactory(CUSTOM_TYPE_ID);
+    }
+
+    @Override
+    public int compareTo(@Nullable ITmfStateValue o) {
+        if (o == null) {
+            throw new IllegalArgumentException();
+        }
+        if (!(o instanceof CustomStateValueStub)) {
+            throw new StateValueTypeException("Need a TestCustomStateValue object to compare to"); //$NON-NLS-1$
+        }
+        CustomStateValueStub other = (CustomStateValueStub) o;
+        int cmp = Integer.compare(fIntField, other.fIntField);
+        if (cmp == 0) {
+            cmp = fStringField.compareTo(other.fStringField);
+        }
+        return cmp;
+    }
+
+    @Override
+    public boolean equals(@Nullable Object arg0) {
+        if (!(arg0 instanceof CustomStateValueStub)) {
+            return false;
+        }
+        CustomStateValueStub tcsv = (CustomStateValueStub) arg0;
+        return (fIntField == tcsv.fIntField) && fStringField.equals(tcsv.fStringField);
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + fIntField;
+        result = prime * result + fStringField.hashCode();
+        return result;
+    }
+
+    @Override
+    protected @NonNull Byte getCustomTypeId() {
+        return CUSTOM_TYPE_ID;
+    }
+
+    @Override
+    public String toString() {
+        return "[" + fIntField + "," + fStringField + "]";
+    }
+
+    @Override
+    protected void serializeValue(@NonNull ISafeByteBufferWriter buffer) {
+        buffer.putInt(fIntField);
+        buffer.putString(fStringField);
+    }
+
+    @Override
+    protected int getSerializedValueSize() {
+        return Integer.BYTES + SafeByteBufferFactory.getStringSizeInBuffer(fStringField);
+    }
+
+}
diff --git a/statesystem/org.eclipse.tracecompass.statesystem.core.tests/stubs/org/eclipse/tracecompass/statesystem/core/tests/stubs/statevalues/package-info.java b/statesystem/org.eclipse.tracecompass.statesystem.core.tests/stubs/org/eclipse/tracecompass/statesystem/core/tests/stubs/statevalues/package-info.java
new file mode 100644 (file)
index 0000000..3534038
--- /dev/null
@@ -0,0 +1,11 @@
+/*******************************************************************************
+ * Copyright (c) 2015 École Polytechnique de Montréal
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+
+@org.eclipse.jdt.annotation.NonNullByDefault
+package org.eclipse.tracecompass.statesystem.core.tests.stubs.statevalues;
index c9be6f07bb7427a13dd21ea8794d2be0e18c2836..f5b6d398c3a6e935ad13d5e94729d374398d93b0 100644 (file)
@@ -21,5 +21,6 @@ Export-Package: org.eclipse.tracecompass.internal.provisional.statesystem.core.s
  org.eclipse.tracecompass.statesystem.core.exceptions,
  org.eclipse.tracecompass.statesystem.core.interval,
  org.eclipse.tracecompass.statesystem.core.statevalue
-Import-Package: com.google.common.base,
+Import-Package: com.google.common.annotations;version="15.0.0",
+ com.google.common.base,
  com.google.common.collect;version="12.0.0"
diff --git a/statesystem/org.eclipse.tracecompass.statesystem.core/src/org/eclipse/tracecompass/internal/provisional/statesystem/core/statevalue/CustomStateValue.java b/statesystem/org.eclipse.tracecompass.statesystem.core/src/org/eclipse/tracecompass/internal/provisional/statesystem/core/statevalue/CustomStateValue.java
new file mode 100644 (file)
index 0000000..8a0dc79
--- /dev/null
@@ -0,0 +1,210 @@
+/*******************************************************************************
+ * Copyright (c) 2016 École Polytechnique de Montréal
+ *
+ * All rights reserved. This program and the accompanying materials are
+ * made available under the terms of the Eclipse Public License v1.0 which
+ * accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+
+package org.eclipse.tracecompass.internal.provisional.statesystem.core.statevalue;
+
+import java.nio.BufferOverflowException;
+
+import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.tracecompass.internal.statesystem.core.Activator;
+import org.eclipse.tracecompass.statesystem.core.statevalue.TmfStateValue;
+
+import com.google.common.annotations.VisibleForTesting;
+
+/**
+ * This allows to define custom state values.
+ *
+ * Each sub-class should define a {@link CustomStateValueFactory} that has to be
+ * registered, for example by the analysis using this state value type, through
+ * the
+ * {@link CustomStateValue#registerCustomFactory(byte, CustomStateValueFactory)}
+ * method.
+ *
+ * Note to implementers: These state values are meant to be used in data
+ * structures that save information on objects that varies in time, like a state
+ * system. It will often be made persistent on disk at some point, so it is
+ * suggested to make the child classes immutable.
+ *
+ * Data structures using these custom values will often keep the values in a
+ * transient state before they are sent to be persisted. For persistence, it
+ * will request the size of the value, then write its bytes to a buffer. Once
+ * the size is requested, the value is about to be saved, so it is important
+ * that its value and size do not change after that, as it may get in an
+ * incoherent state, or, worse throw runtime exceptions.
+ *
+ * @author Geneviève Bastien
+ * @since 2.0
+ */
+public abstract class CustomStateValue extends TmfStateValue {
+
+    /* Minimum size of the state value */
+    private static final int MIN_SIZE = Byte.BYTES;
+
+    /**
+     * Custom value factory interface. Each custom state value should have a
+     * corresponding factory to re-create the object from a ByteBuffer
+     */
+    @FunctionalInterface
+    public interface CustomStateValueFactory {
+
+        /**
+         * Get the custom state value from a byte buffer
+         *
+         * @param buffer
+         *            the byte buffer
+         * @return the custom state value
+         */
+        CustomStateValue readCustomValue(ISafeByteBufferReader buffer);
+    }
+
+    private static final CustomStateValueFactory[] CUSTOM_FACTORIES = new CustomStateValueFactory[256];
+
+    /**
+     * Register the custom factory that will be reused to create instances of
+     * custom state value objects
+     *
+     * @param customId
+     *            The ID of this custom type. For possible values of this ID,
+     *            see {@link #getCustomTypeId()}
+     * @param factory
+     *            The factory used to create a new custom type object of this ID
+     */
+    public static void registerCustomFactory(byte customId, CustomStateValueFactory factory) {
+        if (customId >= 0 && customId <= 20) {
+            throw new IllegalArgumentException("State value IDs between 0 and 20 are reserved for built-in state value types"); //$NON-NLS-1$
+        }
+        CustomStateValueFactory currentFactory = CUSTOM_FACTORIES[customId - Byte.MIN_VALUE];
+        if (currentFactory == null) {
+            CUSTOM_FACTORIES[customId - Byte.MIN_VALUE] = factory;
+        } else if (currentFactory != factory) {
+            throw new IllegalStateException("Already a custom factory registered for " + Byte.toString(customId)); //$NON-NLS-1$
+        }
+    }
+
+    /**
+     * Unregisters the custom factory
+     *
+     * @param customId
+     *            The ID of this custom type
+     */
+    @VisibleForTesting
+    protected static void unregisterCustomFactory(byte customId) {
+        CUSTOM_FACTORIES[customId - Byte.MIN_VALUE] = null;
+    }
+
+    /**
+     * Get the custom factory for a byte
+     *
+     * @param customId
+     *            the custom factory key
+     * @return the custom factory or null if none is registered for the key
+     */
+    public static @Nullable CustomStateValueFactory getCustomFactory(byte customId) {
+        return CUSTOM_FACTORIES[customId - Byte.MIN_VALUE];
+    }
+
+    /**
+     * Read a serialized value (obtained with the
+     * {@link #serialize(ISafeByteBufferWriter)} method) into a real
+     * {@link CustomStateValue} object.
+     *
+     * @param buffer
+     *            The byte buffer to read from
+     * @return The state value object
+     */
+    public static TmfStateValue readSerializedValue(ISafeByteBufferReader buffer) {
+        /* the first byte = the custom type id */
+        byte customType = buffer.get();
+        CustomStateValueFactory customFactory = CustomStateValue.getCustomFactory(customType);
+        if (customFactory == null) {
+            Activator.getDefault().logWarning("Custom factory for type " + customType + " does not exist"); //$NON-NLS-1$ //$NON-NLS-2$
+            return TmfStateValue.nullValue();
+        }
+
+        return customFactory.readCustomValue(buffer);
+    }
+
+    /**
+     * Get custom type id. The value should be between {@link Byte#MIN_VALUE}
+     * and {@link Byte#MAX_VALUE}, but not between {@code 0} and {@code 20} that
+     * are reserved for built-in state value types.
+     *
+     * @return the custom type ID
+     */
+    protected abstract Byte getCustomTypeId();
+
+    /**
+     * Serialize this state value into the byte buffer. This method should only
+     * write the payload of the state value and should match what will be read
+     * by the factory when deserializing.
+     *
+     * This serialized value must contain all the payload of the state value
+     *
+     * @param buffer
+     *            The ByteBuffer to write the values to
+     */
+    protected abstract void serializeValue(ISafeByteBufferWriter buffer);
+
+    /**
+     * Get the serialized size of this state value. The size must not exceed
+     * {@link Short#MAX_VALUE - MIN_SIZE}, otherwise serialization might throw a
+     * {@link BufferOverflowException}
+     *
+     * @return The serialized size of this state value
+     */
+    protected abstract int getSerializedValueSize();
+
+    /**
+     * Serialize this custom state value into a byte buffer. It calls the
+     * serialization of the state value itself and adds the specific fields to
+     * interpret that byte array.
+     *
+     * The format of the value is [custom type (byte)][payload]
+     *
+     * The total serialized size should never exceed {@link Short#MAX_VALUE}
+     *
+     * @param buffer
+     *            The ByteBuffer to write the values to
+     *
+     * @throws BufferOverflowException
+     *             If the serialized size of the value ends up larger than the
+     *             maximum of {@link Short#MAX_VALUE} and the implementation has
+     *             no way of handling it
+     */
+    public final void serialize(ISafeByteBufferWriter buffer) {
+        buffer.put(getCustomTypeId());
+        serializeValue(buffer);
+    }
+
+    /**
+     * Get the serialized size of this state value. This size will be used to
+     * allow the buffer that will be sent to the
+     * {@link #serialize(ISafeByteBufferWriter)} method
+     *
+     * @return The size of the serialized value
+     */
+    public final int getSerializedSize() {
+        int size = getSerializedValueSize();
+        if (size > Short.MAX_VALUE - MIN_SIZE) {
+            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$
+        }
+        return size + MIN_SIZE;
+    }
+
+    @Override
+    public final Type getType() {
+        return Type.CUSTOM;
+    }
+
+    @Override
+    public boolean isNull() {
+        return false;
+    }
+
+}
diff --git a/statesystem/org.eclipse.tracecompass.statesystem.core/src/org/eclipse/tracecompass/internal/provisional/statesystem/core/statevalue/package-info.java b/statesystem/org.eclipse.tracecompass.statesystem.core/src/org/eclipse/tracecompass/internal/provisional/statesystem/core/statevalue/package-info.java
new file mode 100644 (file)
index 0000000..62aadcf
--- /dev/null
@@ -0,0 +1,12 @@
+/*******************************************************************************
+ * Copyright (c) 2014 Ericsson
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Ericsson - Initial API and implementation
+ *******************************************************************************/
+@org.eclipse.jdt.annotation.NonNullByDefault package org.eclipse.tracecompass.internal.provisional.statesystem.core.statevalue;
index 44eada2b9cc8c2b76d099b91d88a03577f500335..8b2139a8bcf2ff991cc8ff61f01cc1b79e44d70b 100644 (file)
@@ -19,6 +19,10 @@ import java.io.IOException;
 import java.nio.ByteBuffer;
 
 import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.tracecompass.internal.provisional.statesystem.core.statevalue.CustomStateValue;
+import org.eclipse.tracecompass.internal.provisional.statesystem.core.statevalue.ISafeByteBufferReader;
+import org.eclipse.tracecompass.internal.provisional.statesystem.core.statevalue.ISafeByteBufferWriter;
+import org.eclipse.tracecompass.internal.provisional.statesystem.core.statevalue.SafeByteBufferFactory;
 import org.eclipse.tracecompass.statesystem.core.exceptions.TimeRangeException;
 import org.eclipse.tracecompass.statesystem.core.interval.ITmfStateInterval;
 import org.eclipse.tracecompass.statesystem.core.statevalue.ITmfStateValue;
@@ -104,6 +108,8 @@ public final class HTInterval implements ITmfStateInterval, Comparable<HTInterva
              */
             return (minSize + sv.unboxStr().getBytes().length + 2);
         case CUSTOM:
+            /* Length of serialized value (short) + state value */
+            return (minSize + Short.BYTES + ((CustomStateValue) sv).getSerializedSize());
         default:
             /*
              * It's very important that we know how to write the state value in
@@ -154,21 +160,16 @@ public final class HTInterval implements ITmfStateInterval, Comparable<HTInterva
      *             If there was an error reading from the buffer
      */
     public static final HTInterval readFrom(ByteBuffer buffer) throws IOException {
-        HTInterval interval;
-        long intervalStart, intervalEnd;
-        int attribute;
         TmfStateValue value;
-        byte valueType;
-        byte array[];
 
         int posStart = buffer.position();
         /* Read the Data Section entry */
-        intervalStart = buffer.getLong();
-        intervalEnd = buffer.getLong();
-        attribute = buffer.getInt();
+        long intervalStart = buffer.getLong();
+        long intervalEnd = buffer.getLong();
+        int attribute = buffer.getInt();
 
         /* Read the 'type' of the value, then react accordingly */
-        valueType = buffer.get();
+        byte valueType = buffer.get();
         switch (valueType) {
 
         case TYPE_NULL:
@@ -179,11 +180,11 @@ public final class HTInterval implements ITmfStateInterval, Comparable<HTInterva
             value = TmfStateValue.newValueInt(buffer.getInt());
             break;
 
-        case TYPE_STRING:
+        case TYPE_STRING: {
             /* the first byte = the size to read */
             int valueSize = buffer.get();
 
-            array = new byte[valueSize];
+            byte[] array = new byte[valueSize];
             buffer.get(array);
             value = TmfStateValue.newValueString(new String(array));
 
@@ -193,6 +194,7 @@ public final class HTInterval implements ITmfStateInterval, Comparable<HTInterva
                 throw new IOException(errMsg);
             }
             break;
+        }
 
         case TYPE_LONG:
             /* Go read the matching entry in the Strings section of the block */
@@ -204,18 +206,22 @@ public final class HTInterval implements ITmfStateInterval, Comparable<HTInterva
             value = TmfStateValue.newValueDouble(buffer.getDouble());
             break;
 
-        case TYPE_CUSTOM:
+        case TYPE_CUSTOM: {
+            short valueSize = buffer.getShort();
+            ISafeByteBufferReader safeBuffer = SafeByteBufferFactory.wrapReader(buffer, valueSize);
+            value = CustomStateValue.readSerializedValue(safeBuffer);
+            break;
+        }
         default:
             /* Unknown data, better to not make anything up... */
             throw new IOException(errMsg);
         }
 
         try {
-            interval = new HTInterval(intervalStart, intervalEnd, attribute, value, buffer.position() - posStart);
+            return new HTInterval(intervalStart, intervalEnd, attribute, value, buffer.position() - posStart);
         } catch (TimeRangeException e) {
             throw new IOException(errMsg);
         }
-        return interval;
     }
 
     /**
@@ -252,7 +258,7 @@ public final class HTInterval implements ITmfStateInterval, Comparable<HTInterva
             buffer.putInt(sv.unboxInt());
             break;
 
-        case TYPE_STRING:
+        case TYPE_STRING: {
             String string = sv.unboxStr();
             byte[] strArray = string.getBytes();
 
@@ -264,6 +270,7 @@ public final class HTInterval implements ITmfStateInterval, Comparable<HTInterva
             buffer.put(strArray);
             buffer.put((byte) 0);
             break;
+        }
 
         case TYPE_LONG:
             buffer.putLong(sv.unboxLong());
@@ -273,6 +280,14 @@ public final class HTInterval implements ITmfStateInterval, Comparable<HTInterva
             buffer.putDouble(sv.unboxDouble());
             break;
 
+        case TYPE_CUSTOM: {
+            int size = ((CustomStateValue) sv).getSerializedSize();
+            buffer.putShort((short) size);
+            ISafeByteBufferWriter safeBuffer = SafeByteBufferFactory.wrapWriter(buffer, size);
+            ((CustomStateValue) sv).serialize(safeBuffer);
+            break;
+        }
+
         default:
             break;
         }
diff --git a/statesystem/org.eclipse.tracecompass.statesystem.core/src/org/eclipse/tracecompass/internal/statesystem/core/statevalue/package-info.java b/statesystem/org.eclipse.tracecompass.statesystem.core/src/org/eclipse/tracecompass/internal/statesystem/core/statevalue/package-info.java
new file mode 100644 (file)
index 0000000..b7b1a62
--- /dev/null
@@ -0,0 +1,12 @@
+/*******************************************************************************
+ * Copyright (c) 2014 Ericsson
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Ericsson - Initial API and implementation
+ *******************************************************************************/
+@org.eclipse.jdt.annotation.NonNullByDefault package org.eclipse.tracecompass.internal.statesystem.core.statevalue;
This page took 0.041138 seconds and 5 git commands to generate.