diff --git a/trace-server/org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core.tests/src/org/eclipse/tracecompass/incubator/trace/server/jersey/rest/core/tests/services/GenericXYDataProviderServiceTest.java b/trace-server/org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core.tests/src/org/eclipse/tracecompass/incubator/trace/server/jersey/rest/core/tests/services/GenericXYDataProviderServiceTest.java index 7f8321452..d3398415e 100644 --- a/trace-server/org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core.tests/src/org/eclipse/tracecompass/incubator/trace/server/jersey/rest/core/tests/services/GenericXYDataProviderServiceTest.java +++ b/trace-server/org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core.tests/src/org/eclipse/tracecompass/incubator/trace/server/jersey/rest/core/tests/services/GenericXYDataProviderServiceTest.java @@ -34,14 +34,14 @@ import org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core.tests.stubs.EntryStub; import org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core.tests.stubs.ExperimentModelStub; import org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core.tests.stubs.IAxisDomainStub; +import org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core.tests.stubs.RangeStub; +import org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core.tests.stubs.TmfXYAxisDescriptionStub; import org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core.tests.stubs.XyEntryModelStub; import org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core.tests.stubs.XyEntryStub; -import org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core.tests.stubs.XyTreeOutputResponseStub; -import org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core.tests.stubs.ISamplingStub; -import org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core.tests.stubs.TmfXYAxisDescriptionStub; import org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core.tests.stubs.XyModelStub; import org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core.tests.stubs.XyOutputResponseStub; import org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core.tests.stubs.XySeriesStub; +import org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core.tests.stubs.XyTreeOutputResponseStub; import org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core.tests.utils.RestServerTest; import org.eclipse.tracecompass.internal.tmf.core.Activator; import org.eclipse.tracecompass.tmf.core.model.StyleProperties; @@ -227,16 +227,17 @@ public void testCallStackFunctionDensityDataProvider() throws InterruptedExcepti assertEquals("Name mismatch", "UNKNOWN_PID", seriesStub.getName()); // Validate xValues - ISamplingStub xValues = seriesStub.getXValues(); - assertTrue("xValues should be a RangesStub", xValues instanceof ISamplingStub.RangesStub); - List expectedRanges = Arrays.asList( - new ISamplingStub.RangesStub.RangeStub(0L, 1195708549L), - new ISamplingStub.RangesStub.RangeStub(1195708550L, 2391417098L), - new ISamplingStub.RangesStub.RangeStub(2391417099L, 3587125647L), - new ISamplingStub.RangesStub.RangeStub(3587125648L, 4782834196L), - new ISamplingStub.RangesStub.RangeStub(4782834197L, 5978542746L) + List xValues = seriesStub.getXRanges(); + assertNotNull(xValues); + assertFalse(xValues.isEmpty()); + List expectedRanges = Arrays.asList( + new RangeStub(0L, 1195708549L), + new RangeStub(1195708550L, 2391417098L), + new RangeStub(2391417099L, 3587125647L), + new RangeStub(3587125648L, 4782834196L), + new RangeStub(4782834197L, 5978542746L) ); - List actualRanges = ((ISamplingStub.RangesStub) xValues).getRanges(); + List actualRanges = xValues; assertEquals("Range size mismatch", expectedRanges.size(), actualRanges.size()); assertEquals("Range size mismatch", expectedRanges.size(), actualRanges.size()); for (int i = 0; i < expectedRanges.size(); i++) { diff --git a/trace-server/org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core.tests/src/org/eclipse/tracecompass/incubator/trace/server/jersey/rest/core/tests/stubs/ISamplingStub.java b/trace-server/org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core.tests/src/org/eclipse/tracecompass/incubator/trace/server/jersey/rest/core/tests/stubs/ISamplingStub.java deleted file mode 100644 index feb4f8b75..000000000 --- a/trace-server/org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core.tests/src/org/eclipse/tracecompass/incubator/trace/server/jersey/rest/core/tests/stubs/ISamplingStub.java +++ /dev/null @@ -1,195 +0,0 @@ -/********************************************************************** - * Copyright (c) 2025 Ericsson - * - * All rights reserved. This program and the accompanying materials are - * made available under the terms of the Eclipse Public License 2.0 which - * accompanies this distribution, and is available at - * https://www.eclipse.org/legal/epl-2.0/ - * - * SPDX-License-Identifier: EPL-2.0 - **********************************************************************/ -package org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core.tests.stubs; - -import java.io.Serializable; -import java.util.Arrays; -import java.util.List; -import java.util.Objects; - -import org.eclipse.jdt.annotation.Nullable; -import org.eclipse.tracecompass.tmf.core.model.ISampling; - -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; - -/** - * Stub version of {@link ISampling}. - * - * @author Siwei Zhang - */ -@JsonSerialize(using = SamplingStubSerializer.class) -@JsonDeserialize(using = SamplingStubDeserializer.class) -public sealed interface ISamplingStub extends Serializable permits - ISamplingStub.TimestampsStub, - ISamplingStub.CategoriesStub, - ISamplingStub.RangesStub { - - /** - * Get the number of sampling points - * - * @return number of points - */ - int size(); - - /** - * Timestamp-based sampling. - */ - final class TimestampsStub implements ISamplingStub { - private static final long serialVersionUID = -8242136490356720296L; - - private final long[] fTimestamps; - - public TimestampsStub(long[] timestamps) { - this.fTimestamps = Objects.requireNonNull(timestamps); - } - - public long[] getTimestamps() { - return fTimestamps; - } - - @Override - public int size() { - return fTimestamps.length; - } - - @Override - public boolean equals(@Nullable Object obj) { - return (this == obj) || (obj instanceof TimestampsStub other && - Arrays.equals(this.fTimestamps, other.fTimestamps)); - } - - @Override - public int hashCode() { - return Arrays.hashCode(fTimestamps); - } - - @Override - public String toString() { - return "Timestamps" + Arrays.toString(fTimestamps); //$NON-NLS-1$ - } - } - - /** - * Categorical sampling (e.g., names). - */ - final class CategoriesStub implements ISamplingStub { - private static final long serialVersionUID = 3751152643508688051L; - - private final List fCategories; - - public CategoriesStub(List categories) { - this.fCategories = Objects.requireNonNull(categories); - } - - public List getCategories() { - return fCategories; - } - - @Override - public int size() { - return fCategories.size(); - } - - @Override - public boolean equals(@Nullable Object obj) { - return (this == obj) || (obj instanceof CategoriesStub other && - Objects.equals(this.fCategories, other.fCategories)); - } - - @Override - public int hashCode() { - return Objects.hash(fCategories); - } - - @Override - public String toString() { - return "Categories" + fCategories.toString(); //$NON-NLS-1$ - } - } - - /** - * Range sampling, representing start-end pairs. - */ - final class RangesStub implements ISamplingStub { - private static final long serialVersionUID = 3434126540189939098L; - - private final List fRanges; - - public RangesStub(List ranges) { - this.fRanges = Objects.requireNonNull(ranges); - } - - public List getRanges() { - return fRanges; - } - - @Override - public int size() { - return fRanges.size(); - } - - @Override - public boolean equals(@Nullable Object obj) { - return (this == obj) || (obj instanceof RangesStub other && - Objects.equals(this.fRanges, other.fRanges)); - } - - @Override - public int hashCode() { - return Objects.hash(fRanges); - } - - @Override - public String toString() { - return "Ranges" + fRanges.toString(); //$NON-NLS-1$ - } - - /** - * Stub representing a range. - */ - public static final class RangeStub implements Serializable { - private static final long serialVersionUID = 1L; - - private final long fStart; - private final long fEnd; - - public RangeStub(long start, long end) { - this.fStart = start; - this.fEnd = end; - } - - public long getStart() { - return fStart; - } - - public long getEnd() { - return fEnd; - } - - @Override - public boolean equals(@Nullable Object obj) { - return (this == obj) || (obj instanceof RangeStub other && - fStart == other.fStart && fEnd == other.fEnd); - } - - @Override - public int hashCode() { - return Objects.hash(fStart, fEnd); - } - - @Override - public String toString() { - return "[" + fStart + ", " + fEnd + "]"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ - } - } - } -} diff --git a/trace-server/org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core.tests/src/org/eclipse/tracecompass/incubator/trace/server/jersey/rest/core/tests/stubs/RangeStub.java b/trace-server/org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core.tests/src/org/eclipse/tracecompass/incubator/trace/server/jersey/rest/core/tests/stubs/RangeStub.java new file mode 100644 index 000000000..c53cb5506 --- /dev/null +++ b/trace-server/org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core.tests/src/org/eclipse/tracecompass/incubator/trace/server/jersey/rest/core/tests/stubs/RangeStub.java @@ -0,0 +1,76 @@ +/********************************************************************** + * Copyright (c) 2025 Ericsson + * + * All rights reserved. This program and the accompanying materials are + * made available under the terms of the Eclipse Public License 2.0 which + * accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + **********************************************************************/ +package org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core.tests.stubs; + +import java.io.Serializable; +import java.util.Objects; + +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.tracecompass.tmf.core.model.ISampling.Range; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * Stub version of {@link Range}. + * + * @author Siwei Zhang + */ +public final class RangeStub implements Serializable { + private static final long serialVersionUID = 1L; + + private final long fStart; + private final long fEnd; + + /** + * Constructor + * + * @param start + * the start value + * @param end + * the end value + */ + @JsonCreator + public RangeStub(@JsonProperty("start") Long start, @JsonProperty("end") Long end) { + this.fStart = start; + this.fEnd = end; + } + + /** + * @return the start value + */ + public long getStart() { + return fStart; + } + + /** + * @return the end value + */ + public long getEnd() { + return fEnd; + } + + @Override + public boolean equals(@Nullable Object obj) { + return (this == obj) || (obj instanceof RangeStub other && + fStart == other.fStart && fEnd == other.fEnd); + } + + @Override + public int hashCode() { + return Objects.hash(fStart, fEnd); + } + + @Override + public String toString() { + return "[" + fStart + ", " + fEnd + "]"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + } +} diff --git a/trace-server/org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core.tests/src/org/eclipse/tracecompass/incubator/trace/server/jersey/rest/core/tests/stubs/SamplingStubDeserializer.java b/trace-server/org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core.tests/src/org/eclipse/tracecompass/incubator/trace/server/jersey/rest/core/tests/stubs/SamplingStubDeserializer.java deleted file mode 100644 index 831a7a4da..000000000 --- a/trace-server/org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core.tests/src/org/eclipse/tracecompass/incubator/trace/server/jersey/rest/core/tests/stubs/SamplingStubDeserializer.java +++ /dev/null @@ -1,70 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2025 Ericsson and others - * - * All rights reserved. This program and the accompanying materials are - * made available under the terms of the Eclipse Public License 2.0 which - * accompanies this distribution, and is available at - * https://www.eclipse.org/legal/epl-2.0/ - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ - -package org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core.tests.stubs; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; - -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.core.JsonToken; -import com.fasterxml.jackson.databind.DeserializationContext; -import com.fasterxml.jackson.databind.JsonDeserializer; - -/** - * Deserializer for SamplingStub. - * - * @author Siwei Zhang - */ -public class SamplingStubDeserializer extends JsonDeserializer { - - @Override - public ISamplingStub deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { - JsonToken token = p.getCurrentToken(); - if (token != JsonToken.START_ARRAY) { - ctxt.reportInputMismatch(ISamplingStub.class, "Expected array for SamplingStub"); - } - - token = p.nextToken(); - if (token == JsonToken.VALUE_NUMBER_INT) { - List timestamps = new ArrayList<>(); - do { - timestamps.add(p.getLongValue()); - } while (p.nextToken() != JsonToken.END_ARRAY); - long[] ts = timestamps.stream().mapToLong(Long::longValue).toArray(); - return new ISamplingStub.TimestampsStub(ts); - - } else if (token == JsonToken.VALUE_STRING) { - List categories = new ArrayList<>(); - do { - categories.add(p.getText()); - } while (p.nextToken() != JsonToken.END_ARRAY); - return new ISamplingStub.CategoriesStub(categories); - - } else if (token == JsonToken.START_ARRAY) { - List ranges = new ArrayList<>(); - while (token != JsonToken.END_ARRAY) { - p.nextToken(); // start - long start = p.getLongValue(); - p.nextToken(); // end - long end = p.getLongValue(); - p.nextToken(); // end of inner array - ranges.add(new ISamplingStub.RangesStub.RangeStub(start, end)); - token = p.nextToken(); // next outer token - } - return new ISamplingStub.RangesStub(ranges); - } - - ctxt.reportInputMismatch(ISamplingStub.class, "Unrecognized structure for SamplingStub"); - return null; - } -} diff --git a/trace-server/org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core.tests/src/org/eclipse/tracecompass/incubator/trace/server/jersey/rest/core/tests/stubs/SamplingStubSerializer.java b/trace-server/org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core.tests/src/org/eclipse/tracecompass/incubator/trace/server/jersey/rest/core/tests/stubs/SamplingStubSerializer.java deleted file mode 100644 index 34f78d0af..000000000 --- a/trace-server/org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core.tests/src/org/eclipse/tracecompass/incubator/trace/server/jersey/rest/core/tests/stubs/SamplingStubSerializer.java +++ /dev/null @@ -1,60 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2025 Ericsson and others - * - * All rights reserved. This program and the accompanying materials are - * made available under the terms of the Eclipse Public License 2.0 which - * accompanies this distribution, and is available at - * https://www.eclipse.org/legal/epl-2.0/ - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ - -package org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core.tests.stubs; - -import java.io.IOException; - -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.databind.SerializerProvider; -import com.fasterxml.jackson.databind.ser.std.StdSerializer; - -/** - * Serializer for SamplingStub. Matches the protocol format used in real - * {@link SamplingStubSerializer} - * - * @author Siwei Zhang - */ -public class SamplingStubSerializer extends StdSerializer { - - private static final long serialVersionUID = 1L; - - public SamplingStubSerializer() { - super(ISamplingStub.class); - } - - @Override - public void serialize(ISamplingStub value, JsonGenerator gen, SerializerProvider provider) throws IOException { - if (value instanceof ISamplingStub.TimestampsStub timestamps) { - gen.writeArray(timestamps.getTimestamps(), 0, timestamps.getTimestamps().length); - - } else if (value instanceof ISamplingStub.CategoriesStub categories) { - gen.writeStartArray(); - for (String category : categories.getCategories()) { - gen.writeString(category); - } - gen.writeEndArray(); - - } else if (value instanceof ISamplingStub.RangesStub ranges) { - gen.writeStartArray(); - for (ISamplingStub.RangesStub.RangeStub range : ranges.getRanges()) { - gen.writeStartArray(); - gen.writeNumber(range.getStart()); - gen.writeNumber(range.getEnd()); - gen.writeEndArray(); - } - gen.writeEndArray(); - - } else { - throw new IllegalArgumentException("Unknown SamplingStub type: " + value.getClass().getName()); - } - } -} diff --git a/trace-server/org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core.tests/src/org/eclipse/tracecompass/incubator/trace/server/jersey/rest/core/tests/stubs/XySeriesStub.java b/trace-server/org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core.tests/src/org/eclipse/tracecompass/incubator/trace/server/jersey/rest/core/tests/stubs/XySeriesStub.java index 7882362ef..c097b8c86 100644 --- a/trace-server/org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core.tests/src/org/eclipse/tracecompass/incubator/trace/server/jersey/rest/core/tests/stubs/XySeriesStub.java +++ b/trace-server/org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core.tests/src/org/eclipse/tracecompass/incubator/trace/server/jersey/rest/core/tests/stubs/XySeriesStub.java @@ -14,6 +14,8 @@ import java.util.List; import java.util.Objects; +import org.eclipse.jdt.annotation.Nullable; + import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; @@ -29,7 +31,9 @@ public class XySeriesStub implements Serializable { private final String fName; private final int fId; - private final ISamplingStub fSampling; + private final long[] fXValues; + private final List fCategories; + private final List fRanges; private final List fYValues; private final OutputElementStyleStub fStyle; private final TmfXYAxisDescriptionStub fXAxisDescription; @@ -43,7 +47,11 @@ public class XySeriesStub implements Serializable { * @param id * The unique ID of the series * @param xValues - * The values for the x axis of this series + * The values for the x axis of this series (timestamps) + * @param categories + * The values for the x axis of this series (categories) + * @param ranges + * The values for the x axis of this series (ranges) * @param yValues * The values for the y axis of this series * @param style @@ -56,14 +64,21 @@ public class XySeriesStub implements Serializable { @JsonCreator public XySeriesStub(@JsonProperty("seriesName") String name, @JsonProperty("seriesId") Integer id, - @JsonProperty("xValues") ISamplingStub xValues, + @JsonProperty("xValues") long[] xValues, + @JsonProperty("xCategories") List categories, + @JsonProperty("xRanges") List ranges, @JsonProperty("yValues") List yValues, @JsonProperty("style") OutputElementStyleStub style, @JsonProperty("xValuesDescription") TmfXYAxisDescriptionStub xAxisDescription, @JsonProperty("yValuesDescription") TmfXYAxisDescriptionStub yAxisDescription) { fName = Objects.requireNonNull(name, "The 'seriesName' json field was not set"); fId = Objects.requireNonNull(id, "The 'seriesId' json field was not set"); - fSampling = Objects.requireNonNull(xValues, "The 'xValues' json field was not set"); + fXValues = xValues; + fCategories = categories; + fRanges = ranges; + if (fXValues == null && fCategories == null && fRanges == null) { + throw new IllegalArgumentException("Neither xValues, xCategories or xRanges were set"); + } fYValues = Objects.requireNonNull(yValues, "The 'yValues' json field was not set"); fStyle = Objects.requireNonNull(style, "The 'style' json field was not set"); fXAxisDescription = xAxisDescription; @@ -89,14 +104,31 @@ public int getId() { } /** - * Get the x values of the series + * Get the x values of the series as list of timestamps * * @return The values on the x axis */ - public ISamplingStub getXValues() { - return fSampling; + public long[] getXValues() { + return fXValues; + } + + /** + * Get the x categories of the series + * + * @return The values on the x axis as list of categories + */ + public @Nullable List getXCategories() { + return fCategories; } + /** + * Get the x ranges of the series + * + * @return The values on the x axis as list of ranges + */ + public @Nullable List getXRanges() { + return fRanges; + } /** * Get the y values of the series * diff --git a/trace-server/org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core.tests/src/org/eclipse/tracecompass/incubator/trace/server/jersey/rest/core/tests/webapp/SamplingSerializerTest.java b/trace-server/org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core.tests/src/org/eclipse/tracecompass/incubator/trace/server/jersey/rest/core/tests/webapp/SamplingSerializerTest.java index 2a6336554..9790f2352 100644 --- a/trace-server/org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core.tests/src/org/eclipse/tracecompass/incubator/trace/server/jersey/rest/core/tests/webapp/SamplingSerializerTest.java +++ b/trace-server/org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core.tests/src/org/eclipse/tracecompass/incubator/trace/server/jersey/rest/core/tests/webapp/SamplingSerializerTest.java @@ -11,8 +11,10 @@ package org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core.tests.webapp; -import static org.junit.Assert.*; +import static org.junit.Assert.assertEquals; +import java.io.IOException; +import java.io.Serializable; import java.util.List; import org.eclipse.tracecompass.incubator.internal.trace.server.jersey.rest.core.webapp.SamplingSerializer; @@ -24,18 +26,22 @@ import org.junit.Before; import org.junit.Test; +import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializerProvider; import com.fasterxml.jackson.databind.module.SimpleModule; +import com.fasterxml.jackson.databind.ser.std.StdSerializer; /** - * Test suite for {@link SamplingSerializer} and {@link SamplingDeserializer}. + * Test suite for {@link SamplingSerializer}. *

