From 60d7d73b14d7dd5f0b5b8c098e0397367f4df7b2 Mon Sep 17 00:00:00 2001 From: David Noble Date: Tue, 17 Sep 2019 20:29:21 -0700 Subject: [PATCH] Progressed on deserializing schemas. --- .../java/com/azure/data/cosmos/core/Json.java | 36 ++++++-- .../hybridrow/layouts/LayoutCompiler.java | 12 +-- .../hybridrow/layouts/SystemSchema.java | 32 ++++++- .../hybridrow/schemas/Namespace.java | 54 ++++++++--- .../schemas/PrimitivePropertyType.java | 14 ++- .../hybridrow/schemas/Property.java | 70 +++++++++++--- .../hybridrow/schemas/PropertyType.java | 62 ++++++++++++- .../hybridrow/schemas/Schema.java | 89 +++++++++++++----- .../hybridrow/schemas/SchemaHash.java | 2 +- .../schemas/SchemaLanguageVersion.java | 35 ++++++- .../hybridrow/schemas/SchemaValidator.java | 4 +- .../hybridrow/schemas/StorageKind.java | 27 ++++-- .../hybridrow/schemas/TypeKind.java | 91 ++++++++++++------- .../hybridrow/schemas/UdtPropertyType.java | 3 +- .../hybridrow/layouts/SystemSchemaTest.java | 4 +- java/src/test/resources/log4j.properties | 12 +-- 16 files changed, 411 insertions(+), 136 deletions(-) diff --git a/java/src/main/java/com/azure/data/cosmos/core/Json.java b/java/src/main/java/com/azure/data/cosmos/core/Json.java index 65b30c9..14278f8 100644 --- a/java/src/main/java/com/azure/data/cosmos/core/Json.java +++ b/java/src/main/java/com/azure/data/cosmos/core/Json.java @@ -3,29 +3,53 @@ package com.azure.data.cosmos.core; +import com.fasterxml.jackson.core.JsonFactory; +import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectReader; import com.fasterxml.jackson.databind.ObjectWriter; +import com.fasterxml.jackson.databind.SerializationFeature; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.io.IOException; +import java.io.InputStream; import java.util.Optional; import static com.google.common.base.Strings.lenientFormat; public final class Json { - private static final ObjectMapper mapper = new ObjectMapper(); - private static final ObjectReader reader = mapper.reader(); - private static final ObjectWriter writer = mapper.writer(); + private static final Logger logger = LoggerFactory.getLogger(Json.class); + + private static final ObjectMapper mapper = new ObjectMapper(new JsonFactory() + .enable(JsonParser.Feature.ALLOW_COMMENTS)); + + private static final ObjectReader reader = mapper.reader() + .withFeatures(DeserializationFeature.READ_ENUMS_USING_TO_STRING); + + private static final ObjectWriter writer = mapper.writer() + .withFeatures(SerializationFeature.WRITE_ENUMS_USING_TO_STRING); private Json() { } - public static Optional parse(String value) { + public static Optional parse(InputStream stream, Class type) { try { - return Optional.of(reader.readValue(value)); - } catch (IOException e) { + return Optional.of(reader.forType(type).readValue(stream)); + } catch (IOException error) { + logger.error("", error); + return Optional.empty(); + } + } + + public static Optional parse(String value, Class type) { + try { + return Optional.of(reader.forType(type).readValue(value)); + } catch (IOException error) { + logger.error("", error); return Optional.empty(); } } diff --git a/java/src/main/java/com/azure/data/cosmos/serialization/hybridrow/layouts/LayoutCompiler.java b/java/src/main/java/com/azure/data/cosmos/serialization/hybridrow/layouts/LayoutCompiler.java index 5ecf1cf..5202b16 100644 --- a/java/src/main/java/com/azure/data/cosmos/serialization/hybridrow/layouts/LayoutCompiler.java +++ b/java/src/main/java/com/azure/data/cosmos/serialization/hybridrow/layouts/LayoutCompiler.java @@ -61,15 +61,15 @@ public final class LayoutCompiler { for (Property p : properties) { - LayoutType type = LayoutCompiler.logicalToPhysicalType(ns, p.propertyType(), typeArgs); + LayoutType type = LayoutCompiler.logicalToPhysicalType(ns, p.type(), typeArgs); switch (LayoutCodeTraits.clearImmutableBit(type.layoutCode())) { case OBJECT_SCOPE: { - if (!p.propertyType().nullable()) { + if (!p.type().nullable()) { throw new LayoutCompilationException("Non-nullable sparse column are not supported."); } - ObjectPropertyType op = (ObjectPropertyType)p.propertyType(); + ObjectPropertyType op = (ObjectPropertyType)p.type(); builder.addObjectScope(p.path(), type); LayoutCompiler.addProperties(builder, ns, type.layoutCode(), op.properties()); builder.EndObjectScope(); @@ -87,7 +87,7 @@ public final class LayoutCompiler { case TAGGED_SCOPE: case TAGGED2_SCOPE: case SCHEMA: { - if (!p.propertyType().nullable()) { + if (!p.type().nullable()) { throw new LayoutCompilationException("Non-nullable sparse column are not supported."); } builder.addTypedScope(p.path(), type, typeArgs.get()); @@ -100,9 +100,9 @@ public final class LayoutCompiler { default: { - if (p.propertyType() instanceof PrimitivePropertyType) { + if (p.type() instanceof PrimitivePropertyType) { - PrimitivePropertyType pp = (PrimitivePropertyType) p.propertyType(); + PrimitivePropertyType pp = (PrimitivePropertyType) p.type(); switch (pp.storage()) { diff --git a/java/src/main/java/com/azure/data/cosmos/serialization/hybridrow/layouts/SystemSchema.java b/java/src/main/java/com/azure/data/cosmos/serialization/hybridrow/layouts/SystemSchema.java index 8149027..5551523 100644 --- a/java/src/main/java/com/azure/data/cosmos/serialization/hybridrow/layouts/SystemSchema.java +++ b/java/src/main/java/com/azure/data/cosmos/serialization/hybridrow/layouts/SystemSchema.java @@ -9,9 +9,13 @@ import com.google.common.base.Suppliers; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; +import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; +import java.net.URL; import java.nio.charset.StandardCharsets; +import java.security.CodeSource; +import java.util.Enumeration; import java.util.Optional; import java.util.function.Supplier; @@ -41,17 +45,20 @@ public final class SystemSchema { final String json; - try (final InputStream stream = SystemSchema.class.getResourceAsStream("SystemSchema.json")) { + try (final InputStream stream = getResourceAsStream("SystemSchema.json")) { + + Optional namespace = Namespace.parse(stream); ByteBuf buffer = Unpooled.buffer(); while (buffer.writeBytes(stream, 8192) == 8192) { } json = buffer.readCharSequence(buffer.readableBytes(), StandardCharsets.UTF_8).toString(); + } catch (IOException cause) { - String message = lenientFormat("failed to load SystemSchema.json due to %s", cause); + String message = lenientFormat("Failed to load SystemSchema.json due to %s", cause); throw new IllegalStateException(message, cause); } Optional namespace = Namespace.parse(json); - checkState(namespace.isPresent(), "failed to load SystemSchema.json"); + checkState(namespace.isPresent(), "Failed to parse SystemSchema.json"); return new LayoutResolverNamespace(namespace.get()); }); @@ -62,4 +69,23 @@ public final class SystemSchema { public static LayoutResolver layoutResolver() { return layoutResolver.get(); } + + private static InputStream getResourceAsStream(final String name) throws IOException { + + final CodeSource codeSource = SystemSchema.class.getProtectionDomain().getCodeSource(); + final ClassLoader classLoader = SystemSchema.class.getClassLoader(); + final String location = codeSource.getLocation().toString(); + final Enumeration urls; + + urls = classLoader.getResources(name); + + while (urls.hasMoreElements()) { + final URL url = urls.nextElement(); + if (url.toString().startsWith(location)) { + return url.openStream(); + } + } + + throw new FileNotFoundException(lenientFormat("cannot find resource at code source location %s", location)); + } } diff --git a/java/src/main/java/com/azure/data/cosmos/serialization/hybridrow/schemas/Namespace.java b/java/src/main/java/com/azure/data/cosmos/serialization/hybridrow/schemas/Namespace.java index ea0c14f..ea86669 100644 --- a/java/src/main/java/com/azure/data/cosmos/serialization/hybridrow/schemas/Namespace.java +++ b/java/src/main/java/com/azure/data/cosmos/serialization/hybridrow/schemas/Namespace.java @@ -4,31 +4,39 @@ package com.azure.data.cosmos.serialization.hybridrow.schemas; import com.azure.data.cosmos.core.Json; +import com.fasterxml.jackson.annotation.JsonProperty; +import java.io.InputStream; import java.util.ArrayList; import java.util.List; import java.util.Optional; public class Namespace { + @JsonProperty(required = true) private String name; - private SchemaLanguageVersion version = SchemaLanguageVersion.values()[0]; + + @JsonProperty(required = true) private ArrayList schemas; - /** - * Initializes a new instance of the {@link Namespace} class. - */ - public Namespace() { - this.schemas(new ArrayList()); - } + @JsonProperty(required = true) + private SchemaLanguageVersion version; /** - * The fully qualified identifier of the namespace. + * The fully qualified name of the namespace. + * + * @return fully qualified name of the {@linkplain Namespace namespace}. */ public final String name() { return this.name; } + /** + * Sets the fully qualified name of the namespace. + * + * @param value fully qualified name of the {@linkplain Namespace namespace}. + * @return a reference to this {@linkplain Namespace namespace}. + */ public final Namespace name(String value) { this.name = value; return this; @@ -54,11 +62,21 @@ public class Namespace { /** * The version of the HybridRow Schema Definition Language used to encode this namespace. + * + * @return {linkplain SchemaLanguageVersion version} of the HybridRow Schema Definition Language used to encode this + * {@linkplain Namespace namespace}. */ public final SchemaLanguageVersion version() { return this.version; } + /** + * Sets the version of the HybridRow Schema Definition Language used to encode this namespace. + * + * @param value {linkplain SchemaLanguageVersion version} of the HybridRow Schema Definition Language that will be + * used to encode this {@linkplain Namespace namespace}. + * @return a reference to this {@linkplain Namespace namespace}. + */ public final Namespace version(SchemaLanguageVersion value) { this.version = value; return this; @@ -67,12 +85,24 @@ public class Namespace { /** * Parse a JSON document and return a full namespace. * - * @param value The JSON text to parse + * @param value The JSON text to parse. * @return A namespace containing a set of logical schemas. */ public static Optional parse(String value) { - Optional ns = Json.parse(value); - ns.ifPresent(SchemaValidator::validate); - return ns; + Optional namespace = Json.parse(value, Namespace.class); + namespace.ifPresent(SchemaValidator::validate); + return namespace; + } + + /** + * Parse a JSON document and return a full namespace. + * + * @param stream The JSON input stream to parse. + * @return A namespace containing a set of logical schemas. + */ + public static Optional parse(InputStream stream) { + Optional namespace = Json.parse(stream, Namespace.class); + namespace.ifPresent(SchemaValidator::validate); + return namespace; } } diff --git a/java/src/main/java/com/azure/data/cosmos/serialization/hybridrow/schemas/PrimitivePropertyType.java b/java/src/main/java/com/azure/data/cosmos/serialization/hybridrow/schemas/PrimitivePropertyType.java index 1682543..5cf560d 100644 --- a/java/src/main/java/com/azure/data/cosmos/serialization/hybridrow/schemas/PrimitivePropertyType.java +++ b/java/src/main/java/com/azure/data/cosmos/serialization/hybridrow/schemas/PrimitivePropertyType.java @@ -3,22 +3,26 @@ package com.azure.data.cosmos.serialization.hybridrow.schemas; +import com.fasterxml.jackson.annotation.JsonProperty; + /** * A primitive property. *

- * Primitive properties map to columns one-to-one. Primitive properties indicate how the - * column should be represented within the row. + * Primitive properties map to columns one-to-one. Primitive properties indicate how the column should be represented + * within the row. */ public class PrimitivePropertyType extends PropertyType { + @JsonProperty private int length; - private StorageKind storage = StorageKind.values()[0]; + + @JsonProperty + private StorageKind storage; /** * The maximum allowable length in bytes. *

- * This annotation is only valid for non-fixed length types. A value of 0 means the maximum - * allowable length. + * This annotation is only valid for non-fixed length types. A value of 0 means the maximum allowable length. */ public final int length() { return this.length; diff --git a/java/src/main/java/com/azure/data/cosmos/serialization/hybridrow/schemas/Property.java b/java/src/main/java/com/azure/data/cosmos/serialization/hybridrow/schemas/Property.java index 3eb8040..1c83c3f 100644 --- a/java/src/main/java/com/azure/data/cosmos/serialization/hybridrow/schemas/Property.java +++ b/java/src/main/java/com/azure/data/cosmos/serialization/hybridrow/schemas/Property.java @@ -3,56 +3,102 @@ package com.azure.data.cosmos.serialization.hybridrow.schemas; +import com.fasterxml.jackson.annotation.JsonProperty; + +import javax.annotation.Nonnull; + /** * Describes a single property definition. */ public class Property { + @JsonProperty(required = false) private String comment; + + @JsonProperty(required = true) private String path; - private PropertyType propertyType; + + @JsonProperty(required = true) + private PropertyType type; /** - * An (optional) comment describing the purpose of this property + * An (optional) comment describing the purpose of this {@linkplain Property property}. *

* Comments are for documentary purpose only and do not affect the property at runtime. + * + * @return the comment on this {@linkplain Schema property} or {@code null}, if there is no comment. */ public final String comment() { return this.comment; } + /** + * Sets the (optional) comment describing the purpose of this {@linkplain Property property}. + *

+ * Comments are for documentary purpose only and do not affect the property at runtime. + * + * @param value a comment on this {@linkplain Property property} or {@code null} to remove the comment, if any, on + * this {@linkplain Property property}. + * @return a reference to this {@linkplain Property property}. + */ public final Property comment(String value) { this.comment = value; return this; } /** - * The logical path of this property. - *

+ * The logical path of this {@linkplain Property property}. + *

. * For complex properties (e.g. objects) the logical path forms a prefix to relative paths of properties defined * within nested structures. *

* See the logical path specification for full details on both relative and absolute paths. + * + * @return the logical path of this {@linkplain Property property}. */ public final String path() { return this.path; } - public final void setPath(String value) { + /** + * Sets the logical path of this {@linkplain Property property}. + *

. + * For complex properties (e.g. objects) the logical path forms a prefix to relative paths of properties defined + * within nested structures. + *

+ * See the logical path specification for full details on both relative and absolute paths. + * + * @param value the logical path of this {@linkplain Property property}. + * @return a reference to this {@linkplain Property property}. + */ + public final Property path(@Nonnull String value) { this.path = value; + return this; } /** - * The type of the property. + * The type of this {@linkplain Property property}. *

- * Types may be simple (e.g. int8) or complex (e.g. object). Simple types always define a single column. Complex + * Types may be simple (e.g. int8) or complex (e.g. object). Simple types always define a single column. Complex * types may define one or more columns depending on their structure. + * + * @return the type of this {@linkplain Property property}. */ - public final PropertyType propertyType() { - return this.propertyType; + public final PropertyType type() { + return this.type; } - public final void setPropertyType(PropertyType value) { - this.propertyType = value; + /** + * Sets the type of this {@linkplain Property property}. + *

+ * Types may be simple (e.g. int8) or complex (e.g. object). Simple types always define a single column. Complex + * types may define one or more columns depending on their structure. + * + * @param value the type of this {@linkplain Property property}. + * @return a reference to this {@linkplain Property property}. + */ + public final Property type(PropertyType value) { + this.type = value; + return this; } -} \ No newline at end of file +} diff --git a/java/src/main/java/com/azure/data/cosmos/serialization/hybridrow/schemas/PropertyType.java b/java/src/main/java/com/azure/data/cosmos/serialization/hybridrow/schemas/PropertyType.java index c2f8b16..94d2b1a 100644 --- a/java/src/main/java/com/azure/data/cosmos/serialization/hybridrow/schemas/PropertyType.java +++ b/java/src/main/java/com/azure/data/cosmos/serialization/hybridrow/schemas/PropertyType.java @@ -3,54 +3,106 @@ package com.azure.data.cosmos.serialization.hybridrow.schemas; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonSubTypes.Type; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.annotation.JsonTypeInfo.Id; + +import java.util.PrimitiveIterator; + /** * The base class for property types both primitive and complex. */ +@JsonTypeInfo(use = Id.NAME, property = "type") +@JsonSubTypes({ + // Composite types + @Type(value = ArrayPropertyType.class, name = "array"), + @Type(value = MapPropertyType.class, name = "map"), + @Type(value = ObjectPropertyType.class, name="object"), + @Type(value = ScopePropertyType.class, name="scope"), + @Type(value = SetPropertyType.class, name="set"), + @Type(value = TaggedPropertyType.class, name="tagged"), + @Type(value = TuplePropertyType.class, name="tuple"), + // Primitive types + @Type(value = PrimitivePropertyType.class, name="int32"), + @Type(value = PrimitivePropertyType.class, name="uint32"), + @Type(value = PrimitivePropertyType.class, name="utf8") +}) public abstract class PropertyType { + @JsonProperty(required = true) + private TypeKind type; + + @JsonProperty private String apiType; + + @JsonProperty(defaultValue = "true") private boolean nullable; - private TypeKind type = TypeKind.values()[0]; protected PropertyType() { this.nullable(true); } /** - * Api-specific type annotations for the property. + * API-specific type annotations for this {@linkplain Property property}. + * + * @return API-specific type annotations for this {@linkplain Property property}. */ public final String apiType() { return this.apiType; } + /** + * Sets API-specific type annotations for this {@linkplain Property property}. + * + * @param value API-specific type annotations for this {@linkplain Property property}. + * @return a reference to this {@linkplain Property property}. + */ public final PropertyType apiType(String value) { this.apiType = value; return this; } /** - * {@code true} if the property can be {@code null} + * {@code true} if the {@linkplain Property property} can be {@code null}. *

* Default: {@code true} + * + * @return {@code true} if the {@linkplain Property property} can be {@code null, otherwise {@code false}}. */ public final boolean nullable() { return this.nullable; } + /** + * Sets a flag indicating whether the {@linkplain Property property} can be {@code null}. + * + * @param value {@code true} indicates that this {@linkplain Property property} can be {@code null}. + * @return a reference to this {@linkplain Property property}. + */ public final PropertyType nullable(boolean value) { this.nullable = value; return this; } /** - * The logical type of the property + * The logical type of this {@linkplain Property property}. + * + * @return the logical type of this {@linkplain Property property}. */ public final TypeKind type() { return this.type; } + /** + * Sets the logical type of this {@linkplain Property property}. + * + * @param value the logical type of this {@linkplain Property property}. + * @return a reference to this {@linkplain Property property}. + */ public final PropertyType type(TypeKind value) { this.type = value; return this; } -} \ No newline at end of file +} diff --git a/java/src/main/java/com/azure/data/cosmos/serialization/hybridrow/schemas/Schema.java b/java/src/main/java/com/azure/data/cosmos/serialization/hybridrow/schemas/Schema.java index a57233d..6c4c3f4 100644 --- a/java/src/main/java/com/azure/data/cosmos/serialization/hybridrow/schemas/Schema.java +++ b/java/src/main/java/com/azure/data/cosmos/serialization/hybridrow/schemas/Schema.java @@ -7,6 +7,7 @@ import com.azure.data.cosmos.core.Json; import com.azure.data.cosmos.serialization.hybridrow.SchemaId; import com.azure.data.cosmos.serialization.hybridrow.layouts.Layout; import com.azure.data.cosmos.serialization.hybridrow.layouts.LayoutCompiler; +import com.fasterxml.jackson.annotation.JsonProperty; import javax.annotation.Nonnull; import javax.annotation.Nullable; @@ -27,23 +28,42 @@ import static com.google.common.base.Preconditions.checkNotNull; */ public class Schema { - private String comment; + // Required fields + + @JsonProperty(required = true) + private SchemaId id; + + @JsonProperty(required = true) private String name; + + @JsonProperty(defaultValue = "schema", required = true) + private TypeKind type; + + // Optional fields + + @JsonProperty + private String comment; + + @JsonProperty private SchemaOptions options; + + @JsonProperty + private List properties; + + @JsonProperty + private SchemaLanguageVersion version; + + // TODO: DANOBLE: how do these properties serialize? + private List partitionKeys; private List primaryKeys; - private List properties; - private SchemaId schemaId = SchemaId.NONE; private List staticKeys; - private TypeKind type = TypeKind.values()[0]; - private SchemaLanguageVersion version = SchemaLanguageVersion.values()[0]; /** * Initializes a new instance of the {@link Schema} class. */ - public Schema() { - this.type(TypeKind.SCHEMA); - this.properties = Collections.emptyList(); + private Schema() { + this.type = TypeKind.SCHEMA; this.partitionKeys = Collections.emptyList(); this.primaryKeys = Collections.emptyList(); this.staticKeys = Collections.emptyList(); @@ -51,12 +71,24 @@ public class Schema { /** * An (optional) comment describing the purpose of this schema. + *

* Comments are for documentary purpose only and do not affect the schema at runtime. + * + * @return the comment on this {@linkplain Schema schema} or {@code null}, if there is no comment. */ public final String comment() { return this.comment; } + /** + * Sets the (optional) comment describing the purpose of this schema. + *

+ * Comments are for documentary purpose only and do not affect the schema at runtime. + * + * @param value a comment on this {@linkplain Schema schema} or {@code null} to remove the comment, if any, on this + * {@linkplain Schema schema}. + * @return a reference to this {@linkplain Schema schema}. + */ public final Schema comment(String value) { this.comment = value; return this; @@ -65,30 +97,41 @@ public class Schema { /** * Compiles this logical schema into a physical layout that can be used to read and write rows. * - * @param ns The namespace within which this schema is defined. + * @param namespace The namespace within which this schema is defined. * @return The layout for the schema. */ - public final Layout compile(Namespace ns) { + public final Layout compile(Namespace namespace) { - checkNotNull(ns, "expected non-null ns"); - checkArgument(ns.schemas().contains(this)); + checkNotNull(namespace, "expected non-null ns"); + checkArgument(namespace.schemas().contains(this)); - return LayoutCompiler.compile(ns, this); + return LayoutCompiler.compile(namespace, this); } /** - * The name of the schema. + * The name of this {@linkplain Schema schema}. *

- * The name of a schema MUST be unique within its namespace. - * - * Names must begin with an alpha-numeric character and can only contain alpha-numeric characters and - * underscores. + * The name of a schema MUST be unique within its namespace. Names must begin with an alpha-numeric character and + * can only contain alpha-numeric characters and underscores. + * + * @return the name of this {@linkplain Schema schema} or {@code null}, if the name has not yet been set. */ public final String name() { return this.name; } - public final Schema name(String value) { + /** + * Sets the name of this {@linkplain Schema schema}. + *

+ * The name of a schema MUST be unique within its namespace. Names must begin with an alpha-numeric character and + * can only contain alpha-numeric characters and underscores. + * + * @param value a name for this {@linkplain Schema schema}. + * @return a reference to this {@linkplain Schema schema}. + */ + @Nonnull + public final Schema name(@Nonnull String value) { + checkNotNull(value); this.name = value; return this; } @@ -112,8 +155,8 @@ public class Schema { * @return A logical schema, if the value parses. */ public static Optional parse(String value) { - return Json.parse(value); // TODO: DANOBLE: perform structural validation on the Schema after JSON - // parsing + return Json.parse(value, Schema.class); + // TODO: DANOBLE: perform structural validation on the Schema after JSON parsing } /** @@ -173,11 +216,11 @@ public class Schema { * Identifiers must be unique within the scope of the database in which they are used. */ public final SchemaId schemaId() { - return this.schemaId; + return this.id; } public final Schema schemaId(SchemaId value) { - this.schemaId = value; + this.id = value; return this; } diff --git a/java/src/main/java/com/azure/data/cosmos/serialization/hybridrow/schemas/SchemaHash.java b/java/src/main/java/com/azure/data/cosmos/serialization/hybridrow/schemas/SchemaHash.java index 8cc86f4..650c7ce 100644 --- a/java/src/main/java/com/azure/data/cosmos/serialization/hybridrow/schemas/SchemaHash.java +++ b/java/src/main/java/com/azure/data/cosmos/serialization/hybridrow/schemas/SchemaHash.java @@ -73,7 +73,7 @@ public final class SchemaHash { HashCode128 hash = seed; hash = Murmur3Hash.Hash128(p.path(), hash); - hash = SchemaHash.computeHash(ns, p.propertyType(), hash); + hash = SchemaHash.computeHash(ns, p.type(), hash); return hash; } diff --git a/java/src/main/java/com/azure/data/cosmos/serialization/hybridrow/schemas/SchemaLanguageVersion.java b/java/src/main/java/com/azure/data/cosmos/serialization/hybridrow/schemas/SchemaLanguageVersion.java index ed11e87..4fb4663 100644 --- a/java/src/main/java/com/azure/data/cosmos/serialization/hybridrow/schemas/SchemaLanguageVersion.java +++ b/java/src/main/java/com/azure/data/cosmos/serialization/hybridrow/schemas/SchemaLanguageVersion.java @@ -12,26 +12,49 @@ public enum SchemaLanguageVersion { /** * Initial version of the HybridRow Schema Description Lanauge. */ - V1((byte)0); + V1((byte) 0, "v1"); public static final int BYTES = Byte.BYTES; private static HashMap mappings; + private String friendlyName; private byte value; - SchemaLanguageVersion(byte value) { + SchemaLanguageVersion(byte value, String text) { this.value = value; + this.friendlyName = text; mappings().put(value, this); } - public byte getValue() { - return this.value; + /** + * Returns the friendly name of this enum constant. + * + * @return the friendly name of this enum constant. + * @see #toString() + */ + public String friendlyName() { + return this.friendlyName; } - public static SchemaLanguageVersion forValue(byte value) { + public static SchemaLanguageVersion from(byte value) { return mappings().get(value); } + /** + * Returns the friendly name of this enum constant. + * + * @return the friendly name of this enum constant. + * @see #friendlyName() + */ + @Override + public String toString() { + return this.friendlyName; + } + + public byte value() { + return this.value; + } + private static HashMap mappings() { if (mappings == null) { synchronized (SchemaLanguageVersion.class) { @@ -42,4 +65,6 @@ public enum SchemaLanguageVersion { } return mappings; } + + } \ No newline at end of file diff --git a/java/src/main/java/com/azure/data/cosmos/serialization/hybridrow/schemas/SchemaValidator.java b/java/src/main/java/com/azure/data/cosmos/serialization/hybridrow/schemas/SchemaValidator.java index 2bdd4d9..7d3830a 100644 --- a/java/src/main/java/com/azure/data/cosmos/serialization/hybridrow/schemas/SchemaValidator.java +++ b/java/src/main/java/com/azure/data/cosmos/serialization/hybridrow/schemas/SchemaValidator.java @@ -110,7 +110,7 @@ public final class SchemaValidator { Property p, Schema s, Map schemas, Map ids) { Assert.isValidIdentifier(p.path(), "Property path"); - SchemaValidator.visit(p.propertyType(), null, schemas, ids); + SchemaValidator.visit(p.type(), null, schemas, ids); } private static void visit( @@ -164,7 +164,7 @@ public final class SchemaValidator { Map pathDupCheck = new HashMap<>(op.properties().size()); for (Property nested : op.properties()) { Assert.duplicateCheck(nested.path(), nested, pathDupCheck, "Property path", "Object"); - SchemaValidator.visit(nested.propertyType(), p, schemas, ids); + SchemaValidator.visit(nested.type(), p, schemas, ids); } return; } diff --git a/java/src/main/java/com/azure/data/cosmos/serialization/hybridrow/schemas/StorageKind.java b/java/src/main/java/com/azure/data/cosmos/serialization/hybridrow/schemas/StorageKind.java index 627df17..5e772f5 100644 --- a/java/src/main/java/com/azure/data/cosmos/serialization/hybridrow/schemas/StorageKind.java +++ b/java/src/main/java/com/azure/data/cosmos/serialization/hybridrow/schemas/StorageKind.java @@ -19,7 +19,7 @@ public enum StorageKind { *

* This is indicative of an error in the the column specification. */ - NONE(-1), + NONE(-1, "none"), /** * The property defines a sparse column @@ -28,14 +28,14 @@ public enum StorageKind { * linked list at the end of the row. Access time for sparse columns is proportional to the number of sparse columns * in the row. */ - SPARSE(0), + SPARSE(0, "sparse"), /** * The property is a fixed-length, space-reserved column *

* The column will consume 1 null-bit, and its byte-width regardless of whether the value is present in the row. */ - FIXED(1), + FIXED(1, "fixed"), /** * The property is a variable-length column. @@ -46,7 +46,7 @@ public enum StorageKind { * When a long value is marked variable then a null-bit is reserved and the value is optionally encoded as * variable if small enough to fit, otherwise the null-bit is set and the value is encoded as sparse. */ - VARIABLE(2); + VARIABLE(2, "variable"); public static final int BYTES = Integer.BYTES; @@ -57,17 +57,28 @@ public enum StorageKind { return new Int2ObjectArrayMap(values, storageKinds); }); - private int value; + private final String friendlyName; + private final int value; - StorageKind(int value) { + StorageKind(int value, String friendlyName) { + this.friendlyName = friendlyName; this.value = value; } - public int value() { - return this.value; + public String friendlyName() { + return this.friendlyName; } public static StorageKind from(int value) { return mappings.get().get(value); } + + @Override + public String toString() { + return this.friendlyName; + } + + public int value() { + return this.value; + } } diff --git a/java/src/main/java/com/azure/data/cosmos/serialization/hybridrow/schemas/TypeKind.java b/java/src/main/java/com/azure/data/cosmos/serialization/hybridrow/schemas/TypeKind.java index 6cdcf1e..75e2b07 100644 --- a/java/src/main/java/com/azure/data/cosmos/serialization/hybridrow/schemas/TypeKind.java +++ b/java/src/main/java/com/azure/data/cosmos/serialization/hybridrow/schemas/TypeKind.java @@ -5,6 +5,7 @@ package com.azure.data.cosmos.serialization.hybridrow.schemas; // TODO: DANOBLE: Fixup JSON-serialized naming for agreement with the dotnet code +import com.fasterxml.jackson.annotation.JsonEnumDefaultValue; import com.google.common.base.Suppliers; import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; @@ -19,7 +20,8 @@ public enum TypeKind { /** * Reserved. */ - INVALID(0), + @JsonEnumDefaultValue + INVALID(0, "invalid"), /** * The literal null. @@ -27,166 +29,166 @@ public enum TypeKind { * When used as a fixed column, only a presence bit is allocated. When used as a sparse column, a sparse value with * 0-length payload is written. */ - NULL(1), + NULL(1, "null"), /** * A boolean property. *

* Boolean properties are allocated a single bit when schematized within a row. */ - BOOLEAN(2), + BOOLEAN(2, "bool"), /** * 8-bit signed integer. */ - INT_8(3), + INT_8(3, "int8"), /** * 16-bit signed integer. */ - INT_16(4), + INT_16(4, "int16"), /** * 32-bit signed integer. */ - INT_32(5), + INT_32(5, "int32"), /** * 64-bit signed integer. */ - INT_64(6), + INT_64(6, "int64"), /** * 8-bit unsigned integer. */ - UINT_8(7), + UINT_8(7, "uint8"), /** * 16-bit unsigned integer. */ - UINT_16(8), + UINT_16(8, "uint16"), /** * 32-bit unsigned integer. */ - UINT_32(9), + UINT_32(9, "uint32"), /** * 64-bit unsigned integer. */ - UINT_64(10), + UINT_64(10, "uint64"), /** * Variable length encoded signed integer. */ - VAR_INT(11), + VAR_INT(11, "varint"), /** * Variable length encoded unsigned integer. */ - VAR_UINT(12), + VAR_UINT(12, "varuint"), /** * 32-bit IEEE 754 floating point value. */ - FLOAT_32(13), + FLOAT_32(13, "float32"), /** * 64-bit IEEE 754 floating point value. */ - FLOAT_64(14), + FLOAT_64(14, "float64"), /** * 128-bit IEEE 754-2008 floating point value. */ - FLOAT_128(15), + FLOAT_128(15, "float128"), /** * 128-bit floating point value. * * @see java.math.BigDecimal */ - DECIMAL(16), + DECIMAL(16, "decimal"), /** * 64-bit date/time value in 100ns increments from C# epoch. * * @see java.time.OffsetDateTime */ - DATE_TIME(17), + DATE_TIME(17, "datetime"), /** * 64-bit date/time value in milliseconds increments from Unix epoch. * * @see com.azure.data.cosmos.serialization.hybridrow.UnixDateTime */ - UNIX_DATE_TIME(18), + UNIX_DATE_TIME(18, "unixdatetime"), /** * 128-bit globally unique identifier (in little-endian byte order). */ - GUID(19), + GUID(19, "guid"), /** * 12-byte MongoDB Object Identifier (in little-endian byte order). */ - MONGODB_OBJECT_ID(20), + MONGODB_OBJECT_ID(20, "mongodb.objectid"), /** * Zero to MAX_ROW_SIZE bytes encoded as UTF-8 code points. */ - UTF_8(21), + UTF_8(21, "utf8"), /** * Zero to MAX_ROW_SIZE untyped bytes. */ - BINARY(22), + BINARY(22, "binary"), /** * An object property. */ - OBJECT(23), + OBJECT(23, "object"), /** * An array property, either typed or untyped. */ - ARRAY(24), + ARRAY(24, "array"), /** * A set property, either typed or untyped. */ - SET(25), + SET(25, "set"), /** * A map property, either typed or untyped. */ - MAP(26), + MAP(26, "map"), /** * A tuple property. Tuples are typed, finite, ordered, sets. */ - TUPLE(27), + TUPLE(27, "tuple"), /** * A tagged property. *

* Tagged properties pair one or more typed values with an API-specific uint8 type code. */ - TAGGED(28), + TAGGED(28, "tagged"), /** * A row with schema. *

* May define either a top-level table schema or a UDT (nested row). */ - SCHEMA(29), + SCHEMA(29, "schema"), /** * An untyped sparse field. *

* May only be used to define the type within a nested scope. */ - ANY(30); + ANY(30, "any"); public static final int BYTES = Integer.BYTES; @@ -197,12 +199,35 @@ public enum TypeKind { return new Int2ObjectOpenHashMap<>(values, typeKinds); }); - private int value; + private final String friendlyName; + private final int value; - TypeKind(int value) { + TypeKind(final int value, final String friendlyName) { + this.friendlyName = friendlyName; this.value = value; } + /** + * Returns the friendly name of this enum constant. + * + * @return the friendly name of this enum constant. + * @see #toString() + */ + public String friendlyName() { + return this.friendlyName; + } + + /** + * Returns the friendly name of this enum constant. + * + * @return the friendly name of this enum constant. + * @see #friendlyName() + */ + @Override + public String toString() { + return this.friendlyName; + } + public int value() { return this.value; } diff --git a/java/src/main/java/com/azure/data/cosmos/serialization/hybridrow/schemas/UdtPropertyType.java b/java/src/main/java/com/azure/data/cosmos/serialization/hybridrow/schemas/UdtPropertyType.java index 1ced491..1e382c7 100644 --- a/java/src/main/java/com/azure/data/cosmos/serialization/hybridrow/schemas/UdtPropertyType.java +++ b/java/src/main/java/com/azure/data/cosmos/serialization/hybridrow/schemas/UdtPropertyType.java @@ -27,8 +27,7 @@ public class UdtPropertyType extends ScopePropertyType { /** * The identifier of the UDT schema defining the structure for the nested row. *

- * The UDT schema MUST be defined within the same {@link Namespace} as the schema that - * references it. + * The UDT schema MUST be defined within the same {@link Namespace} as the schema that references it. */ public final String name() { return this.name; diff --git a/java/src/test/java/com/azure/data/cosmos/serialization/hybridrow/layouts/SystemSchemaTest.java b/java/src/test/java/com/azure/data/cosmos/serialization/hybridrow/layouts/SystemSchemaTest.java index a05e4af..f64cc91 100644 --- a/java/src/test/java/com/azure/data/cosmos/serialization/hybridrow/layouts/SystemSchemaTest.java +++ b/java/src/test/java/com/azure/data/cosmos/serialization/hybridrow/layouts/SystemSchemaTest.java @@ -13,9 +13,9 @@ import static org.testng.Assert.*; @Test(groups = "unit") public class SystemSchemaTest { - private static final Path SchemaFile = Paths.get("data", "CustomerSchema.json"); + private static final Path SCHEMA_FILE = Paths.get("test-data", "CustomerSchema.json"); - @Test(enabled = false) + @Test public void testLoadSchema() { final LayoutResolver layoutResolver = SystemSchema.layoutResolver(); diff --git a/java/src/test/resources/log4j.properties b/java/src/test/resources/log4j.properties index 00b89ec..5e59e08 100644 --- a/java/src/test/resources/log4j.properties +++ b/java/src/test/resources/log4j.properties @@ -1,16 +1,6 @@ -# this is the log4j configuration for tests +# log4j configuration for tests -# Set root logger level to DEBUG and its only appender to A1. log4j.rootLogger=INFO, A1 - -# Set HTTP components' logger to INFO - -log4j.category.io.netty=INFO -log4j.category.io.reactivex=INFO -log4j.category.com.microsoft.azure.cosmosdb=INFO -# A1 is set to be a ConsoleAppender. log4j.appender.A1=org.apache.log4j.ConsoleAppender - -# A1 uses PatternLayout. log4j.appender.A1.layout=org.apache.log4j.PatternLayout log4j.appender.A1.layout.ConversionPattern=%d %5X{pid} [%t] %-5p %c - %m%n