/* * Copyright 2008 Google Inc. * * Licensed 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 com.google.common.css.compiler.ast; import com.google.common.base.Preconditions; import com.google.common.css.SourceCodeLocation; import javax.annotation.Nullable; /** * A node representing a boolean expression to evaluate. * */ // TODO(user): It might be better for each operator to have a list of // operands, not just the left and right operand. Thus, A || B || C would be // represented as a single operator node and a list of three constant nodes // (similar to what the old tree structure does, but with explicit lists). // This might make the code larger but has the potential of faster // compilation (important for Gmail). This structure might also ease // constructing a canonical form of the expression and simplifying // expressions (such as EXPR || !EXPR). public class CssBooleanExpressionNode extends CssValueNode { /** * Boolean expression node types. The types are given in the order of their * precedence, "!" having the highest priority. */ public enum Type { CONSTANT(null), NOT("!"), AND("&&"), OR("||"); public static final String TRUE_CONSTANT = "TRUE"; public static final String FALSE_CONSTANT = "FALSE"; private final String operatorString; private Type(@Nullable String operatorString) { this.operatorString = operatorString; } public String getOperatorString() { return operatorString; } public boolean isConstant() { return this == CONSTANT; } public boolean isOperator() { return !isConstant(); } public boolean isBinaryOperator() { Preconditions.checkArgument(this.isOperator()); return this != NOT; } public boolean isUnaryOperator() { Preconditions.checkArgument(this.isOperator()); return this == NOT; } /** * Lower numbers are lower priority. */ public int getPriority() { return -this.ordinal(); } /** * For debugging only. */ @Override public String toString() { return getOperatorString(); } } private final Type type; private final CssBooleanExpressionNode left; private final CssBooleanExpressionNode right; /** * Constructor for a boolean expression node. * * @param type Type of node * @param value Value of node * @param left Left expression node * @param right Right expression node * @param sourceCodeLocation The location of the source code */ public CssBooleanExpressionNode(Type type, String value, @Nullable CssBooleanExpressionNode left, @Nullable CssBooleanExpressionNode right, @Nullable SourceCodeLocation sourceCodeLocation) { super(value, sourceCodeLocation); this.type = type; this.left = left; this.right = right; becomeParentForNode(this.left); becomeParentForNode(this.right); Preconditions.checkArgument(isValidExpressionTree()); } /** * Constructor for a boolean expression node. * * @param type Type of node * @param value Value of node * @param left Left expression node * @param sourceCodeLocation The location of the source code */ // TODO(oana): Maybe we want to change the constructor to build the right // child instead of the left ona for the unary operators. public CssBooleanExpressionNode(Type type, String value, @Nullable CssBooleanExpressionNode left, @Nullable SourceCodeLocation sourceCodeLocation) { this(type, value, left, null, sourceCodeLocation); } /** * Constructor for a boolean expression node. * * @param type * @param value * @param sourceCodeLocation */ public CssBooleanExpressionNode(Type type, String value, @Nullable SourceCodeLocation sourceCodeLocation) { this(type, value, null, null, sourceCodeLocation); } /** * Constructor for a boolean expression node. * * @param type Type of node * @param value Value of node */ public CssBooleanExpressionNode(Type type, String value) { this(type, value, null, null, null); } /** * Copy constructor. */ public CssBooleanExpressionNode(CssBooleanExpressionNode node) { super(node); this.type = node.getType(); if (node.getLeft() != null) { this.left = new CssBooleanExpressionNode(node.getLeft()); becomeParentForNode(this.left); } else { this.left = null; } if (node.getRight() != null) { this.right = new CssBooleanExpressionNode(node.getRight()); becomeParentForNode(this.right); } else { this.right = null; } } @Override public CssBooleanExpressionNode deepCopy() { return new CssBooleanExpressionNode(this); } public Type getType() { return type; } public CssBooleanExpressionNode getLeft() { return left; } public CssBooleanExpressionNode getRight() { return right; } /** * Checks if the expression tree is valid. */ public boolean isValidExpressionTree() { if (getType().isConstant()) { return true; } else if (!getType().isOperator()) { return getLeft() == null && getRight() == null; } else if (getType().isBinaryOperator()) { return getLeft() != null && getRight() != null; } else if (getType().isUnaryOperator()) { return getLeft() != null && getRight() == null; } else { // assert false return false; } } /** * For debugging only. */ private void appendChildExpression(StringBuilder sb, CssBooleanExpressionNode child) { if (child.getType().getPriority() >= getType().getPriority()) { sb.append(child.toString()); } else { sb.append("(" + child.toString() + ")"); } } /** * For debugging only. */ @Override public String toString() { if (!getType().isOperator()) { return getValue(); } else if (getType().isBinaryOperator()) { StringBuilder sb = new StringBuilder(); appendChildExpression(sb, getLeft()); sb.append(" " + this.getType().getOperatorString() + " "); appendChildExpression(sb, getRight()); return sb.toString(); } else if (getType().isUnaryOperator()) { StringBuilder sb = new StringBuilder(); sb.append(this.getType().getOperatorString()); appendChildExpression(sb, getLeft()); return sb.toString(); } else { // assert false; return null; } } }