001/*
002 * Copyright (c) 2012-2018 Institut National des Sciences Appliquées de Lyon (INSA Lyon) and others
003 *
004 * This program and the accompanying materials are made available under the
005 * terms of the Eclipse Public License 2.0 which is available at
006 * http://www.eclipse.org/legal/epl-2.0.
007 *
008 * SPDX-License-Identifier: EPL-2.0
009 */
010
011package gololang.ir;
012
013import java.util.Arrays;
014import java.util.List;
015
016/**
017 * Represents a binary operation.
018 *
019 * @see OperatorType
020 */
021public final class BinaryOperation extends ExpressionStatement<BinaryOperation> {
022
023  private final OperatorType type;
024  private ExpressionStatement<?> leftExpression;
025  private ExpressionStatement<?> rightExpression;
026
027  private BinaryOperation(OperatorType type) {
028    super();
029    this.type = type;
030  }
031
032  /**
033   * Create a binary operation.
034   *
035   * @param type a {@link OperatorType} or a {@code String} representing the operator.
036   */
037  public static BinaryOperation of(Object type) {
038    return new BinaryOperation(OperatorType.of(type));
039  }
040
041  /**
042   * Full generic binary operation creation in one call.
043   *
044   * <p>Less readable than the fluent API, but useful when doing meta-generation
045   *
046   * @param type the type of the operation ({@link BinaryOperation#of(Object)})
047   * @param left the left expression.
048   * @param right the right expression.
049   * @return a configured binary operation.
050   */
051  public static BinaryOperation create(Object type, Object left, Object right) {
052    return of(type).left(left).right(right);
053  }
054
055  protected BinaryOperation self() { return this; }
056
057  public OperatorType getType() {
058    return type;
059  }
060
061  public ExpressionStatement<?> left() {
062    return leftExpression;
063  }
064
065  /**
066   * Define the left expression of this operation.
067   *
068   * <p>This is a builder method.
069   *
070   * @param expr the {@link gololang.ir.ExpressionStatement} to use as the left
071   * operand
072   * @return this operation
073   */
074  public BinaryOperation left(Object expr) {
075    this.leftExpression = makeParentOf(ExpressionStatement.of(expr));
076    return this;
077  }
078
079  /**
080   * Define the right expression of this operation.
081   *
082   * <p>This is a builder method.
083   *
084   * @param expr the {@link gololang.ir.ExpressionStatement} to use as the
085   * right operand
086   * @return this operation
087   */
088  public BinaryOperation right(Object expr) {
089    this.rightExpression = makeParentOf(ExpressionStatement.of(expr));
090    if (this.type == OperatorType.ELVIS_METHOD_CALL && this.rightExpression instanceof MethodInvocation) {
091      ((MethodInvocation) this.rightExpression).nullSafe(true);
092    }
093    return this;
094  }
095
096  public ExpressionStatement<?> right() {
097    return rightExpression;
098  }
099
100  @Override
101  public String toString() {
102    return String.format("%s %s %s", leftExpression, type, rightExpression);
103  }
104
105  public boolean isMethodCall() {
106    return this.getType() == OperatorType.METHOD_CALL
107      || this.getType() == OperatorType.ELVIS_METHOD_CALL
108      || this.getType() == OperatorType.ANON_CALL;
109  }
110
111  /**
112   * {@inheritDoc}
113   */
114  @Override
115  public void accept(GoloIrVisitor visitor) {
116    visitor.visitBinaryOperation(this);
117  }
118
119  /**
120   * {@inheritDoc}
121   */
122  @Override
123  public List<GoloElement<?>> children() {
124    return Arrays.asList(leftExpression, rightExpression);
125  }
126
127  /**
128   * {@inheritDoc}
129   */
130  @Override
131  protected void replaceElement(GoloElement<?> original, GoloElement<?> newElement) {
132    if (!(newElement instanceof ExpressionStatement)) {
133      throw cantConvert("ExpressionStatement", newElement);
134    }
135    if (leftExpression.equals(original)) {
136      left(newElement);
137    } else if (rightExpression.equals(original)) {
138      right(newElement);
139    } else {
140      throw cantReplace(original, newElement);
141    }
142  }
143}