blob: 165b26471d5411398eb8d3e4d1a1ac7edb0f1e27 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2020 Willink Transformations Ltd and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v20.html
*
* Contributors:
* E.D.Willink - Initial API and implementation
*******************************************************************************/
package org.eclipse.ocl.examples.codegen.java.operation;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.ocl.examples.codegen.cgmodel.CGLibraryOperationCallExp;
import org.eclipse.ocl.examples.codegen.cgmodel.CGValuedElement;
import org.eclipse.ocl.examples.codegen.java.JavaStream;
import org.eclipse.ocl.pivot.utilities.ValueUtil;
import org.eclipse.ocl.pivot.values.InvalidValueException;
/**
* AbstractLibraryOperationHandler provides the mandatory implementation of the LibraryOperationHandler
* API for a custom code generator for a library operation. It also provides many helper methods for generating
* code for 2-valued / 4-valued Boolean sub-expressions.
*/
public abstract class AbstractLibraryOperationHandler implements LibraryOperationHandler
{
protected final @NonNull JavaStream js;
protected AbstractLibraryOperationHandler(@NonNull JavaStream js) {
this.js = js;
}
protected void appendAssignBooleanLiteral(boolean hasDeclaration, @NonNull CGLibraryOperationCallExp cgOperationCallExp, boolean value) {
if (!hasDeclaration) {
js.appendDeclaration(cgOperationCallExp);
}
else {
js.appendValueName(cgOperationCallExp);
}
js.append(" = ");
if (cgOperationCallExp.isNonNull()) {
js.appendBooleanString(value);
}
else {
js.appendClassReference(null, ValueUtil.class);
js.append(".");
js.append(value ? "TRUE_VALUE" : "FALSE_VALUE");
}
js.append(";\n");
}
/* protected void appendAssignNotValue(boolean hasDeclaration, @NonNull CGLibraryOperationCallExp cgOperationCallExp, @NonNull CGValuedElement value) {
if (!cgOperationCallExp.isNonInvalid()) {
appendThrowIfMayBeInvalid(value);
}
if (!hasDeclaration) {
js.appendDeclaration(cgOperationCallExp);
}
else {
js.appendValueName(cgOperationCallExp);
}
js.append(" = ");
if (value.isNull()) {
js.append("null");
}
else if (value.isNonNull() && value.isNonInvalid()) {
js.append("!");
js.appendValueName(value);
}
else {
js.appendValueName(value);
js.append(" instanceof ");
js.appendClassReference(null, Boolean.class);
js.append(" ? !(");
js.appendClassReference(null, Boolean.class);
js.append(")");
js.appendValueName(value);
js.append(" : null "); // true/false cast to Boolean, invalid already thrown
// js.appendValueName(value);
}
js.append(";\n");
} */
protected void appendAssignNullLiteral(boolean hasDeclaration, @NonNull CGLibraryOperationCallExp cgOperationCallExp) {
assert !cgOperationCallExp.isNonNull();
if (!hasDeclaration) {
js.appendDeclaration(cgOperationCallExp);
}
else {
js.appendValueName(cgOperationCallExp);
}
js.append(" = null;\n");
}
protected void appendAssignValue(boolean hasDeclaration, @NonNull CGLibraryOperationCallExp cgOperationCallExp, @NonNull CGValuedElement value) {
if (!cgOperationCallExp.isNonInvalid()) {
appendThrowIfMayBeInvalid(value);
}
if (!hasDeclaration) {
js.appendDeclaration(cgOperationCallExp);
}
else {
js.appendValueName(cgOperationCallExp);
}
js.append(" = ");
if (value.isNull()) {
js.append("null");
}
else if (value.isNonNull() && value.isNonInvalid()) {
js.appendValueName(value);
}
else {
js.append("(");
js.appendClassReference(null, Boolean.class);
js.append(")");
js.appendValueName(value);
}
js.append(";\n");
}
protected void appendAssignXor(boolean hasDeclaration, @NonNull CGLibraryOperationCallExp cgOperationCallExp,
@NonNull CGValuedElement cgSource, @NonNull CGValuedElement cgArgument) {
if (!hasDeclaration) {
js.appendDeclaration(cgOperationCallExp);
}
else {
js.appendValueName(cgOperationCallExp);
}
js.append(" = ");
if (cgSource.isFalse() && cgArgument.isFalse()) {
js.appendBooleanString(false);
}
else if (cgSource.isFalse() && cgArgument.isTrue()) {
js.appendBooleanString(true);
}
else if (cgSource.isTrue() && cgArgument.isFalse()) {
js.appendBooleanString(true);
}
else if (cgSource.isTrue() && cgArgument.isTrue()) {
js.appendBooleanString(false);
}
else {
js.appendValueName(cgSource);
js.append(" != ");
js.appendValueName(cgArgument);
}
js.append(";\n");
}
protected boolean appendDeclaration(boolean hasDeclaration, @NonNull CGLibraryOperationCallExp cgOperationCallExp) {
if (!hasDeclaration) {
js.appendDeclaration(cgOperationCallExp);
js.append(";\n");
hasDeclaration = true;
}
return hasDeclaration;
}
protected void appendElse() {
js.popIndentation();
js.append("}\n");
js.append("else {\n");
js.pushIndentation(null);
}
protected void appendEndIf() {
js.popIndentation();
js.append("}\n");
}
private void appendEqualsBoolean(@NonNull CGValuedElement cgValue, boolean value) {
if (cgValue.isNonNull() && cgValue.isNonInvalid()) {
if (!value) {
js.append("!");
}
js.appendValueName(cgValue);
}
else {
js.appendValueName(cgValue);
js.append(" == ");
js.appendClassReference(null, ValueUtil.class);
js.append(".");
js.append(value ? "TRUE_VALUE" : "FALSE_VALUE");
}
}
/**
* Append an if cgSource -- value prolog.
*
* Append the start of an if test that cgValue matches refValue.
*
* Asserts that the if test is necessary.
*/
protected void appendIfEqualsBoolean0(@NonNull CGValuedElement cgValue, boolean refValue) {
assert !cgValue.isConstant();
assert canMatch(cgValue, refValue);
js.append("if (");
appendEqualsBoolean(cgValue, refValue);
js.append(") {\n");
js.pushIndentation(null);
}
/**
* Return true if the start of an if test that cgValue matches refValue has been generated.
* Return false if no test is needed since neither can match.
*/
protected boolean appendIfEqualsBoolean1(@NonNull CGValuedElement cgValue, boolean refValue) {
boolean canMatch = canMatch(cgValue, refValue);
if (!canMatch) {
return false;
}
js.append("if (");
appendEqualsBoolean(cgValue, refValue);
js.append(") {\n");
js.pushIndentation(null);
return true;
}
/**
* Append an if cgValue == null prolog.
*/
protected void appendIfEqualsNull(@NonNull CGValuedElement cgValue) {
assert !cgValue.isConstant();
js.append("if (");
js.appendValueName(cgValue);
js.append(" == null) {\n");
js.pushIndentation(null);
}
/**
* Append an if cgValue1 == null or cgValue2 == null prolog.
*/
protected void appendIfEqualsNull(@NonNull CGValuedElement cgValue1, @NonNull CGValuedElement cgValue2) {
assert !cgValue1.isConstant();
assert !cgValue2.isConstant();
js.append("if ((");
js.appendValueName(cgValue1);
js.append(" == null) || (");
js.appendValueName(cgValue2);
js.append(" == null)) {\n");
js.pushIndentation(null);
}
/**
* Append a mandatory throw if cgElement is unconditionally invalid.
*
* Returns true if throw appended.
*/
protected boolean appendThrowIfInvalid(final CGValuedElement cgElement, @NonNull String context) {
if (cgElement.isInvalid()) {
js.append("throw new ");
js.appendClassReference(null, InvalidValueException.class);
js.append("(\"Null " + context + "\");\n");
return true;
}
return false;
}
/**
* Append a conditionsal throw if cgElement may be invalid.
*/
protected void appendThrowIfMayBeInvalid(final CGValuedElement cgElement) {
if (!cgElement.isNonInvalid()) {
js.append("if (");
js.appendValueName(cgElement);
js.append(" instanceof ");
js.appendClassReference(null, InvalidValueException.class);
js.append(") {\n");
js.pushIndentation(null);
js.append("throw (");
js.appendClassReference(null, InvalidValueException.class);
js.append(")");
js.appendValueName(cgElement);
js.append(";\n");
js.popIndentation();
js.append("}\n");
}
}
/**
* Append a conditionsal throw if cgElement may be null.
*/
protected void appendThrowIfMayBeNull(final CGValuedElement cgElement, @NonNull String context) {
if (!cgElement.isNonNull()) {
js.append("if (");
js.appendValueName(cgElement);
js.append(" == null) {\n");
js.pushIndentation(null);
js.append("throw new ");
js.appendClassReference(null, InvalidValueException.class);
js.append("(\"Null " + context + "\");\n");
js.popIndentation();
js.append("}\n");
}
}
/**
* Append a mandatory throw if cgElement is unconditionally null.
*
* Returns true of throw appended.
*/
protected boolean appendThrowIfNull(final CGValuedElement cgElement, @NonNull String context) {
if (cgElement.isNull()) {
js.append("throw new ");
js.appendClassReference(null, InvalidValueException.class);
js.append("(\"Null " + context + "\");\n");
return true;
}
return false;
}
private boolean canMatch(@NonNull CGValuedElement cgValue, boolean value) {
if (value) assert !cgValue.isTrue(); else assert !cgValue.isFalse();
if (cgValue.isNull() || cgValue.isInvalid()) {
return false;
}
else if (value) {
return cgValue.isFalse() ? false : true;
}
else {
return cgValue.isTrue() ? false : true;
}
}
@Override
public @NonNull String toString() {
return js.toString();
}
}