CommonCompoundLiteralExpression.java

package dev.civl.mc.model.common.expression;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;

import dev.civl.mc.model.IF.CIVLSource;
import dev.civl.mc.model.IF.Scope;
import dev.civl.mc.model.IF.expression.CompoundLiteralExpression;
import dev.civl.mc.model.IF.expression.Expression;
import dev.civl.mc.model.IF.type.CIVLType;
import dev.civl.mc.model.IF.variable.Variable;
import dev.civl.sarl.IF.expr.SymbolicExpression;

public class CommonCompoundLiteralExpression extends CommonExpression
		implements
			CompoundLiteralExpression {

	private CIVLCompoundLiteralObject obj = null;
	
	final private boolean isStringLiteral;

	public CommonCompoundLiteralExpression(CIVLSource source, Scope hscope,
			Scope lscope, CIVLType type, boolean isStringLiteral) {
		super(source, hscope, lscope, type);
		this.isStringLiteral = isStringLiteral;
	}
	
	@Override
	public boolean isStringLiteral() {
		return isStringLiteral;
	}

	@Override
	public ExpressionKind expressionKind() {
		return ExpressionKind.COMPOUND_LITERAL;
	}

	@Override
	public CIVLType type() {
		return expressionType;
	}

	@Override
	public String toString() {
		assert (obj != null || this.constantValue != null);
		if (obj == null)
			return "(" + this.type() + ")" + this.constantValue();
		return this.obj.toString();
	}

	@Override
	public Set<Variable> variableAddressedOf(Scope scope) {
		Set<Variable> result = new HashSet<>();

		if (obj == null)
			return result;
		for (Expression elt : obj.subExpressions()) {
			Set<Variable> vars = elt.variableAddressedOf(scope);

			if (vars != null)
				result.addAll(vars);
		}
		return result;
	}

	@Override
	public Set<Variable> variableAddressedOf() {
		Set<Variable> result = new HashSet<>();

		if (obj == null)
			return result;
		for (Expression elt : obj.subExpressions()) {
			Set<Variable> vars = elt.variableAddressedOf();

			if (vars != null)
				result.addAll(vars);
		}
		return result;
	}

	@Override
	public LiteralKind literalKind() {
		return LiteralKind.COMPOUND;
	}

	@Override
	protected boolean expressionEquals(Expression expression) {
		assert (obj != null || this.constantValue != null);

		if (expression instanceof CommonCompoundLiteralExpression) {
			CommonCompoundLiteralExpression that = (CommonCompoundLiteralExpression) expression;

			if (obj == null)
				return that.constantValue != null
						&& this.constantValue == that.constantValue;
			if (that.obj == null)
				return false;
			if (obj.size() != that.obj.size() || !type().equals(that.type()))
				return false;

			Iterator<CIVLLiteralObject> thisI = this.getLiteralObject()
					.iterator();
			Iterator<CIVLLiteralObject> thatI = that.getLiteralObject()
					.iterator();

			while (thisI.hasNext() && thatI.hasNext()) {
				CIVLLiteralObject thisE = thisI.next();
				CIVLLiteralObject thatE = thatI.next();

				if (!thisE.equals(thatE))
					return false;
			}
			return true;
		}
		return false;
	}

	@Override
	public void setLiteralConstantValue(SymbolicExpression value) {
		assert obj == null;
		this.constantValue = value;
	}

	@Override
	protected void addFreeVariables(Set<Variable> result) {
		if (obj == null)
			return;
		for (Expression elt : obj.subExpressions())
			result.addAll(elt.freeVariables());
	}

	@Override
	public void setLiteralObject(CIVLCompoundLiteralObject obj) {
		assert constantValue == null;
		this.obj = obj;
	}

	@Override
	public CIVLCompoundLiteralObject getLiteralObject() {
		return this.obj;
	}

	@Override
	public CIVLLiteralObject createScalarLiteralObject(CIVLType type,
			Expression expr) {
		return new CommonCIVLScalarLiteralObject(type, expr);
	}

	@Override
	public CIVLCompoundLiteralObject createCompoundLiteralObject(CIVLType type,
			List<CIVLLiteralObject> elements) {
		return new CommonCIVLCompoundLiteralObject(type, elements);
	}

	class CommonCIVLScalarLiteralObject implements CIVLScalarLiteralObject {

		final private Expression expr;
		final private CIVLType type;

		CommonCIVLScalarLiteralObject(CIVLType type, Expression expr) {
			assert expr == null || expr.getExpressionType() == type;
			this.expr = expr;
			this.type = type;
		}

		@Override
		public CIVLType type() {
			return type;
		}

		@Override
		public Expression getExpression() {
			return expr;
		}

		@Override
		public String toString() {
			if (expr == null)
				return "(" + type + ") null";
			return expr.toString();
		}

		@Override
		public List<Expression> subExpressions() {
			if (expr != null)
				return Arrays.asList(expr);
			return Arrays.asList();
		}

		@Override
		public boolean equals(Object o) {
			if (o instanceof CIVLScalarLiteralObject) {
				if (expr == null)
					return ((CIVLScalarLiteralObject) o).getExpression() == null;

				Expression otherExpr = ((CIVLScalarLiteralObject) o).getExpression();

				return otherExpr != null && otherExpr.equals(expr);
			}
			return false;
		}
	}

	class CommonCIVLCompoundLiteralObject implements CIVLCompoundLiteralObject {
		private final CIVLType type;
		private final List<CIVLLiteralObject> elements;

		CommonCIVLCompoundLiteralObject(CIVLType type, List<CIVLLiteralObject> elements) {
			this.type = type;
			this.elements = new ArrayList<>(elements);
		}

		@Override
		public CIVLType type() {
			return type;
		}

		@Override
		public Iterator<CIVLLiteralObject> iterator() {
			return elements.iterator();
		}

		@Override
		public String toString() {
			StringBuffer SB = new StringBuffer();

			SB.append("(" + this.type() + "){");
			if (elements.size() <= 0) {
				SB.append("}");
				return SB.toString();
			}
			SB.append(elements.get(0));

			final int size = elements.size();

			for (CIVLLiteralObject elt : elements.subList(1, size)) {
				SB.append(", " + elt);
			}
			SB.append("}");
			return SB.toString();
		}

		@Override
		public boolean equals(Object o) {
			if (o instanceof CommonCIVLCompoundLiteralObject) {
				CommonCIVLCompoundLiteralObject that = (CommonCIVLCompoundLiteralObject) o;

				if (that.size() != size())
					return false;
				if (!that.type.equals(this.type))
					return false;

				Iterator<CIVLLiteralObject> thisI = iterator();
				Iterator<CIVLLiteralObject> thatI = that.iterator();

				while (thisI.hasNext() && thatI.hasNext()) {
					CIVLLiteralObject thisE = thisI.next();
					CIVLLiteralObject thatE = thatI.next();

					if (!thisE.equals(thatE))
						return false;
				}
				return true;
			}
			return false;
		}

		@Override
		public List<Expression> subExpressions() {
			List<Expression> ll = new LinkedList<>();

			for (CIVLLiteralObject elt : elements)
				if (elt != null)
					ll.addAll(elt.subExpressions());
			return ll;
		}

		@Override
		public int size() {
			return elements.size();
		}
	}
}