/* * 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.eigenbase.rex; import java.util.*; import org.eigenbase.relopt.*; import org.eigenbase.reltype.*; /** * Visitor which checks the validity of a {@link RexNode} expression. * * <p>There are two modes of operation: * * <ul> * <li>Use<code>fail=true</code> to throw an {@link AssertionError} as soon as * an invalid node is detected: * * <blockquote><code>RexNode node;<br> * RelDataType rowType;<br> * assert new RexChecker(rowType, true).isValid(node);</code></blockquote> * * This mode requires that assertions are enabled.</li> * * <li>Use <code>fail=false</code> to test for validity without throwing an * error. * * <blockquote><code>RexNode node;<br> * RelDataType rowType;<br> * RexChecker checker = new RexChecker(rowType, false);<br> * node.accept(checker);<br> * if (!checker.valid) {<br> *    ...<br> * }</code></blockquote> * </li> * </ul> * * @see RexNode */ public class RexChecker extends RexVisitorImpl<Boolean> { //~ Instance fields -------------------------------------------------------- protected final boolean fail; protected final List<RelDataType> inputTypeList; protected int failCount; //~ Constructors ----------------------------------------------------------- /** * Creates a RexChecker with a given input row type. * * <p>If <code>fail</code> is true, the checker will throw an {@link * AssertionError} if an invalid node is found and assertions are enabled. * * <p>Otherwise, each method returns whether its part of the tree is valid. * * @param inputRowType Input row type * @param fail Whether to throw an {@link AssertionError} if an invalid node * is detected */ public RexChecker(final RelDataType inputRowType, boolean fail) { this(RelOptUtil.getFieldTypeList(inputRowType), fail); } /** * Creates a RexChecker with a given set of input fields. * * <p>If <code>fail</code> is true, the checker will throw an {@link * AssertionError} if an invalid node is found and assertions are enabled. * * <p>Otherwise, each method returns whether its part of the tree is valid. * * @param inputTypeList Input row type * @param fail Whether to throw an {@link AssertionError} if an invalid node * is detected */ public RexChecker(List<RelDataType> inputTypeList, boolean fail) { super(true); this.inputTypeList = inputTypeList; this.fail = fail; } //~ Methods ---------------------------------------------------------------- /** * Returns the number of failures encountered. * * @return Number of failures */ public int getFailureCount() { return failCount; } public Boolean visitInputRef(RexInputRef ref) { final int index = ref.getIndex(); if ((index < 0) || (index >= inputTypeList.size())) { assert !fail : "RexInputRef index " + index + " out of range 0.." + (inputTypeList.size() - 1); ++failCount; return false; } if (!ref.getType().isStruct() && !RelOptUtil.eq("ref", ref.getType(), "input", inputTypeList.get(index), fail)) { assert !fail; ++failCount; return false; } return true; } public Boolean visitLocalRef(RexLocalRef ref) { assert !fail : "RexLocalRef illegal outside program"; ++failCount; return false; } public Boolean visitCall(RexCall call) { for (RexNode operand : call.getOperands()) { Boolean valid = operand.accept(this); if (valid != null && !valid) { return false; } } return true; } public Boolean visitFieldAccess(RexFieldAccess fieldAccess) { super.visitFieldAccess(fieldAccess); final RelDataType refType = fieldAccess.getReferenceExpr().getType(); assert refType.isStruct(); final RelDataTypeField field = fieldAccess.getField(); final int index = field.getIndex(); if ((index < 0) || (index > refType.getFieldList().size())) { assert !fail; ++failCount; return false; } final RelDataTypeField typeField = refType.getFieldList().get(index); if (!RelOptUtil.eq( "type1", typeField.getType(), "type2", fieldAccess.getType(), fail)) { assert !fail; ++failCount; return false; } return true; } /** * Returns whether an expression is valid. */ public final boolean isValid(RexNode expr) { return expr.accept(this); } } // End RexChecker.java