blob: 778037b8ce514feb813a91eb9c9c5825f2cfebb8 [file] [log] [blame]
/*
* 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.iceberg.orc;
import java.util.Deque;
import java.util.List;
import org.apache.iceberg.relocated.com.google.common.base.Preconditions;
import org.apache.iceberg.relocated.com.google.common.collect.Lists;
import org.apache.orc.TypeDescription;
/**
* Generic visitor of an ORC Schema.
*/
public abstract class OrcSchemaVisitor<T> {
private final Deque<String> fieldNames = Lists.newLinkedList();
public static <T> List<T> visitSchema(TypeDescription schema, OrcSchemaVisitor<T> visitor) {
Preconditions.checkArgument(schema.getId() == 0, "TypeDescription must be root schema.");
List<TypeDescription> fields = schema.getChildren();
List<String> names = schema.getFieldNames();
return visitFields(fields, names, visitor);
}
public static <T> T visit(TypeDescription schema, OrcSchemaVisitor<T> visitor) {
switch (schema.getCategory()) {
case STRUCT:
return visitRecord(schema, visitor);
case UNION:
throw new UnsupportedOperationException("Cannot handle " + schema);
case LIST:
final T elementResult;
TypeDescription element = schema.getChildren().get(0);
visitor.beforeElementField(element);
try {
elementResult = visit(element, visitor);
} finally {
visitor.afterElementField(element);
}
return visitor.list(schema, elementResult);
case MAP:
final T keyResult;
final T valueResult;
TypeDescription key = schema.getChildren().get(0);
visitor.beforeKeyField(key);
try {
keyResult = visit(key, visitor);
} finally {
visitor.afterKeyField(key);
}
TypeDescription value = schema.getChildren().get(1);
visitor.beforeValueField(value);
try {
valueResult = visit(value, visitor);
} finally {
visitor.afterValueField(value);
}
return visitor.map(schema, keyResult, valueResult);
default:
return visitor.primitive(schema);
}
}
private static <T> List<T> visitFields(List<TypeDescription> fields, List<String> names,
OrcSchemaVisitor<T> visitor) {
Preconditions.checkArgument(fields.size() == names.size(), "Not all fields have names in ORC struct");
List<T> results = Lists.newArrayListWithExpectedSize(fields.size());
for (int i = 0; i < fields.size(); i++) {
TypeDescription field = fields.get(i);
String name = names.get(i);
visitor.beforeField(name, field);
try {
results.add(visit(field, visitor));
} finally {
visitor.afterField(name, field);
}
}
return results;
}
private static <T> T visitRecord(TypeDescription record, OrcSchemaVisitor<T> visitor) {
List<TypeDescription> fields = record.getChildren();
List<String> names = record.getFieldNames();
return visitor.record(record, names, visitFields(fields, names, visitor));
}
public String elementName() {
return "_elem";
}
public String keyName() {
return "_key";
}
public String valueName() {
return "_value";
}
public String currentFieldName() {
return fieldNames.peek();
}
public void beforeField(String name, TypeDescription type) {
fieldNames.push(name);
}
public void afterField(String name, TypeDescription type) {
fieldNames.pop();
}
public void beforeElementField(TypeDescription element) {
beforeField(elementName(), element);
}
public void afterElementField(TypeDescription element) {
afterField(elementName(), element);
}
public void beforeKeyField(TypeDescription key) {
beforeField(keyName(), key);
}
public void afterKeyField(TypeDescription key) {
afterField(keyName(), key);
}
public void beforeValueField(TypeDescription value) {
beforeField(valueName(), value);
}
public void afterValueField(TypeDescription value) {
afterField(valueName(), value);
}
public T record(TypeDescription record, List<String> names, List<T> fields) {
return null;
}
public T list(TypeDescription array, T element) {
return null;
}
public T map(TypeDescription map, T key, T value) {
return null;
}
public T primitive(TypeDescription primitive) {
return null;
}
protected String[] currentPath() {
return Lists.newArrayList(fieldNames.descendingIterator()).toArray(new String[0]);
}
protected String[] path(String name) {
List<String> list = Lists.newArrayList(fieldNames.descendingIterator());
list.add(name);
return list.toArray(new String[0]);
}
}