/*
* Copyright 2016 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.template.soy.passes;
import com.google.common.base.Optional;
import com.google.common.collect.ImmutableList;
import com.google.template.soy.basetree.CopyState;
import com.google.template.soy.exprtree.ExprEquivalence;
import com.google.template.soy.exprtree.ExprNode;
import com.google.template.soy.exprtree.ExprRootNode;
import java.util.List;
/** Condition for a particular control flow branch. */
abstract class Condition {
abstract Condition copy();
abstract boolean isDefaultCond();
static Condition getEmptyCondition() {
return EmptyCondition.INSTANCE;
}
static Condition createIfCondition() {
return new IfCondition();
}
static Condition createIfCondition(ExprNode expr) {
return new IfCondition(expr);
}
static Condition createSwitchCondition(ExprNode switchExpr, List<ExprRootNode> caseExprs) {
// cast ImmutableList<ExprRootNode> to List<ExprNode>
@SuppressWarnings("unchecked")
List<ExprNode> list = (List<ExprNode>) ((List<?>) caseExprs);
return new SwitchCondition(switchExpr, list);
}
/** A placeholder for an empty condition. */
private static final class EmptyCondition extends Condition {
private static final EmptyCondition INSTANCE = new EmptyCondition();
private EmptyCondition() {}
@Override
EmptyCondition copy() {
return INSTANCE;
}
@Override
public String toString() {
return "EMPTY_CONDITION";
}
@Override
boolean isDefaultCond() {
return true;
}
}
/** A condition for an {@code IfCondNode} or an {@code IfElseNode}. */
private static final class IfCondition extends Condition {
/**
* An optional {@code ExprNode} for {@code IfCondNode} or {@code IfElseNode}. This should only
* be absent if it is for an {@code IfElseNode}.
*/
private final Optional<ExprNode> expr;
IfCondition() {
this.expr = Optional.absent();
}
IfCondition(ExprNode expr) {
this.expr = Optional.of(expr);
}
/** Copy constructor. */
IfCondition(IfCondition condition) {
if (condition.expr.isPresent()) {
this.expr = Optional.of(condition.expr.get().copy(new CopyState()));
} else {
this.expr = Optional.absent();
}
}
@Override
boolean isDefaultCond() {
return !expr.isPresent();
}
@Override
Condition copy() {
return new IfCondition(this);
}
@Override
public String toString() {
return "IF_CONDITION: " + this.expr;
}
@Override
public boolean equals(Object object) {
if (this == object) {
return true;
}
if (object instanceof IfCondition) {
IfCondition cond = (IfCondition) object;
return ExprEquivalence.get().equivalent(expr.orNull(), cond.expr.orNull());
}
return false;
}
@Override
public int hashCode() {
return ExprEquivalence.get().hash(expr.orNull());
}
}
/**
* A {@code Condition} for children of {@code SwitchNode}. It is a union of an expression of
* {@code SwitchNode} and a list of expressions for the current child.
*
* <p>TODO(user): make this class comparable so that we can sort the switch conditions.
*/
private static final class SwitchCondition extends Condition {
/** A expression for the parent {@code SwitchNode}. */
private final ExprNode switchExpr;
/**
* A list of {@code ExprNode} for a {@code SwitchCaseNode} or a {@code SwitchDefaultNode}. An
* empty list of caseExprs implies that this condition is for a SwitchDefaultNode.
*/
private final ImmutableList<ExprNode> caseExprs;
SwitchCondition(ExprNode switchExpr, List<ExprNode> caseExprs) {
this.switchExpr = switchExpr;
this.caseExprs = ImmutableList.copyOf(caseExprs);
}
@Override
boolean isDefaultCond() {
return caseExprs.isEmpty();
}
@Override
Condition copy() {
return new SwitchCondition(switchExpr, caseExprs);
}
@Override
public String toString() {
return "SWITCH_CONDITION: " + this.switchExpr + ", CASE: " + this.caseExprs;
}
@Override
public boolean equals(Object object) {
if (this == object) {
return true;
}
if (object instanceof SwitchCondition) {
SwitchCondition cond = (SwitchCondition) object;
return ExprEquivalence.get().equivalent(switchExpr, cond.switchExpr)
&& ExprEquivalence.get().pairwise().equivalent(caseExprs, cond.caseExprs);
}
return false;
}
@Override
public int hashCode() {
return 31 * ExprEquivalence.get().hash(switchExpr)
+ ExprEquivalence.get().pairwise().hash(caseExprs);
}
}
}