This is an automated email from the ASF dual-hosted git repository. Cole-Greer pushed a commit to branch simplePDT in repository https://gitbox.apache.org/repos/asf/tinkerpop.git
commit 3ec227cc9704564ffe98109c1a0a38f00bac14c3 Author: Cole Greer <[email protected]> AuthorDate: Wed Jun 24 13:14:29 2026 -0700 Add core PrimitiveProviderDefinedType + GraphBinary serializer (0xF1) Introduces the core value type for PrimitivePDT and its GraphBinary serialization, mirroring the composite ProviderDefinedType. - PrimitiveProviderDefinedType: immutable (String name, String value). name non-null/non-empty; value non-null with empty string allowed and null rejected; equals/hashCode on (name, value); toString pdt[name](value); transient withHydrated/getHydrated for symmetry. The value is opaque and is never parsed or normalized. - PrimitiveProviderDefinedTypeSerializer: SimpleTypeSerializer over DataType.PRIMITIVE_PDT writing/reading two fully-qualified Strings ({name}{value}). - DataType: add PRIMITIVE_PDT(0xF1). - TypeSerializerRegistry: register PrimitiveProviderDefinedType -> PrimitiveProviderDefinedTypeSerializer. Adapter/registry hydration, reader/writer dehydration dispatch, GraphSON, grammar, and GLVs are deferred to later beads. Tests: PrimitiveProviderDefinedTypeTest (validation, equals/hashCode/toString) and PrimitiveProviderDefinedTypeSerializerTest (round-trip incl. opaque-value fidelity: leading zeros, large and non-numeric values). tinkerpop-2gy.2 Assisted-by: Kiro:claude-opus-4.8 --- .../gremlin/structure/io/binary/DataType.java | 1 + .../io/binary/TypeSerializerRegistry.java | 5 +- .../PrimitiveProviderDefinedTypeSerializer.java | 47 +++++++++ .../io/pdt/PrimitiveProviderDefinedType.java | 82 ++++++++++++++++ .../io/pdt/PrimitiveProviderDefinedTypeTest.java | 99 +++++++++++++++++++ ...PrimitiveProviderDefinedTypeSerializerTest.java | 106 +++++++++++++++++++++ 6 files changed, 339 insertions(+), 1 deletion(-) diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/binary/DataType.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/binary/DataType.java index 6ca99db840..fa6115cc76 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/binary/DataType.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/binary/DataType.java @@ -59,6 +59,7 @@ public enum DataType { DURATION(0X81), COMPOSITE_PDT(0xF0), + PRIMITIVE_PDT(0xF1), MARKER(0XFD), UNSPECIFIED_NULL(0XFE); diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/binary/TypeSerializerRegistry.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/binary/TypeSerializerRegistry.java index c0a3234aac..549cb2ccc0 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/binary/TypeSerializerRegistry.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/binary/TypeSerializerRegistry.java @@ -33,6 +33,7 @@ import org.apache.tinkerpop.gremlin.structure.Vertex; import org.apache.tinkerpop.gremlin.structure.VertexProperty; import org.apache.tinkerpop.gremlin.structure.io.pdt.ProviderDefined; import org.apache.tinkerpop.gremlin.structure.io.pdt.ProviderDefinedType; +import org.apache.tinkerpop.gremlin.structure.io.pdt.PrimitiveProviderDefinedType; import org.apache.tinkerpop.gremlin.structure.io.binary.types.BigDecimalSerializer; import org.apache.tinkerpop.gremlin.structure.io.binary.types.BigIntegerSerializer; import org.apache.tinkerpop.gremlin.structure.io.binary.types.BulkSetSerializer; @@ -49,6 +50,7 @@ import org.apache.tinkerpop.gremlin.structure.io.binary.types.DateTimeSerializer import org.apache.tinkerpop.gremlin.structure.io.binary.types.PathSerializer; import org.apache.tinkerpop.gremlin.structure.io.binary.types.PropertySerializer; import org.apache.tinkerpop.gremlin.structure.io.binary.types.ProviderDefinedTypeSerializer; +import org.apache.tinkerpop.gremlin.structure.io.binary.types.PrimitiveProviderDefinedTypeSerializer; import org.apache.tinkerpop.gremlin.structure.io.binary.types.SetSerializer; import org.apache.tinkerpop.gremlin.structure.io.binary.types.SingleTypeSerializer; import org.apache.tinkerpop.gremlin.structure.io.binary.types.StringSerializer; @@ -121,7 +123,8 @@ public class TypeSerializerRegistry { new RegistryEntry<>(Character.class, new CharSerializer()), new RegistryEntry<>(Duration.class, new DurationSerializer()), new RegistryEntry<>(OffsetDateTime.class, new DateTimeSerializer()), - new RegistryEntry<>(ProviderDefinedType.class, new ProviderDefinedTypeSerializer()) + new RegistryEntry<>(ProviderDefinedType.class, new ProviderDefinedTypeSerializer()), + new RegistryEntry<>(PrimitiveProviderDefinedType.class, new PrimitiveProviderDefinedTypeSerializer()) }; public static final TypeSerializerRegistry INSTANCE = build().create(); diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/binary/types/PrimitiveProviderDefinedTypeSerializer.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/binary/types/PrimitiveProviderDefinedTypeSerializer.java new file mode 100644 index 0000000000..9bb6b1f646 --- /dev/null +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/binary/types/PrimitiveProviderDefinedTypeSerializer.java @@ -0,0 +1,47 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.tinkerpop.gremlin.structure.io.binary.types; + +import org.apache.tinkerpop.gremlin.structure.io.Buffer; +import org.apache.tinkerpop.gremlin.structure.io.binary.DataType; +import org.apache.tinkerpop.gremlin.structure.io.binary.GraphBinaryReader; +import org.apache.tinkerpop.gremlin.structure.io.binary.GraphBinaryWriter; +import org.apache.tinkerpop.gremlin.structure.io.pdt.PrimitiveProviderDefinedType; + +import java.io.IOException; + +public class PrimitiveProviderDefinedTypeSerializer extends SimpleTypeSerializer<PrimitiveProviderDefinedType> { + + public PrimitiveProviderDefinedTypeSerializer() { + super(DataType.PRIMITIVE_PDT); + } + + @Override + protected PrimitiveProviderDefinedType readValue(final Buffer buffer, final GraphBinaryReader context) throws IOException { + final String name = context.read(buffer); + final String value = context.read(buffer); + return new PrimitiveProviderDefinedType(name, value); + } + + @Override + protected void writeValue(final PrimitiveProviderDefinedType value, final Buffer buffer, final GraphBinaryWriter context) throws IOException { + context.write(value.getName(), buffer); + context.write(value.getValue(), buffer); + } +} diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/pdt/PrimitiveProviderDefinedType.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/pdt/PrimitiveProviderDefinedType.java new file mode 100644 index 0000000000..dbf3938ac2 --- /dev/null +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/pdt/PrimitiveProviderDefinedType.java @@ -0,0 +1,82 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.tinkerpop.gremlin.structure.io.pdt; + +import java.util.Objects; + +/** + * An immutable representation of a primitive provider-defined type consisting of a name and an opaque string value. + */ +public final class PrimitiveProviderDefinedType { + + private final String name; + private final String value; + private transient Object hydrated; + + public PrimitiveProviderDefinedType(final String name, final String value) { + if (name == null || name.isEmpty()) + throw new IllegalArgumentException("name cannot be null or empty"); + if (value == null) + throw new IllegalArgumentException("value cannot be null"); + this.name = name; + this.value = value; + } + + public String getName() { + return name; + } + + public String getValue() { + return value; + } + + /** + * Returns a copy of this primitive PDT with the hydrated object attached. + */ + public PrimitiveProviderDefinedType withHydrated(final Object hydrated) { + final PrimitiveProviderDefinedType copy = new PrimitiveProviderDefinedType(this.name, this.value); + copy.hydrated = hydrated; + return copy; + } + + /** + * Returns the hydrated object if set, or {@code null}. + */ + public Object getHydrated() { + return hydrated; + } + + @Override + public boolean equals(final Object o) { + if (this == o) return true; + if (!(o instanceof PrimitiveProviderDefinedType)) return false; + final PrimitiveProviderDefinedType that = (PrimitiveProviderDefinedType) o; + return name.equals(that.name) && value.equals(that.value); + } + + @Override + public int hashCode() { + return Objects.hash(name, value); + } + + @Override + public String toString() { + return "pdt[" + name + "](" + value + ")"; + } +} diff --git a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/structure/io/pdt/PrimitiveProviderDefinedTypeTest.java b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/structure/io/pdt/PrimitiveProviderDefinedTypeTest.java new file mode 100644 index 0000000000..dcde0f6452 --- /dev/null +++ b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/structure/io/pdt/PrimitiveProviderDefinedTypeTest.java @@ -0,0 +1,99 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.tinkerpop.gremlin.structure.io.pdt; + +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertThrows; + +public class PrimitiveProviderDefinedTypeTest { + + @Test + public void shouldConstructWithNameAndValue() { + final PrimitiveProviderDefinedType pdt = new PrimitiveProviderDefinedType("Uint32", "42"); + assertEquals("Uint32", pdt.getName()); + assertEquals("42", pdt.getValue()); + } + + @Test + public void shouldAllowEmptyStringValue() { + final PrimitiveProviderDefinedType pdt = new PrimitiveProviderDefinedType("Empty", ""); + assertEquals("", pdt.getValue()); + } + + @Test + public void shouldThrowOnNullName() { + assertThrows(IllegalArgumentException.class, () -> new PrimitiveProviderDefinedType(null, "v")); + } + + @Test + public void shouldThrowOnEmptyName() { + assertThrows(IllegalArgumentException.class, () -> new PrimitiveProviderDefinedType("", "v")); + } + + @Test + public void shouldThrowOnNullValue() { + assertThrows(IllegalArgumentException.class, () -> new PrimitiveProviderDefinedType("Name", null)); + } + + @Test + public void shouldHaveCorrectEquals() { + final PrimitiveProviderDefinedType a = new PrimitiveProviderDefinedType("Uint32", "007"); + final PrimitiveProviderDefinedType b = new PrimitiveProviderDefinedType("Uint32", "007"); + assertEquals(a, b); + assertEquals(a.hashCode(), b.hashCode()); + } + + @Test + public void shouldNotEqualDifferentName() { + final PrimitiveProviderDefinedType a = new PrimitiveProviderDefinedType("Uint32", "42"); + final PrimitiveProviderDefinedType b = new PrimitiveProviderDefinedType("Int64", "42"); + assertNotEquals(a, b); + } + + @Test + public void shouldNotEqualDifferentValue() { + final PrimitiveProviderDefinedType a = new PrimitiveProviderDefinedType("Uint32", "42"); + final PrimitiveProviderDefinedType b = new PrimitiveProviderDefinedType("Uint32", "43"); + assertNotEquals(a, b); + } + + @Test + public void shouldHaveCorrectToString() { + final PrimitiveProviderDefinedType pdt = new PrimitiveProviderDefinedType("Uint32", "42"); + assertEquals("pdt[Uint32](42)", pdt.toString()); + } + + @Test + public void shouldSupportHydratedObject() { + final PrimitiveProviderDefinedType pdt = new PrimitiveProviderDefinedType("Uint32", "42"); + assertNull(pdt.getHydrated()); + + final Object hydrated = Long.valueOf(42L); + final PrimitiveProviderDefinedType withHydrated = pdt.withHydrated(hydrated); + assertEquals(hydrated, withHydrated.getHydrated()); + // original is unchanged + assertNull(pdt.getHydrated()); + // logical equality is not affected by hydration + assertEquals(pdt, withHydrated); + } +} diff --git a/gremlin-util/src/test/java/org/apache/tinkerpop/gremlin/util/ser/binary/types/PrimitiveProviderDefinedTypeSerializerTest.java b/gremlin-util/src/test/java/org/apache/tinkerpop/gremlin/util/ser/binary/types/PrimitiveProviderDefinedTypeSerializerTest.java new file mode 100644 index 0000000000..1cc9d216f5 --- /dev/null +++ b/gremlin-util/src/test/java/org/apache/tinkerpop/gremlin/util/ser/binary/types/PrimitiveProviderDefinedTypeSerializerTest.java @@ -0,0 +1,106 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.tinkerpop.gremlin.util.ser.binary.types; + +import io.netty.buffer.ByteBufAllocator; +import org.apache.tinkerpop.gremlin.structure.io.Buffer; +import org.apache.tinkerpop.gremlin.structure.io.binary.GraphBinaryReader; +import org.apache.tinkerpop.gremlin.structure.io.binary.GraphBinaryWriter; +import org.apache.tinkerpop.gremlin.structure.io.pdt.PrimitiveProviderDefinedType; +import org.apache.tinkerpop.gremlin.util.ser.NettyBufferFactory; +import org.junit.Test; + +import java.io.IOException; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; + +public class PrimitiveProviderDefinedTypeSerializerTest { + + private static final GraphBinaryReader reader = new GraphBinaryReader(); + private static final GraphBinaryWriter writer = new GraphBinaryWriter(); + private static final ByteBufAllocator allocator = ByteBufAllocator.DEFAULT; + private static final NettyBufferFactory bufferFactory = new NettyBufferFactory(); + + private Buffer writeAndRead(final Object value) throws IOException { + final Buffer buffer = bufferFactory.create(allocator.buffer()); + writer.write(value, buffer); + buffer.readerIndex(0); + return buffer; + } + + @Test + public void shouldRoundTripSimplePrimitivePdt() throws IOException { + final PrimitiveProviderDefinedType pdt = new PrimitiveProviderDefinedType("Uint32", "42"); + + final Buffer buffer = writeAndRead(pdt); + final PrimitiveProviderDefinedType result = reader.read(buffer); + + assertEquals(pdt, result); + } + + @Test + public void shouldRoundTripEmptyValue() throws IOException { + final PrimitiveProviderDefinedType pdt = new PrimitiveProviderDefinedType("Empty", ""); + + final Buffer buffer = writeAndRead(pdt); + final PrimitiveProviderDefinedType result = reader.read(buffer); + + assertEquals(pdt, result); + } + + @Test + public void shouldPreserveLeadingZeros() throws IOException { + final PrimitiveProviderDefinedType pdt = new PrimitiveProviderDefinedType("Uint32", "007"); + + final Buffer buffer = writeAndRead(pdt); + final PrimitiveProviderDefinedType result = reader.read(buffer); + + assertEquals("007", result.getValue()); + } + + @Test + public void shouldPreserveLargeValues() throws IOException { + final PrimitiveProviderDefinedType pdt = new PrimitiveProviderDefinedType("Uint32", "4294967295"); + + final Buffer buffer = writeAndRead(pdt); + final PrimitiveProviderDefinedType result = reader.read(buffer); + + assertEquals("4294967295", result.getValue()); + } + + @Test + public void shouldPreserveNonNumericStrings() throws IOException { + final PrimitiveProviderDefinedType pdt = new PrimitiveProviderDefinedType("TinkerId", "abc-def-123"); + + final Buffer buffer = writeAndRead(pdt); + final PrimitiveProviderDefinedType result = reader.read(buffer); + + assertEquals("abc-def-123", result.getValue()); + } + + @Test + public void shouldHandleNullPrimitivePdt() throws IOException { + final Buffer buffer = bufferFactory.create(allocator.buffer()); + writer.write(null, buffer); + buffer.readerIndex(0); + final Object result = reader.read(buffer); + assertNull(result); + } +}
