/** * 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.drill.common.expression; import java.io.IOException; import java.util.Iterator; import org.antlr.runtime.ANTLRStringStream; import org.antlr.runtime.CommonTokenStream; import org.antlr.runtime.RecognitionException; import org.apache.drill.common.config.DrillConfig; import org.apache.drill.common.expression.PathSegment.ArraySegment; import org.apache.drill.common.expression.PathSegment.NameSegment; import org.apache.drill.common.expression.parser.ExprLexer; import org.apache.drill.common.expression.parser.ExprParser; import org.apache.drill.common.expression.parser.ExprParser.parse_return; import org.apache.drill.common.expression.visitors.ExprVisitor; import org.apache.drill.common.types.TypeProtos.MajorType; import org.apache.drill.common.types.Types; import org.apache.drill.exec.proto.UserBitShared.NamePart; import org.apache.drill.exec.proto.UserBitShared.NamePart.Type; import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.databind.DeserializationContext; import com.fasterxml.jackson.databind.deser.std.StdDeserializer; import com.google.common.base.Preconditions; import com.google.common.collect.Iterators; public class SchemaPath extends LogicalExpressionBase { private final NameSegment rootSegment; public static SchemaPath getSimplePath(String name) { return getCompoundPath(name); } public static SchemaPath getCompoundPath(String... strings) { NameSegment s = null; // loop through strings in reverse order for (int i = strings.length - 1; i >= 0; i--) { s = new NameSegment(strings[i], s); } return new SchemaPath(s); } @SuppressWarnings("unused") public PathSegment getLastSegment() { PathSegment s= rootSegment; while (s.getChild() != null) { s = s.getChild(); } return s; } @Deprecated public SchemaPath(String simpleName, ExpressionPosition pos) { super(pos); this.rootSegment = new NameSegment(simpleName); if (simpleName.contains(".")) { throw new IllegalStateException("This is deprecated and only supports simpe paths."); } } public NamePart getAsNamePart() { return getNamePart(rootSegment); } private static NamePart getNamePart(PathSegment s) { if (s == null) { return null; } NamePart.Builder b = NamePart.newBuilder(); if (s.getChild() != null) { b.setChild(getNamePart(s.getChild())); } if (s.isArray()) { if (s.getArraySegment().hasIndex()) { throw new IllegalStateException("You cannot convert a indexed schema path to a NamePart. NameParts can only reference Vectors, not individual records or values."); } b.setType(Type.ARRAY); } else { b.setType(Type.NAME); b.setName(s.getNameSegment().getPath()); } return b.build(); } private static PathSegment getPathSegment(NamePart n) { PathSegment child = n.hasChild() ? getPathSegment(n.getChild()) : null; if (n.getType() == Type.ARRAY) { return new ArraySegment(child); } else { return new NameSegment(n.getName(), child); } } public static SchemaPath create(NamePart namePart) { Preconditions.checkArgument(namePart.getType() == NamePart.Type.NAME); return new SchemaPath((NameSegment) getPathSegment(namePart)); } /** * A simple is a path where there are no repeated elements outside the lowest level of the path. * @return Whether this path is a simple path. */ public boolean isSimplePath() { PathSegment seg = rootSegment; while (seg != null) { if (seg.isArray() && !seg.isLastPath()) { return false; } seg = seg.getChild(); } return true; } public SchemaPath(SchemaPath path) { super(path.getPosition()); this.rootSegment = path.rootSegment; } public SchemaPath(NameSegment rootSegment) { super(ExpressionPosition.UNKNOWN); this.rootSegment = rootSegment; } public SchemaPath(NameSegment rootSegment, ExpressionPosition pos) { super(pos); this.rootSegment = rootSegment; } @Override public <T, V, E extends Exception> T accept(ExprVisitor<T, V, E> visitor, V value) throws E { return visitor.visitSchemaPath(this, value); } public SchemaPath getChild(String childPath) { NameSegment newRoot = rootSegment.cloneWithNewChild(new NameSegment(childPath)); return new SchemaPath(newRoot); } @SuppressWarnings("unused") public SchemaPath getUnindexedArrayChild() { NameSegment newRoot = rootSegment.cloneWithNewChild(new ArraySegment(null)); return new SchemaPath(newRoot); } public SchemaPath getChild(int index) { NameSegment newRoot = rootSegment.cloneWithNewChild(new ArraySegment(index)); return new SchemaPath(newRoot); } public NameSegment getRootSegment() { return rootSegment; } @Override public MajorType getMajorType() { return Types.LATE_BIND_TYPE; } @Override public int hashCode() { return ((rootSegment == null) ? 0 : rootSegment.hashCode()); } @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj == null) { return false; } if (!(obj instanceof SchemaPath)) { return false; } SchemaPath other = (SchemaPath) obj; if (rootSegment == null) { return (other.rootSegment == null); } return rootSegment.equals(other.rootSegment); } public boolean contains(Object obj) { if (this == obj) { return true; } if (obj == null) { return false; } if (!(obj instanceof SchemaPath)) { return false; } SchemaPath other = (SchemaPath) obj; return rootSegment == null || rootSegment.contains(other.rootSegment); } @Override public Iterator<LogicalExpression> iterator() { return Iterators.emptyIterator(); } @Override public String toString() { return ExpressionStringBuilder.toString(this); } public String toExpr() { return ExpressionStringBuilder.toString(this); } public String getAsUnescapedPath() { StringBuilder sb = new StringBuilder(); PathSegment seg = getRootSegment(); if (seg.isArray()) { throw new IllegalStateException("Drill doesn't currently support top level arrays"); } sb.append(seg.getNameSegment().getPath()); while ( (seg = seg.getChild()) != null) { if (seg.isNamed()) { sb.append('.'); sb.append(seg.getNameSegment().getPath()); } else { sb.append('['); sb.append(seg.getArraySegment().getIndex()); sb.append(']'); } } return sb.toString(); } public static class De extends StdDeserializer<SchemaPath> { public De() { super(LogicalExpression.class); } @Override public SchemaPath deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException { String expr = jp.getText(); if (expr == null || expr.isEmpty()) { return null; } try { // logger.debug("Parsing expression string '{}'", expr); ExprLexer lexer = new ExprLexer(new ANTLRStringStream(expr)); CommonTokenStream tokens = new CommonTokenStream(lexer); ExprParser parser = new ExprParser(tokens); //TODO: move functionregistry and error collector to injectables. //ctxt.findInjectableValue(valueId, forProperty, beanInstance) parse_return ret = parser.parse(); // ret.e.resolveAndValidate(expr, errorCollector); if (ret.e instanceof SchemaPath) { return (SchemaPath) ret.e; } else { throw new IllegalStateException("Schema path is not a valid format."); } } catch (RecognitionException e) { throw new RuntimeException(e); } } } }