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}