[NO ISSUE][TYPE] Introduce IATypeVisitor
- user mode changes: no
- storage format changes: no
- interface changes: yes
Interface changes:
- Add accept method to IAType
Details:
Several operation require to traverse IAType to process types. The IATypeVisitor provides
an easy way to traverse nested types (e.g., comparing AsterixDB type and Parquet Type)
Change-Id: I16cbb7b3675846b3a4097d5a38ee15fa28634f92
Reviewed-on: https://asterix-gerrit.ics.uci.edu/c/asterixdb/+/12824
Tested-by: Jenkins <jenkins@fulliautomatix.ics.uci.edu>
Integration-Tests: Jenkins <jenkins@fulliautomatix.ics.uci.edu>
Reviewed-by: Wael Alkowaileet <wael.y.k@gmail.com>
Reviewed-by: Dmitry Lychagin <dmitry.lychagin@couchbase.com>
diff --git a/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/types/ARecordType.java b/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/types/ARecordType.java
index b91b4ae..a79b8de 100644
--- a/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/types/ARecordType.java
+++ b/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/types/ARecordType.java
@@ -428,4 +428,9 @@
}
return false;
}
+
+ @Override
+ public <R, T> R accept(IATypeVisitor<R, T> visitor, T arg) {
+ return visitor.visit(this, arg);
+ }
}
diff --git a/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/types/AUnionType.java b/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/types/AUnionType.java
index 7ef18d4..caa926d 100644
--- a/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/types/AUnionType.java
+++ b/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/types/AUnionType.java
@@ -70,6 +70,16 @@
return false;
}
+ public IAType getType(ATypeTag typeTag) {
+ for (int i = 0; i < unionList.size(); i++) {
+ IAType type = unionList.get(i);
+ if (typeTag == type.getTypeTag()) {
+ return type;
+ }
+ }
+ return null;
+ }
+
public IAType getActualType() {
return unionList.get(AUnionType.OPTIONAL_TYPE_INDEX_IN_UNION_LIST);
}
@@ -241,6 +251,11 @@
return jsonObject;
}
+ @Override
+ public <R, T> R accept(IATypeVisitor<R, T> visitor, T arg) {
+ return visitor.visit(this, arg);
+ }
+
public static IJsonSerializable fromJson(IPersistedResourceRegistry registry, JsonNode json)
throws HyracksDataException {
String typeName = json.get(TYPE_NAME_FIELD).asText();
diff --git a/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/types/AbstractCollectionType.java b/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/types/AbstractCollectionType.java
index ef15224..9b75566 100644
--- a/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/types/AbstractCollectionType.java
+++ b/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/types/AbstractCollectionType.java
@@ -70,6 +70,11 @@
return isTyped() && itemType.getTypeName().equals(type.getTypeName());
}
+ @Override
+ public <R, T> R accept(IATypeVisitor<R, T> visitor, T arg) {
+ return visitor.visit(this, arg);
+ }
+
JsonNode convertToJson(IPersistedResourceRegistry registry, Class<? extends IJsonSerializable> clazz, long version)
throws HyracksDataException {
final ObjectNode jsonObject = registry.getClassIdentifier(clazz, version);
diff --git a/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/types/BuiltinType.java b/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/types/BuiltinType.java
index bdac9e9..2404ac2d 100644
--- a/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/types/BuiltinType.java
+++ b/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/types/BuiltinType.java
@@ -1081,6 +1081,14 @@
return getType().getTypeTag().serialize();
}
+ /**
+ * Visit built-in type as a flat type
+ */
+ @Override
+ public <R, T> R accept(IATypeVisitor<R, T> visitor, T arg) {
+ return visitor.visitFlat(this, arg);
+ }
+
private static JsonNode convertToJson(IPersistedResourceRegistry registry, short tag, long version) {
ObjectNode jsonNode = registry.getClassIdentifier(BuiltinType.class, version);
jsonNode.put(TAG_FIELD, tag);
@@ -1090,7 +1098,10 @@
@SuppressWarnings("squid:S1172") // unused parameter
public static IJsonSerializable fromJson(IPersistedResourceRegistry registry, JsonNode json) {
byte tag = (byte) json.get(TAG_FIELD).shortValue();
- ATypeTag typeTag = VALUE_TYPE_MAPPING[tag];
+ return getBuiltinType(VALUE_TYPE_MAPPING[tag]);
+ }
+
+ public static IAType getBuiltinType(ATypeTag typeTag) {
switch (typeTag) {
case TYPE:
return ALL_TYPE;
diff --git a/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/types/IAType.java b/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/types/IAType.java
index 0e6cc4f..ed4c2bb 100644
--- a/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/types/IAType.java
+++ b/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/types/IAType.java
@@ -31,4 +31,11 @@
public String getTypeName();
+ /**
+ * Allow for additional traversal and processing for {@link IAType}
+ *
+ * @param visitor visitor
+ * @param arg visitor's argument
+ */
+ <R, T> R accept(IATypeVisitor<R, T> visitor, T arg);
}
diff --git a/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/types/IATypeVisitor.java b/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/types/IATypeVisitor.java
new file mode 100644
index 0000000..0951763
--- /dev/null
+++ b/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/types/IATypeVisitor.java
@@ -0,0 +1,35 @@
+/*
+ * 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.asterix.om.types;
+
+/**
+ * Allows for a specialized processing for the {@link IAType}
+ *
+ * @param <R> return type
+ * @param <T> argument type
+ */
+public interface IATypeVisitor<R, T> {
+ R visit(ARecordType recordType, T arg);
+
+ R visit(AbstractCollectionType collectionType, T arg);
+
+ R visit(AUnionType unionType, T arg);
+
+ R visitFlat(IAType flatType, T arg);
+}
diff --git a/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/types/visitor/SimpleStringBuilderForIATypeVisitor.java b/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/types/visitor/SimpleStringBuilderForIATypeVisitor.java
new file mode 100644
index 0000000..9f0c82b
--- /dev/null
+++ b/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/types/visitor/SimpleStringBuilderForIATypeVisitor.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.asterix.om.types.visitor;
+
+import java.util.List;
+
+import org.apache.asterix.om.types.ARecordType;
+import org.apache.asterix.om.types.ATypeTag;
+import org.apache.asterix.om.types.AUnionType;
+import org.apache.asterix.om.types.AbstractCollectionType;
+import org.apache.asterix.om.types.IAType;
+import org.apache.asterix.om.types.IATypeVisitor;
+
+/**
+ * This visitor produces a oneliner JSON-like representation of {@link IAType} to be interpreted by the user.
+ */
+public class SimpleStringBuilderForIATypeVisitor implements IATypeVisitor<Void, StringBuilder> {
+ /**
+ * Example: {"field1":string,"field2":[bigint]}
+ */
+ @Override
+ public Void visit(ARecordType recordType, StringBuilder arg) {
+ String[] fieldNames = recordType.getFieldNames();
+ IAType[] fieldTypes = recordType.getFieldTypes();
+
+ arg.append("{");
+ for (int i = 0; i < fieldNames.length; i++) {
+ if (i > 0) {
+ arg.append(',');
+ }
+ arg.append(fieldNames[i]);
+ arg.append(':');
+ fieldTypes[i].accept(this, arg);
+ }
+ arg.append("}");
+ return null;
+ }
+
+ /**
+ * Example:
+ * - Array: [{"field1":bigint}]
+ * - Multiset: {{bigint}}
+ */
+ @Override
+ public Void visit(AbstractCollectionType collectionType, StringBuilder arg) {
+ IAType itemType = collectionType.getItemType();
+
+ arg.append(collectionType.getTypeTag() == ATypeTag.ARRAY ? "[" : "{{");
+ itemType.accept(this, arg);
+ arg.append(collectionType.getTypeTag() == ATypeTag.ARRAY ? "]" : "}}");
+ return null;
+ }
+
+ /**
+ * Example: A union type of array, object, and bigint
+ * - <[{"field1":...}],{"field1:...},bigint>
+ */
+ @Override
+ public Void visit(AUnionType unionType, StringBuilder arg) {
+ List<IAType> unionList = unionType.getUnionList();
+
+ arg.append("<");
+ for (int i = 0; i < unionList.size(); i++) {
+ if (i > 0) {
+ arg.append(',');
+ }
+ unionList.get(i).accept(this, arg);
+ }
+ arg.append(">");
+ return null;
+ }
+
+ /**
+ * Example:
+ * - bigint
+ * - string
+ */
+ @Override
+ public Void visitFlat(IAType flatType, StringBuilder arg) {
+ arg.append(flatType.getTypeTag());
+ return null;
+ }
+}
diff --git a/asterixdb/asterix-om/src/test/java/org/apache/asterix/test/om/types/visitor/TypeSimpleStringBuilderTest.java b/asterixdb/asterix-om/src/test/java/org/apache/asterix/test/om/types/visitor/TypeSimpleStringBuilderTest.java
new file mode 100644
index 0000000..8186810
--- /dev/null
+++ b/asterixdb/asterix-om/src/test/java/org/apache/asterix/test/om/types/visitor/TypeSimpleStringBuilderTest.java
@@ -0,0 +1,121 @@
+/*
+ * 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.asterix.test.om.types.visitor;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.asterix.om.types.AOrderedListType;
+import org.apache.asterix.om.types.ARecordType;
+import org.apache.asterix.om.types.AUnionType;
+import org.apache.asterix.om.types.AUnorderedListType;
+import org.apache.asterix.om.types.BuiltinType;
+import org.apache.asterix.om.types.IAType;
+import org.apache.asterix.om.types.visitor.SimpleStringBuilderForIATypeVisitor;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class TypeSimpleStringBuilderTest {
+ private static final ARecordType ROOT_TYPE;
+ private static final String EXPECTED_STRING;
+
+ static {
+ StringBuilder expectedStringBuilder = new StringBuilder();
+ //Record with two fields
+ ARecordType recordType = createNestedRecord(expectedStringBuilder);
+ String recordTypeString = getStringAndReset(expectedStringBuilder);
+
+ //Array of records
+ AOrderedListType arrayOfRecords = new AOrderedListType(recordType, "arrayOfRecords");
+ surroundString(expectedStringBuilder, "[", "]", recordTypeString);
+ String arrayOfRecordsString = getStringAndReset(expectedStringBuilder);
+
+ //Multiset of records
+ AUnorderedListType multiSetOfRecords = new AUnorderedListType(recordType, "multiSetOfRecords");
+ surroundString(expectedStringBuilder, "{{", "}}", recordTypeString);
+ String multiSetOfRecordsString = getStringAndReset(expectedStringBuilder);
+
+ //Union
+ List<IAType> unionList = new ArrayList<>();
+ unionList.add(recordType);
+ unionList.add(arrayOfRecords);
+ unionList.add(multiSetOfRecords);
+ unionList.add(BuiltinType.AINT64);
+ AUnionType unionType = new AUnionType(unionList, "unionType");
+ surroundString(expectedStringBuilder, "<", ">", recordTypeString, arrayOfRecordsString, multiSetOfRecordsString,
+ BuiltinType.AINT64.getTypeTag().toString());
+ String unionTypeString = getStringAndReset(expectedStringBuilder);
+
+ //Root type
+ String[] rootFieldNames = { BuiltinType.ANY.getTypeName(), arrayOfRecords.getTypeName(),
+ multiSetOfRecords.getTypeName(), unionType.getTypeName() };
+ IAType[] rootFieldTypes = { BuiltinType.ANY, arrayOfRecords, multiSetOfRecords, unionType };
+ ROOT_TYPE = new ARecordType("rootType", rootFieldNames, rootFieldTypes, false);
+ buildRecordString(expectedStringBuilder, rootFieldNames, rootFieldTypes[0].getTypeTag().toString(),
+ arrayOfRecordsString, multiSetOfRecordsString, unionTypeString);
+ EXPECTED_STRING = getStringAndReset(expectedStringBuilder);
+ }
+
+ private static ARecordType createNestedRecord(StringBuilder builder) {
+ String[] fieldNames = { "field1", "field2" };
+ IAType[] fieldTypes = { BuiltinType.ASTRING, BuiltinType.AINT64 };
+ buildRecordString(builder, fieldNames, fieldTypes[0].getTypeTag().toString(),
+ fieldTypes[1].getTypeTag().toString());
+ return new ARecordType("nestedRecord", fieldNames, fieldTypes, true);
+ }
+
+ private static void surroundString(StringBuilder builder, String open, String close, String... strings) {
+ builder.append(open);
+ for (int i = 0; i < strings.length; i++) {
+ if (i > 0) {
+ builder.append(',');
+ }
+ builder.append(strings[i]);
+ }
+ builder.append(close);
+ }
+
+ private static void buildRecordString(StringBuilder builder, String[] fieldNames, String... fieldTypesStrings) {
+ builder.append('{');
+ for (int i = 0; i < fieldNames.length; i++) {
+ if (i > 0) {
+ builder.append(',');
+ }
+ builder.append(fieldNames[i]);
+ builder.append(':');
+ builder.append(fieldTypesStrings[i]);
+ }
+ builder.append('}');
+ }
+
+ private static String getStringAndReset(StringBuilder expectedStringBuilder) {
+ String value = expectedStringBuilder.toString();
+ expectedStringBuilder.setLength(0);
+ return value;
+ }
+
+ @Test
+ public void testSimpleStringBuilderForIAType() {
+ StringBuilder builder = new StringBuilder();
+ SimpleStringBuilderForIATypeVisitor visitor = new SimpleStringBuilderForIATypeVisitor();
+ ROOT_TYPE.accept(visitor, builder);
+ Assert.assertEquals(EXPECTED_STRING, builder.toString());
+ }
+
+}