* Validates JSON round-trip behavior for different {@link ISampling} * subtypes: {@link Timestamps}, {@link Categories}, and {@link Ranges}. * * @author Siwei Zhang */ +@SuppressWarnings("null") public class SamplingSerializerTest { private ObjectMapper fMapper; @@ -49,53 +55,85 @@ public void setup() { fMapper = new ObjectMapper(); SimpleModule module = new SimpleModule(); module.addSerializer(ISampling.class, new SamplingSerializer()); + module.addSerializer(TestSampling.class, new TestSamplingSerializer()); fMapper.registerModule(module); } /** - * Test round-trip serialization and deserialization for - * {@link ISampling.Timestamps}. The format is a flat array of @NonNull Longs. + * Test round-trip serialization {@link ISampling.Timestamps}. + * The format is a flat array of @NonNull Longs. * * @throws JsonProcessingException * if JSON processing fails */ @Test public void testTimestampsRoundTrip() throws JsonProcessingException { - ISampling original = new Timestamps(new long[] { 1, 2, 3 }); + TestSampling original = new TestSampling(new Timestamps(new long[] { 1, 2, 3 })); String json = fMapper.writeValueAsString(original); - assertEquals("[1,2,3]", json); + assertEquals("{\"xValues\":[1,2,3]}", json); } /** - * Test round-trip serialization and deserialization for - * {@link ISampling.Categories}. The format is an array of strings. + * Test round-trip serialization {@link ISampling.Categories}. + * The format is an array of strings. * * @throws JsonProcessingException * if JSON processing fails */ @Test public void testCategoriesRoundTrip() throws JsonProcessingException { - ISampling original = new Categories(List.of("Read", "Write", "Idle")); + TestSampling original = new TestSampling(new Categories(List.of("Read", "Write", "Idle"))); String json = fMapper.writeValueAsString(original); - assertEquals("[\"Read\",\"Write\",\"Idle\"]", json); + assertEquals("{\"xCategories\":[\"Read\",\"Write\",\"Idle\"]}", json); } /** - * Test round-trip serialization and deserialization for - * {@link ISampling.Ranges}. The format is a 2D array of timestamp - * ranges, i.e., {@code [[start, end], ...]}. + * Test round-trip serialization {@link ISampling.Ranges}. + * The format is an array of range json objects. * * @throws JsonProcessingException * if JSON processing fails */ @Test public void testTimeRangesRoundTrip() throws JsonProcessingException { - ISampling original = new Ranges(List.of( + TestSampling original = new TestSampling(new Ranges(List.of( new Range<>(1L, 2L), new Range<>(2L, 3L), - new Range<>(3L, 4L) + new Range<>(3L, 4L)) )); String json = fMapper.writeValueAsString(original); - assertEquals("[[1,2],[2,3],[3,4]]", json); + assertEquals("{\"xRanges\":[{\"start\":1,\"end\":2},{\"start\":2,\"end\":3},{\"start\":3,\"end\":4}]}", json); + } + + private class TestSampling implements Serializable { + private static final long serialVersionUID = 4522987850097455165L; + ISampling fSampling; + + public TestSampling(ISampling sampling) { + fSampling = sampling; + } + + public ISampling getSampling() { + return fSampling; + } + } + + private class TestSamplingSerializer extends StdSerializer { + + private static final long serialVersionUID = 4522987850097455165L; + + /** + * Constructor + */ + public TestSamplingSerializer() { + super(TestSampling.class); + } + + @Override + public void serialize(TestSampling value, JsonGenerator gen, SerializerProvider provider) throws IOException { + gen.writeStartObject(); + gen.writeObject(value.getSampling()); + gen.writeEndObject(); + } } } diff --git a/trace-server/org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core.tests/src/org/eclipse/tracecompass/incubator/trace/server/jersey/rest/core/tests/webapp/SeriesModelSerializerTest.java b/trace-server/org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core.tests/src/org/eclipse/tracecompass/incubator/trace/server/jersey/rest/core/tests/webapp/SeriesModelSerializerTest.java index b8685eaf8..426727e69 100644 --- a/trace-server/org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core.tests/src/org/eclipse/tracecompass/incubator/trace/server/jersey/rest/core/tests/webapp/SeriesModelSerializerTest.java +++ b/trace-server/org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core.tests/src/org/eclipse/tracecompass/incubator/trace/server/jersey/rest/core/tests/webapp/SeriesModelSerializerTest.java @@ -23,10 +23,9 @@ import org.eclipse.tracecompass.incubator.internal.trace.server.jersey.rest.core.webapp.SamplingSerializer; import org.eclipse.tracecompass.incubator.internal.trace.server.jersey.rest.core.webapp.SeriesModelSerializer; import org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core.tests.stubs.OutputElementStyleStub; -import org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core.tests.stubs.ISamplingStub; import org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core.tests.stubs.XySeriesStub; -import org.eclipse.tracecompass.tmf.core.model.OutputElementStyle; import org.eclipse.tracecompass.tmf.core.model.ISampling; +import org.eclipse.tracecompass.tmf.core.model.OutputElementStyle; import org.eclipse.tracecompass.tmf.core.model.SeriesModel.SeriesModelBuilder; import org.eclipse.tracecompass.tmf.core.model.StyleProperties; import org.eclipse.tracecompass.tmf.core.model.xy.ISeriesModel; @@ -75,9 +74,13 @@ public void testValidStyles() throws JsonProcessingException { assertEquals(TITLE, deserialized.getName()); assertEquals(ID, deserialized.getId()); // the sampling is de-serialized into sampling-stub - assertTrue(deserialized.getXValues() instanceof ISamplingStub.TimestampsStub); - long[] actual = ((ISamplingStub.TimestampsStub) deserialized.getXValues()).getTimestamps(); - assertArrayEquals(new long[] {0, 1, 2, 3}, actual); + long[] xValues = deserialized.getXValues(); + assertNotNull(xValues); + assertNull(deserialized.getXCategories()); + assertNull(deserialized.getXRanges()); + assertTrue(xValues.length > 0); + + assertArrayEquals(new long[] {0, 1, 2, 3}, xValues); List yValues = deserialized.getYValues(); assertTrue(fValues.length == yValues.size()); for (int i = 0; i < fValues.length; i++) { diff --git a/trace-server/org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core/src/org/eclipse/tracecompass/incubator/internal/trace/server/jersey/rest/core/model/Range.java b/trace-server/org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core/src/org/eclipse/tracecompass/incubator/internal/trace/server/jersey/rest/core/model/Range.java new file mode 100644 index 000000000..a793b5a18 --- /dev/null +++ b/trace-server/org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core/src/org/eclipse/tracecompass/incubator/internal/trace/server/jersey/rest/core/model/Range.java @@ -0,0 +1,32 @@ +package org.eclipse.tracecompass.incubator.internal.trace.server.jersey.rest.core.model; + +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.media.Schema.RequiredMode; + +/** + * Represents a closed interval [start, end] for a sampling range. + */ +@Schema( + name = "Range", + description = "An object representing a closed interval with a start and end." +) +class Range { + @Schema(description = "Start of the range (inclusive).", requiredMode = RequiredMode.REQUIRED) + private final long start; + + @Schema(description = "End of the range (inclusive).", requiredMode = RequiredMode.REQUIRED) + private final long end; + + public Range(long start, long end) { + this.start = start; + this.end = end; + } + + public long getStart() { + return start; + } + + public long getEnd() { + return end; + } +} diff --git a/trace-server/org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core/src/org/eclipse/tracecompass/incubator/internal/trace/server/jersey/rest/core/model/Sampling.java b/trace-server/org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core/src/org/eclipse/tracecompass/incubator/internal/trace/server/jersey/rest/core/model/Sampling.java deleted file mode 100644 index 7978bd6f2..000000000 --- a/trace-server/org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core/src/org/eclipse/tracecompass/incubator/internal/trace/server/jersey/rest/core/model/Sampling.java +++ /dev/null @@ -1,88 +0,0 @@ -/********************************************************************** - * Copyright (c) 2025 Ericsson - * - * All rights reserved. This program and the accompanying materials are - * made available under the terms of the Eclipse Public License 2.0 which - * accompanies this distribution, and is available at - * https://www.eclipse.org/legal/epl-2.0/ - * - * SPDX-License-Identifier: EPL-2.0 - **********************************************************************/ - -package org.eclipse.tracecompass.incubator.internal.trace.server.jersey.rest.core.model; - -import io.swagger.v3.oas.annotations.media.Schema; -import io.swagger.v3.oas.annotations.media.Schema.RequiredMode; - -import java.util.List; - -/** - * Describes alternative representations of sampling options. - * Used for OpenAPI generation. - */ -@Schema(oneOf = { - Sampling.TimestampSampling.class, - Sampling.CategorySampling.class, - Sampling.RangeSampling.class -}) -public interface Sampling { - - /** - * Sampling as list of timestamps. - */ - public static class TimestampSampling { - /** - * The sampling points as timestamp values. - */ - @Schema( - description = "Sampling as list of timestamps", - requiredMode = RequiredMode.REQUIRED - ) - public long[] sampling; - } - - /** - * Sampling as list of categories. - */ - public static class CategorySampling { - /** - * The sampling points as category names or labels. - */ - @Schema( - description = "Sampling as list of categories", - requiredMode = RequiredMode.REQUIRED - ) - public String[] sampling; - } - - /** - * Sampling as a list of [start, end] ranges. - */ - public static class RangeSampling { - /** - * The list of sampling ranges, each with a start and end value. - */ - @Schema( - description = "Sampling as list of [start, end] timestamp ranges", - requiredMode = RequiredMode.REQUIRED - ) - public List sampling; - } - - /** - * Represents a closed interval [start, end] for a sampling range. - */ - public static class StartEndRange { - /** - * Start timestamp of the range (inclusive). - */ - @Schema(description = "Start timestamp of the range", requiredMode = RequiredMode.REQUIRED) - public long start; - - /** - * End timestamp of the range (inclusive). - */ - @Schema(description = "End timestamp of the range", requiredMode = RequiredMode.REQUIRED) - public long end; - } -} diff --git a/trace-server/org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core/src/org/eclipse/tracecompass/incubator/internal/trace/server/jersey/rest/core/model/SeriesModel.java b/trace-server/org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core/src/org/eclipse/tracecompass/incubator/internal/trace/server/jersey/rest/core/model/SeriesModel.java index 998127901..592512ceb 100644 --- a/trace-server/org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core/src/org/eclipse/tracecompass/incubator/internal/trace/server/jersey/rest/core/model/SeriesModel.java +++ b/trace-server/org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core/src/org/eclipse/tracecompass/incubator/internal/trace/server/jersey/rest/core/model/SeriesModel.java @@ -11,6 +11,8 @@ package org.eclipse.tracecompass.incubator.internal.trace.server.jersey.rest.core.model; +import java.util.List; + import org.eclipse.jdt.annotation.NonNull; import com.fasterxml.jackson.annotation.JsonProperty; @@ -39,15 +41,25 @@ public interface SeriesModel { String getSeriesName(); /** - * @return The X values. + * @return The X values as list of longs */ @JsonProperty("xValues") - @Schema(description = "Sampling values", requiredMode = RequiredMode.REQUIRED, oneOf = { - Sampling.TimestampSampling.class, - Sampling.CategorySampling.class, - Sampling.RangeSampling.class - }) - Sampling getXValues(); + @Schema(description = "X values as list of int64 values (e.g. timestamps). Example: [100, 200, 350]. Mutually exclusive with xCategories/xRanges.") + public List getXValues(); + + /** + * @return The X values as list of strings + */ + @JsonProperty("xCategories") + @Schema(description = "X values as list of category strings. Example: [\"READ\", \"WRITE\"]. Mutually exclusive with xValues/xRanges.") + public List getXCategories(); + + /** + * @return The X values as list of ranges + */ + @JsonProperty("xRanges") + @Schema(description = "X values as list of start/end range objects. Example: [{\"start\": 10, \"end\": 20}, {\"start\": 50, \"end\": 75}]. Mutually exclusive with xValues/xCategories.") + public List getXRanges(); /** * @return The Y values. diff --git a/trace-server/org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core/src/org/eclipse/tracecompass/incubator/internal/trace/server/jersey/rest/core/webapp/SamplingSerializer.java b/trace-server/org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core/src/org/eclipse/tracecompass/incubator/internal/trace/server/jersey/rest/core/webapp/SamplingSerializer.java index 2cd788cb0..f134a8561 100644 --- a/trace-server/org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core/src/org/eclipse/tracecompass/incubator/internal/trace/server/jersey/rest/core/webapp/SamplingSerializer.java +++ b/trace-server/org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core/src/org/eclipse/tracecompass/incubator/internal/trace/server/jersey/rest/core/webapp/SamplingSerializer.java @@ -26,9 +26,13 @@ /** * Custom serializer for all Sampling subtypes. - * - Timestamps → flat array: [1, 2, 3] - * - Categories → array of strings: ["Read", "Write"] - * - TimeRanges → array of arrays: [[1, 2], [2, 3]] + * + * Note: It serializes only the object content without JSON object start and end marker. Hence + * ISampling has to be embedded inside an outer JSON object + * + * - Timestamps → flat array → "xValues": [1, 2, 3] + * - Categories → array of strings → "categories": ["Read", "Write"] + * - Ranges → array of Range → ["ranges": { "start": 1, "end": 2 }, { "start": 3, "end": 4 }] */ public class SamplingSerializer extends StdSerializer { @@ -43,23 +47,20 @@ public SamplingSerializer() { @Override public void serialize(ISampling value, JsonGenerator gen, SerializerProvider provider) throws IOException { + // Note that ISampling is not serialized as a json object, but only as json field. + // Hence it has to be embedded in an outer JSON object if (value instanceof Timestamps timestamps) { - gen.writeArray(timestamps.timestamps(), 0, timestamps.timestamps().length); - + gen.writeObjectField("xValues", timestamps.timestamps()); //$NON-NLS-1$ } else if (value instanceof Categories categories) { - gen.writeStartArray(); - for (String category : categories.categories()) { - gen.writeString(category); - } - gen.writeEndArray(); - + gen.writeObjectField("xCategories", categories.categories()); //$NON-NLS-1$ } else if (value instanceof Ranges timeRanges) { + gen.writeFieldName("xRanges"); //$NON-NLS-1$ gen.writeStartArray(); for (Range<@NonNull Long> range : timeRanges.ranges()) { - gen.writeStartArray(); - gen.writeNumber(range.start()); - gen.writeNumber(range.end()); - gen.writeEndArray(); + gen.writeStartObject(); + gen.writeNumberField("start", range.start()); //$NON-NLS-1$ + gen.writeNumberField("end", range.end()); //$NON-NLS-1$ + gen.writeEndObject(); } gen.writeEndArray(); } else { diff --git a/trace-server/org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core/src/org/eclipse/tracecompass/incubator/internal/trace/server/jersey/rest/core/webapp/SeriesModelSerializer.java b/trace-server/org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core/src/org/eclipse/tracecompass/incubator/internal/trace/server/jersey/rest/core/webapp/SeriesModelSerializer.java index b30e373ba..bd2cab86d 100644 --- a/trace-server/org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core/src/org/eclipse/tracecompass/incubator/internal/trace/server/jersey/rest/core/webapp/SeriesModelSerializer.java +++ b/trace-server/org.eclipse.tracecompass.incubator.trace.server.jersey.rest.core/src/org/eclipse/tracecompass/incubator/internal/trace/server/jersey/rest/core/webapp/SeriesModelSerializer.java @@ -47,7 +47,7 @@ public void serialize(ISeriesModel value, JsonGenerator gen, SerializerProvider gen.writeStartObject(); gen.writeNumberField("seriesId", value.getId()); //$NON-NLS-1$ gen.writeStringField("seriesName", value.getName()); //$NON-NLS-1$ - gen.writeObjectField("xValues", value.getSampling()); //$NON-NLS-1$ + gen.writeObject(value.getSampling()); gen.writeObjectField("yValues", value.getData()); //$NON-NLS-1$ // no-op trim below, null-related (unlikely case) warning otherwise-