[433103] Support CG of short-circuit Boolean operators
diff --git a/examples/org.eclipse.ocl.examples.codegen/src/org/eclipse/ocl/examples/codegen/analyzer/AS2CGVisitor.java b/examples/org.eclipse.ocl.examples.codegen/src/org/eclipse/ocl/examples/codegen/analyzer/AS2CGVisitor.java
index 7d06b1f..2382954 100644
--- a/examples/org.eclipse.ocl.examples.codegen/src/org/eclipse/ocl/examples/codegen/analyzer/AS2CGVisitor.java
+++ b/examples/org.eclipse.ocl.examples.codegen/src/org/eclipse/ocl/examples/codegen/analyzer/AS2CGVisitor.java
@@ -174,12 +174,6 @@
 import org.eclipse.ocl.pivot.library.LibraryOperation;
 import org.eclipse.ocl.pivot.library.LibraryProperty;
 import org.eclipse.ocl.pivot.library.collection.CollectionExcludingOperation;
-import org.eclipse.ocl.pivot.library.logical.BooleanAndOperation;
-import org.eclipse.ocl.pivot.library.logical.BooleanAndOperation2;
-import org.eclipse.ocl.pivot.library.logical.BooleanImpliesOperation;
-import org.eclipse.ocl.pivot.library.logical.BooleanImpliesOperation2;
-import org.eclipse.ocl.pivot.library.logical.BooleanOrOperation;
-import org.eclipse.ocl.pivot.library.logical.BooleanOrOperation2;
 import org.eclipse.ocl.pivot.library.oclany.OclAnyEqualOperation;
 import org.eclipse.ocl.pivot.library.oclany.OclAnyNotEqualOperation;
 import org.eclipse.ocl.pivot.library.oclany.OclAnyOclIsInvalidOperation;
@@ -701,56 +695,6 @@
 			cgIsEqualExp.setValidating(true);
 			return cgIsEqualExp;
 		}
-		else if ((libraryOperation instanceof BooleanAndOperation2)
-				|| ((libraryOperation instanceof BooleanAndOperation) && (cgSource != null) && cgSource.isNonNull() && cgSource.isNonInvalid())) {
-			OCLExpression pArgument = PivotUtil.getOwnedArgument(element, 0);
-			CGIfExp cgIfExp = CGModelFactory.eINSTANCE.createCGIfExp();
-			setAst(cgIfExp, element);
-			CGValuedElement cgCondition = doVisit(CGValuedElement.class, pSource);
-			CGValuedElement cgThenExpression = doVisit(CGValuedElement.class, pArgument);
-			CGValuedElement cgElseExpression = context.createCGConstantExp(context.getBoolean(false));
-			cgIfExp.setCondition(cgCondition);
-			cgIfExp.setThenExpression(cgThenExpression);
-			cgIfExp.setElseExpression(cgElseExpression);
-			return cgIfExp;
-		}
-		else if ((libraryOperation instanceof BooleanImpliesOperation2)
-				|| ((libraryOperation instanceof BooleanImpliesOperation) && (cgSource != null) && cgSource.isNonNull() && cgSource.isNonInvalid())) {
-			OCLExpression pArgument = PivotUtil.getOwnedArgument(element, 0);
-			CGIfExp cgIfExp = CGModelFactory.eINSTANCE.createCGIfExp();
-			setAst(cgIfExp, element);
-			CGValuedElement cgCondition = doVisit(CGValuedElement.class, pSource);
-			CGValuedElement cgThenExpression = doVisit(CGValuedElement.class, pArgument);
-			CGValuedElement cgElseExpression = context.createCGConstantExp(context.getBoolean(true));
-			cgIfExp.setCondition(cgCondition);
-			cgIfExp.setThenExpression(cgThenExpression);
-			cgIfExp.setElseExpression(cgElseExpression);
-			return cgIfExp;
-		}
-		else if ((libraryOperation instanceof BooleanOrOperation2)
-				|| ((libraryOperation instanceof BooleanOrOperation) && (cgSource != null) && cgSource.isNonNull() && cgSource.isNonInvalid())) {
-			OCLExpression pArgument = PivotUtil.getOwnedArgument(element, 0);
-			CGIfExp cgIfExp = CGModelFactory.eINSTANCE.createCGIfExp();
-			setAst(cgIfExp, element);
-			CGValuedElement cgCondition = doVisit(CGValuedElement.class, pSource);
-			CGValuedElement cgThenExpression = context.createCGConstantExp(context.getBoolean(true));
-			CGValuedElement cgElseExpression = doVisit(CGValuedElement.class, pArgument);
-			cgIfExp.setCondition(cgCondition);
-			cgIfExp.setThenExpression(cgThenExpression);
-			cgIfExp.setElseExpression(cgElseExpression);
-			return cgIfExp;
-		}
-		/*		else if (libraryOperation instanceof OclAnyEqual2Operation) {
-			OCLExpression pArgument = element.getOwnedArguments().get(0);
-			CGValuedElement cgArgument = pArgument != null ? doVisit(CGValuedElement.class, pArgument) : null;
-			CGIsEqual2Exp cgIsEqualExp = CGModelFactory.eINSTANCE.createCGIsEqual2Exp();
-			cgIsEqualExp.setSource(cgSource);
-			cgIsEqualExp.setArgument(cgArgument);
-			setAst(cgIsEqualExp, element);
-			cgIsEqualExp.setInvalidating(false);
-			cgIsEqualExp.setValidating(true);
-			return cgIsEqualExp;
-		} */
 		else if (libraryOperation instanceof NativeStaticOperation) {
 			LanguageExpression bodyExpression = asOperation.getBodyExpression();
 			if (bodyExpression != null) {
diff --git a/examples/org.eclipse.ocl.examples.codegen/src/org/eclipse/ocl/examples/codegen/analyzer/BoxingAnalyzer.java b/examples/org.eclipse.ocl.examples.codegen/src/org/eclipse/ocl/examples/codegen/analyzer/BoxingAnalyzer.java
index e59246e..b1824b1 100644
--- a/examples/org.eclipse.ocl.examples.codegen/src/org/eclipse/ocl/examples/codegen/analyzer/BoxingAnalyzer.java
+++ b/examples/org.eclipse.ocl.examples.codegen/src/org/eclipse/ocl/examples/codegen/analyzer/BoxingAnalyzer.java
@@ -24,6 +24,7 @@
 import org.eclipse.ocl.examples.codegen.cgmodel.CGCachedOperationCallExp;
 import org.eclipse.ocl.examples.codegen.cgmodel.CGCallExp;
 import org.eclipse.ocl.examples.codegen.cgmodel.CGCastExp;
+import org.eclipse.ocl.examples.codegen.cgmodel.CGConstantExp;
 import org.eclipse.ocl.examples.codegen.cgmodel.CGEcoreExp;
 import org.eclipse.ocl.examples.codegen.cgmodel.CGEcoreOperation;
 import org.eclipse.ocl.examples.codegen.cgmodel.CGEcoreOperationCallExp;
@@ -35,6 +36,7 @@
 import org.eclipse.ocl.examples.codegen.cgmodel.CGExecutorType;
 import org.eclipse.ocl.examples.codegen.cgmodel.CGGuardExp;
 import org.eclipse.ocl.examples.codegen.cgmodel.CGIfExp;
+import org.eclipse.ocl.examples.codegen.cgmodel.CGInvalid;
 import org.eclipse.ocl.examples.codegen.cgmodel.CGIsEqual2Exp;
 import org.eclipse.ocl.examples.codegen.cgmodel.CGIsEqualExp;
 import org.eclipse.ocl.examples.codegen.cgmodel.CGIterationCallExp;
@@ -67,6 +69,7 @@
 import org.eclipse.ocl.pivot.CompleteClass;
 import org.eclipse.ocl.pivot.Element;
 import org.eclipse.ocl.pivot.Operation;
+import org.eclipse.ocl.pivot.Parameter;
 import org.eclipse.ocl.pivot.Property;
 import org.eclipse.ocl.pivot.TupleType;
 import org.eclipse.ocl.pivot.Type;
@@ -481,17 +484,48 @@
 	@Override
 	public @Nullable Object visitCGLibraryOperationCallExp(@NonNull CGLibraryOperationCallExp cgElement) {
 		super.visitCGLibraryOperationCallExp(cgElement);
-		//		if (!cgElement.getReferredOperation().isValidating()) {
-		//			OperationId operationId = cgElement.getReferredOperation().getOperationId();
-		//			if (!hasOclVoidOperation(operationId)) {
-		//				guard(cgElement, cgElement.getSource());
-		//			}
-		//		}
-		rewriteAsBoxed(cgElement.getSource());
+		Operation referredOperation = cgElement.getReferredOperation();
+		OperationId operationId = referredOperation.getOperationId();
+		boolean sourceMayBeNull = hasOclVoidOperation(operationId);
+		CGValuedElement cgSource = cgElement.getSource();
+		if (!sourceMayBeNull) {
+			if (cgSource.isNull()) {
+//				CGInvalid cgInvalid = context.getInvalid("null value1 for source parameter");
+				CGInvalid cgInvalid = context.getInvalid("''" + referredOperation.getOwningClass().getName() + "'' rather than ''OclVoid'' value required");
+				CGConstantExp cgLiteralExp = context.createCGConstantExp(CGUtil.getAST(cgElement), cgInvalid);
+				CGUtil.replace(cgElement, cgLiteralExp);
+				return null;
+			}
+		}
+		List<Parameter> ownedParameters = referredOperation.getOwnedParameters();
 		List<CGValuedElement> cgArguments = cgElement.getArguments();
 		int iMax = cgArguments.size();
 		for (int i = 0; i < iMax; i++) {			// Avoid CME from rewrite
-			rewriteAsBoxed(cgArguments.get(i));
+			CGValuedElement cgArgument = cgArguments.get(i);
+			Parameter asParameter = ownedParameters.get(i);
+			if (asParameter.isIsRequired()) {
+				if (cgArgument.isNull()) {
+//					CGInvalid cgInvalid = context.getInvalid("null value2 for " + asParameter.getName() + " parameter");
+					CGInvalid cgInvalid = context.getInvalid("''" + asParameter.getType().getName() + "'' rather than ''OclVoid'' value required");
+					CGConstantExp cgLiteralExp = context.createCGConstantExp(CGUtil.getAST(cgElement), cgInvalid);
+					CGUtil.replace(cgElement, cgLiteralExp);
+					return null;
+				}
+			}
+		}
+		rewriteAsBoxed(cgSource);
+		if (!sourceMayBeNull && !cgSource.isNonNull()) {
+//			rewriteAsGuarded(cgSource, false, "value3 for source parameter");
+			rewriteAsGuarded(cgSource, false, "''" + referredOperation.getOwningClass().getName() + "'' rather than ''OclVoid'' value required");
+		}
+		for (int i = 0; i < iMax; i++) {			// Avoid CME from rewrite
+			CGValuedElement cgArgument = cgArguments.get(i);
+			rewriteAsBoxed(cgArgument);
+			Parameter asParameter = ownedParameters.get(i);
+			if (asParameter.isIsRequired() && !cgArgument.isNonNull()) {
+//				rewriteAsGuarded(cgArgument, false, "value4 for " + asParameter.getName() + " parameter");
+				rewriteAsGuarded(cgArgument, false, "''" + asParameter.getType().getName() + "'' rather than ''OclVoid'' value required");
+			}
 		}
 		return null;
 	}
diff --git a/examples/org.eclipse.ocl.examples.codegen/src/org/eclipse/ocl/examples/codegen/java/CG2JavaVisitor.java b/examples/org.eclipse.ocl.examples.codegen/src/org/eclipse/ocl/examples/codegen/java/CG2JavaVisitor.java
index 208bb00..6070e2a 100644
--- a/examples/org.eclipse.ocl.examples.codegen/src/org/eclipse/ocl/examples/codegen/java/CG2JavaVisitor.java
+++ b/examples/org.eclipse.ocl.examples.codegen/src/org/eclipse/ocl/examples/codegen/java/CG2JavaVisitor.java
@@ -13,8 +13,10 @@
 import java.lang.reflect.Method;
 import java.math.BigDecimal;
 import java.math.BigInteger;
+import java.util.HashMap;
 import java.util.Iterator;
 import java.util.List;
+import java.util.Map;
 import java.util.Set;
 
 import org.eclipse.emf.codegen.ecore.genmodel.GenPackage;
@@ -104,6 +106,17 @@
 import org.eclipse.ocl.examples.codegen.generator.GenModelHelper;
 import org.eclipse.ocl.examples.codegen.generator.TypeDescriptor;
 import org.eclipse.ocl.examples.codegen.java.JavaStream.SubStream;
+import org.eclipse.ocl.examples.codegen.java.operation.AndOperation2Handler;
+import org.eclipse.ocl.examples.codegen.java.operation.AndOperationHandler;
+import org.eclipse.ocl.examples.codegen.java.operation.ImpliesOperation2Handler;
+import org.eclipse.ocl.examples.codegen.java.operation.ImpliesOperationHandler;
+import org.eclipse.ocl.examples.codegen.java.operation.LibraryOperationHandler;
+import org.eclipse.ocl.examples.codegen.java.operation.NotOperation2Handler;
+import org.eclipse.ocl.examples.codegen.java.operation.NotOperationHandler;
+import org.eclipse.ocl.examples.codegen.java.operation.OrOperation2Handler;
+import org.eclipse.ocl.examples.codegen.java.operation.OrOperationHandler;
+import org.eclipse.ocl.examples.codegen.java.operation.XorOperation2Handler;
+import org.eclipse.ocl.examples.codegen.java.operation.XorOperationHandler;
 import org.eclipse.ocl.examples.codegen.utilities.CGUtil;
 import org.eclipse.ocl.pivot.CollectionLiteralExp;
 import org.eclipse.ocl.pivot.Element;
@@ -190,6 +203,7 @@
 	protected final @NonNull EnvironmentFactoryInternal environmentFactory;
 	protected final @NonNull Id2JavaInterfaceVisitor id2JavaInterfaceVisitor;
 	protected final @NonNull JavaStream js;
+	protected final @NonNull Map<@NonNull Class<? extends LibraryOperation>, @NonNull LibraryOperationHandler> libraryOperation2handler = new HashMap<>();;
 
 	/**
 	 * The local Java context for the current operation.
@@ -204,6 +218,20 @@
 		this.id2JavaInterfaceVisitor = createId2JavaClassVisitor();
 		this.environmentFactory = analyzer.getCodeGenerator().getEnvironmentFactory();
 		this.js = codeGenerator.createJavaStream(this);
+		installLibraryHandler(new AndOperationHandler(js));
+		installLibraryHandler(new AndOperation2Handler(js));
+		installLibraryHandler(new ImpliesOperationHandler(js));
+		installLibraryHandler(new ImpliesOperation2Handler(js));
+		installLibraryHandler(new NotOperationHandler(js));
+		installLibraryHandler(new NotOperation2Handler(js));
+		installLibraryHandler(new OrOperationHandler(js));
+		installLibraryHandler(new OrOperation2Handler(js));
+		installLibraryHandler(new XorOperationHandler(js));
+		installLibraryHandler(new XorOperation2Handler(js));
+	}
+
+	protected void installLibraryHandler(@NonNull LibraryOperationHandler libraryOperationHandler) {
+		libraryOperation2handler.put(libraryOperationHandler.getLibraryOperationClass(), libraryOperationHandler);
 	}
 
 	protected @NonNull String addImport(@Nullable Boolean isRequired, @NonNull String className) {
@@ -786,7 +814,7 @@
 		return OCLstdlibPackage.Literals.OCL_ELEMENT__OCL_CONTAINER;
 	}
 
-	protected @NonNull CGValuedElement getExpression(@Nullable CGValuedElement cgExpression) {
+	public @NonNull CGValuedElement getExpression(@Nullable CGValuedElement cgExpression) {
 		return analyzer.getExpression(cgExpression);
 	}
 
@@ -2319,9 +2347,13 @@
 
 	@Override
 	public @NonNull Boolean visitCGLibraryOperationCallExp(@NonNull CGLibraryOperationCallExp cgOperationCallExp) {
+		final LibraryOperation libraryOperation = ClassUtil.nonNullState(cgOperationCallExp.getLibraryOperation());
+		LibraryOperationHandler libraryOperationHandler = libraryOperation2handler.get(libraryOperation.getClass());
+		if (libraryOperationHandler != null) {
+			return libraryOperationHandler.generate(cgOperationCallExp);
+		}
 		final CGValuedElement source = getExpression(cgOperationCallExp.getSource());
 		final List<CGValuedElement> arguments = cgOperationCallExp.getArguments();
-		final LibraryOperation libraryOperation = ClassUtil.nonNullState(cgOperationCallExp.getLibraryOperation());
 		Method actualMethod = getJavaMethod(libraryOperation, arguments.size());
 		Class<?> actualReturnClass = actualMethod != null ? actualMethod.getReturnType() : null;
 		boolean actualIsNonNull = (actualMethod != null) && (context.getIsNonNull(actualMethod) == Boolean.TRUE);
@@ -2335,6 +2367,52 @@
 				return false;
 			}
 		}
+		for (int i = 0; i < arguments.size(); i++) {
+			CGValuedElement cgArgument = arguments.get(i);
+			Parameter asParameter = cgOperationCallExp.getReferredOperation().getOwnedParameters().get(i);
+			if (asParameter.isIsRequired()) {
+				if (cgArgument.isNull()) {
+					js.append("throw new ");
+					js.appendClassReference(null, InvalidValueException.class);
+					js.append("(\"Null argument\");\n");
+					return false;
+				}
+				else if (cgArgument.isInvalid()) {
+					js.append("throw new ");
+					js.appendClassReference(null, InvalidValueException.class);
+					js.append("(\"Invalid argument\");\n");
+					return false;
+				}
+				else {
+					if (!cgArgument.isNonNull()) {
+						js.append("if (");
+						js.appendValueName(cgArgument);
+						js.append(" == null) {\n");
+						js.pushIndentation(null);
+						js.append("throw new ");
+						js.appendClassReference(null, InvalidValueException.class);
+						js.append("(\"Null argument\");\n");
+						js.popIndentation();
+						js.append("}\n");
+					}
+					if (!cgArgument.isNonInvalid()) {
+						js.append("if (");
+						js.appendValueName(cgArgument);
+						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(cgArgument);
+						js.append(";\n");
+						js.popIndentation();
+						js.append("}\n");
+					}
+				}
+			}
+		}
 		if (expectedIsNonNull && !actualIsNonNull) {
 			js.appendSuppressWarningsNull(true);
 		}
diff --git a/examples/org.eclipse.ocl.examples.codegen/src/org/eclipse/ocl/examples/codegen/java/operation/AbstractLibraryOperationHandler.java b/examples/org.eclipse.ocl.examples.codegen/src/org/eclipse/ocl/examples/codegen/java/operation/AbstractLibraryOperationHandler.java
new file mode 100644
index 0000000..165b264
--- /dev/null
+++ b/examples/org.eclipse.ocl.examples.codegen/src/org/eclipse/ocl/examples/codegen/java/operation/AbstractLibraryOperationHandler.java
@@ -0,0 +1,328 @@
+/*******************************************************************************
+ * 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();
+	}
+}
\ No newline at end of file
diff --git a/examples/org.eclipse.ocl.examples.codegen/src/org/eclipse/ocl/examples/codegen/java/operation/AndOperation2Handler.java b/examples/org.eclipse.ocl.examples.codegen/src/org/eclipse/ocl/examples/codegen/java/operation/AndOperation2Handler.java
new file mode 100644
index 0000000..e618a9f
--- /dev/null
+++ b/examples/org.eclipse.ocl.examples.codegen/src/org/eclipse/ocl/examples/codegen/java/operation/AndOperation2Handler.java
@@ -0,0 +1,88 @@
+/*******************************************************************************
+ * 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.library.LibraryOperation;
+import org.eclipse.ocl.pivot.library.logical.BooleanAndOperation2;
+
+/**
+ * AndOperation2Handler generates inline code for the and2 Boolean operation.
+ */
+public class AndOperation2Handler extends AbstractLibraryOperationHandler
+	{
+		public AndOperation2Handler( @NonNull JavaStream js) {
+			super(js);
+		}
+
+		@Override
+		public @NonNull Boolean generate(@NonNull CGLibraryOperationCallExp cgOperationCallExp) {
+			assert !cgOperationCallExp.getReferredOperation().isIsInvalidating();
+			assert !cgOperationCallExp.getReferredOperation().isIsValidating();
+			boolean hasDeclaration = false;
+			//
+			//	Trivial source cases
+			//
+			CGValuedElement cgSource = cgOperationCallExp.getSource();
+			assert cgSource.isNonInvalid();
+			assert cgSource.isNonNull();
+			if (cgSource.isFalse()) {
+				appendAssignBooleanLiteral(hasDeclaration, cgOperationCallExp, false);
+				return true;
+			}
+			//
+			//	Trivial argument cases
+			//
+			CGValuedElement cgArgument = cgOperationCallExp.getArguments().get(0);
+			assert cgArgument != null;
+			assert cgArgument.isNonInvalid();
+			assert cgArgument.isNonNull();
+			if (cgArgument.isFalse()) {
+				appendAssignBooleanLiteral(hasDeclaration, cgOperationCallExp, false);
+				return true;
+			}
+			//
+			//	Trivial source+argument case
+			//
+			if (cgSource.isTrue() && cgArgument.isTrue()) {
+				appendAssignBooleanLiteral(hasDeclaration, cgOperationCallExp, true);
+				return true;
+			}
+			//
+			//	Real case
+			//
+			if (!js.appendLocalStatements(cgSource)) {
+				return false;
+			}
+			hasDeclaration = appendDeclaration(hasDeclaration, cgOperationCallExp);
+			try {
+				appendIfEqualsBoolean0(cgSource, false);
+				appendAssignBooleanLiteral(hasDeclaration, cgOperationCallExp, false);
+				appendElse();
+				if (!js.appendLocalStatements(cgArgument)) {
+					return false;
+				}
+				appendAssignValue(hasDeclaration, cgOperationCallExp, cgArgument);
+				return true;
+			}
+			finally {
+				appendEndIf();
+			}
+		}
+
+		@Override
+		public@NonNull Class<? extends LibraryOperation> getLibraryOperationClass() {
+			return BooleanAndOperation2.class;
+		}
+	}
\ No newline at end of file
diff --git a/examples/org.eclipse.ocl.examples.codegen/src/org/eclipse/ocl/examples/codegen/java/operation/AndOperationHandler.java b/examples/org.eclipse.ocl.examples.codegen/src/org/eclipse/ocl/examples/codegen/java/operation/AndOperationHandler.java
new file mode 100644
index 0000000..6c11649
--- /dev/null
+++ b/examples/org.eclipse.ocl.examples.codegen/src/org/eclipse/ocl/examples/codegen/java/operation/AndOperationHandler.java
@@ -0,0 +1,144 @@
+/*******************************************************************************
+ * 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.library.LibraryOperation;
+import org.eclipse.ocl.pivot.library.logical.BooleanAndOperation;
+
+/**
+ * AndOperationHandler generates inline code for the and Boolean operation.
+ */
+public class AndOperationHandler extends AbstractLibraryOperationHandler
+	{
+		public AndOperationHandler( @NonNull JavaStream js) {
+			super(js);
+		}
+
+		@Override
+		public @NonNull Boolean generate(@NonNull CGLibraryOperationCallExp cgOperationCallExp) {
+			assert cgOperationCallExp.getReferredOperation().isIsValidating();
+			boolean hasDeclaration = false;
+			//
+			//	Short-circuit cases
+			//
+			final CGValuedElement cgSource = cgOperationCallExp.getSource();
+			CGValuedElement cgArgument = cgOperationCallExp.getArguments().get(0);
+			assert cgArgument != null;
+			if (cgSource.isFalse() || cgArgument.isFalse()) {
+				appendAssignBooleanLiteral(hasDeclaration, cgOperationCallExp, false);
+				return true;
+			}
+			//
+			//	Constant cases
+			//
+			if (cgSource.isConstant() && cgArgument.isConstant()) {
+				if (appendThrowIfInvalid(cgSource, "and source")) {
+					return false;
+				}
+				if (appendThrowIfInvalid(cgArgument, "and argument")) {
+					return false;
+				}
+				if (cgSource.isNull() || cgArgument.isNull()) {
+					appendAssignNullLiteral(hasDeclaration, cgOperationCallExp);
+					return true;
+				}
+				assert cgSource.isTrue() && cgArgument.isTrue();
+				appendAssignBooleanLiteral(hasDeclaration, cgOperationCallExp, true);
+				return true;
+			}
+			//
+			//	Real cases that require first term evaluation
+			//
+			if (!js.appendLocalStatements(cgSource)) {
+				return false;
+			}
+			hasDeclaration = appendDeclaration(hasDeclaration, cgOperationCallExp);
+			boolean mayBeFalse1 = appendIfEqualsBoolean1(cgSource, false);
+			if (mayBeFalse1) {
+				appendAssignBooleanLiteral(hasDeclaration, cgOperationCallExp, false);
+				appendElse();
+			}
+			try {
+				//
+				//	Real cases that require second term evaluation too
+				//
+				if (!js.appendLocalStatements(cgArgument)) {
+					return false;
+				}
+				if (cgSource.isTrue()) {
+					appendAssignValue(hasDeclaration, cgOperationCallExp, cgArgument);
+					return true;
+				}
+				if (cgArgument.isTrue()) {
+					appendAssignValue(hasDeclaration, cgOperationCallExp, cgSource);
+					return true;
+				}
+				boolean mayBeFalse2 = appendIfEqualsBoolean1(cgArgument, false);
+				if (mayBeFalse2) {
+					appendAssignBooleanLiteral(hasDeclaration, cgOperationCallExp, false);
+					appendElse();
+				}
+				if (!appendThrowIfInvalid(cgSource, "and source") && !appendThrowIfInvalid(cgSource, "and argument")) {
+					appendThrowIfMayBeInvalid(cgSource);
+					appendThrowIfMayBeInvalid(cgArgument);
+					if (cgSource.isNull() || cgArgument.isNull()) {
+						appendAssignNullLiteral(hasDeclaration, cgOperationCallExp);
+					}
+					else if (cgSource.isNonNull()){
+						if (cgArgument.isNonNull()){
+							appendAssignBooleanLiteral(hasDeclaration, cgOperationCallExp, true);
+						}
+						else {
+							appendIfEqualsNull(cgArgument);
+							appendAssignNullLiteral(hasDeclaration, cgOperationCallExp);
+							appendElse();
+							appendAssignBooleanLiteral(hasDeclaration, cgOperationCallExp, true);
+							appendEndIf();
+						}
+					}
+					else {
+						if (cgArgument.isNonNull()){
+							appendIfEqualsNull(cgSource);
+							appendAssignNullLiteral(hasDeclaration, cgOperationCallExp);
+							appendElse();
+							appendAssignBooleanLiteral(hasDeclaration, cgOperationCallExp, true);
+							appendEndIf();
+						}
+						else {
+							appendIfEqualsNull(cgSource, cgArgument);
+							appendAssignNullLiteral(hasDeclaration, cgOperationCallExp);
+							appendElse();
+							appendAssignBooleanLiteral(hasDeclaration, cgOperationCallExp, true);
+							appendEndIf();
+						}
+					}
+				}
+				if (mayBeFalse2) {
+					appendEndIf();
+				}
+			}
+			finally {
+				if (mayBeFalse1) {
+					appendEndIf();
+				}
+			}
+			return true;
+		}
+
+		@Override
+		public@NonNull Class<? extends LibraryOperation> getLibraryOperationClass() {
+			return BooleanAndOperation.class;
+		}
+	}
\ No newline at end of file
diff --git a/examples/org.eclipse.ocl.examples.codegen/src/org/eclipse/ocl/examples/codegen/java/operation/ImpliesOperation2Handler.java b/examples/org.eclipse.ocl.examples.codegen/src/org/eclipse/ocl/examples/codegen/java/operation/ImpliesOperation2Handler.java
new file mode 100644
index 0000000..c5b0230
--- /dev/null
+++ b/examples/org.eclipse.ocl.examples.codegen/src/org/eclipse/ocl/examples/codegen/java/operation/ImpliesOperation2Handler.java
@@ -0,0 +1,115 @@
+/*******************************************************************************
+ * 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.library.LibraryOperation;
+import org.eclipse.ocl.pivot.library.logical.BooleanImpliesOperation2;
+
+/**
+ * ImpliesOperation2Handler generates inline code for the implies2 Boolean operation.
+ */
+public class ImpliesOperation2Handler extends AbstractLibraryOperationHandler
+	{
+		public ImpliesOperation2Handler(@NonNull JavaStream js) {
+			super(js);
+		}
+
+		@Override
+		public @NonNull Boolean generate(@NonNull CGLibraryOperationCallExp cgOperationCallExp) {
+			assert !cgOperationCallExp.getReferredOperation().isIsInvalidating();
+			assert !cgOperationCallExp.getReferredOperation().isIsValidating();
+			boolean hasDeclaration = false;
+			//
+			//	Trivial source cases
+			//
+			final CGValuedElement cgSource = cgOperationCallExp.getSource();
+			if (appendThrowIfNull(cgSource, "implies2 source")) {
+				return false;
+			}
+			if (appendThrowIfInvalid(cgSource, "implies2 source")) {
+				return false;
+			}
+			if (cgSource.isFalse()) {
+				appendAssignBooleanLiteral(hasDeclaration, cgOperationCallExp, true);
+				return true;
+			}
+			//
+			//	Trivial argument cases
+			//
+			CGValuedElement cgArgument = cgOperationCallExp.getArguments().get(0);
+			assert cgArgument != null;
+			if (appendThrowIfNull(cgArgument, "implies2 argument")) {
+				return false;
+			}
+			if (appendThrowIfInvalid(cgArgument, "implies2 argument")) {
+				return false;
+			}
+			if (cgArgument.isTrue()) {
+				appendAssignBooleanLiteral(hasDeclaration, cgOperationCallExp, true);
+				return true;
+			}
+			//
+			//	Trivial source+argument case
+			//
+			if (cgSource.isTrue() && cgArgument.isFalse()) {
+				appendAssignBooleanLiteral(hasDeclaration, cgOperationCallExp, false);
+				return true;
+			}
+			//
+			//	Real case
+			//
+//			boolean hasConstantSource = cgSource.isTrue();
+//			if (!hasConstantSource) {
+			if (!js.appendLocalStatements(cgSource)) {
+				return false;
+			}
+			appendThrowIfMayBeNull(cgSource, "implies2 source");
+			appendThrowIfMayBeInvalid(cgSource);
+			hasDeclaration = appendDeclaration(hasDeclaration, cgOperationCallExp);
+			try {
+				appendIfEqualsBoolean0(cgSource, false);
+				appendAssignBooleanLiteral(hasDeclaration, cgOperationCallExp, true);
+				appendElse();
+//			}
+				if (!js.appendLocalStatements(cgArgument)) {
+					return false;
+				}
+				if (appendThrowIfNull(cgArgument, "implies2 argument")) {
+					return false;//!hasConstantSource;
+				}
+				if (appendThrowIfInvalid(cgArgument, "implies2 argument")) {
+					return false;//!hasConstantSource;
+				}
+				if (cgArgument.isFalse()) {
+					appendAssignBooleanLiteral(hasDeclaration, cgOperationCallExp, false);
+					return true;
+				}
+				appendThrowIfMayBeNull(cgArgument, "implies2 argument");
+				appendThrowIfMayBeInvalid(cgArgument);
+				appendAssignValue(hasDeclaration, cgOperationCallExp, cgArgument);
+				return true;
+			}
+			finally {
+//				if (!hasConstantSource) {
+					appendEndIf();
+//				}
+			}
+		}
+
+		@Override
+		public@NonNull Class<? extends LibraryOperation> getLibraryOperationClass() {
+			return BooleanImpliesOperation2.class;
+		}
+	}
\ No newline at end of file
diff --git a/examples/org.eclipse.ocl.examples.codegen/src/org/eclipse/ocl/examples/codegen/java/operation/ImpliesOperationHandler.java b/examples/org.eclipse.ocl.examples.codegen/src/org/eclipse/ocl/examples/codegen/java/operation/ImpliesOperationHandler.java
new file mode 100644
index 0000000..ea2ee46
--- /dev/null
+++ b/examples/org.eclipse.ocl.examples.codegen/src/org/eclipse/ocl/examples/codegen/java/operation/ImpliesOperationHandler.java
@@ -0,0 +1,145 @@
+/*******************************************************************************
+ * 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.library.LibraryOperation;
+import org.eclipse.ocl.pivot.library.logical.BooleanImpliesOperation;
+
+/**
+ * ImpliesOperationHandler generates inline code for the implies Boolean operation.
+ */
+public class ImpliesOperationHandler extends AbstractLibraryOperationHandler
+	{
+		public ImpliesOperationHandler( @NonNull JavaStream js) {
+			super(js);
+		}
+
+		@Override
+		public @NonNull Boolean generate(@NonNull CGLibraryOperationCallExp cgOperationCallExp) {
+			assert cgOperationCallExp.getReferredOperation().isIsValidating();
+			boolean hasDeclaration = false;
+			//
+			//	Short-circuit cases
+			//
+			final CGValuedElement cgSource = cgOperationCallExp.getSource();
+			CGValuedElement cgArgument = cgOperationCallExp.getArguments().get(0);
+			assert cgArgument != null;
+			if (cgSource.isFalse() || cgArgument.isTrue()) {
+				appendAssignBooleanLiteral(hasDeclaration, cgOperationCallExp, true);
+				return true;
+			}
+			//
+			//	Constant cases
+			//
+			if (cgSource.isConstant() && cgArgument.isConstant()) {
+				if (appendThrowIfInvalid(cgSource, "implies source")) {
+					return false;
+				}
+				if (appendThrowIfInvalid(cgArgument, "implies argument")) {
+					return false;
+				}
+				if (cgSource.isNull() || cgArgument.isNull()) {
+					appendAssignNullLiteral(hasDeclaration, cgOperationCallExp);
+					return true;
+				}
+				assert cgSource.isTrue() && cgArgument.isFalse();
+				appendAssignBooleanLiteral(hasDeclaration, cgOperationCallExp, false);
+				return true;
+			}
+			//
+			//	Real cases that require first term evaluation
+			//
+			if (!js.appendLocalStatements(cgSource)) {
+				return false;
+			}
+			hasDeclaration = appendDeclaration(hasDeclaration, cgOperationCallExp);
+			boolean mayBeFalseSource = appendIfEqualsBoolean1(cgSource, false);
+			if (mayBeFalseSource) {
+				appendAssignBooleanLiteral(hasDeclaration, cgOperationCallExp, true);
+				appendElse();
+			}
+			try {
+				//
+				//	Real cases that require second term evaluation too
+				//
+				if (!js.appendLocalStatements(cgArgument)) {
+					return false;
+				}
+				if (cgSource.isTrue()) {
+					appendAssignValue(hasDeclaration, cgOperationCallExp, cgArgument);
+					return true;
+				}
+			//	if (cgArgument.isFalse()) {
+			//		appendAssignNotValue(hasDeclaration, cgOperationCallExp, cgSource);
+			//		return true;
+			//	}
+
+				boolean mayBeTrueArgument = appendIfEqualsBoolean1(cgArgument, true);
+				if (mayBeTrueArgument) {
+					appendAssignBooleanLiteral(hasDeclaration, cgOperationCallExp, true);
+					appendElse();
+				}
+				if (!appendThrowIfInvalid(cgSource, "implies source") && !appendThrowIfInvalid(cgSource, "implies argument")) {
+					appendThrowIfMayBeInvalid(cgSource);
+					appendThrowIfMayBeInvalid(cgArgument);
+					if (cgSource.isNull() || cgArgument.isNull()) {
+						appendAssignNullLiteral(hasDeclaration, cgOperationCallExp);
+					}
+					else if (cgSource.isNonNull()){
+						if (cgArgument.isNonNull()){
+							appendAssignBooleanLiteral(hasDeclaration, cgOperationCallExp, false);
+						}
+						else {
+							appendIfEqualsNull(cgArgument);
+							appendAssignNullLiteral(hasDeclaration, cgOperationCallExp);
+							appendElse();
+							appendAssignBooleanLiteral(hasDeclaration, cgOperationCallExp, false);
+							appendEndIf();
+						}
+					}
+					else {
+						if (cgArgument.isNonNull()){
+							appendIfEqualsNull(cgSource);
+							appendAssignNullLiteral(hasDeclaration, cgOperationCallExp);
+							appendElse();
+							appendAssignBooleanLiteral(hasDeclaration, cgOperationCallExp, false);
+							appendEndIf();
+						}
+						else {
+							appendIfEqualsNull(cgSource, cgArgument);
+							appendAssignNullLiteral(hasDeclaration, cgOperationCallExp);
+							appendElse();
+							appendAssignBooleanLiteral(hasDeclaration, cgOperationCallExp, false);
+							appendEndIf();
+						}
+					}
+				}
+				if (mayBeTrueArgument) {
+					appendEndIf();
+				}
+			}
+			finally {
+				if (mayBeFalseSource) {
+					appendEndIf();
+				}
+			}
+			return true;
+		}
+
+		@Override
+		public@NonNull Class<? extends LibraryOperation> getLibraryOperationClass() {
+			return BooleanImpliesOperation.class;
+		}
+	}
\ No newline at end of file
diff --git a/examples/org.eclipse.ocl.examples.codegen/src/org/eclipse/ocl/examples/codegen/java/operation/LibraryOperationHandler.java b/examples/org.eclipse.ocl.examples.codegen/src/org/eclipse/ocl/examples/codegen/java/operation/LibraryOperationHandler.java
new file mode 100644
index 0000000..1ee39b8
--- /dev/null
+++ b/examples/org.eclipse.ocl.examples.codegen/src/org/eclipse/ocl/examples/codegen/java/operation/LibraryOperationHandler.java
@@ -0,0 +1,24 @@
+/*******************************************************************************
+ * 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.pivot.library.LibraryOperation;
+
+/**
+ * LibraryOperationHandler defines the API for a custom code generator for a library operation.
+ */
+public interface LibraryOperationHandler
+{
+	@NonNull Boolean generate(@NonNull CGLibraryOperationCallExp cgOperationCallExp);
+	@NonNull Class<? extends LibraryOperation> getLibraryOperationClass();
+}
\ No newline at end of file
diff --git a/examples/org.eclipse.ocl.examples.codegen/src/org/eclipse/ocl/examples/codegen/java/operation/NotOperation2Handler.java b/examples/org.eclipse.ocl.examples.codegen/src/org/eclipse/ocl/examples/codegen/java/operation/NotOperation2Handler.java
new file mode 100644
index 0000000..f8b9070
--- /dev/null
+++ b/examples/org.eclipse.ocl.examples.codegen/src/org/eclipse/ocl/examples/codegen/java/operation/NotOperation2Handler.java
@@ -0,0 +1,65 @@
+/*******************************************************************************
+ * 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.library.LibraryOperation;
+import org.eclipse.ocl.pivot.library.logical.BooleanNotOperation2;
+
+/**
+ * NotOperation2Handler generates inline code for the not2 Boolean operation.
+ */
+public class NotOperation2Handler extends AbstractLibraryOperationHandler
+	{
+		public NotOperation2Handler( @NonNull JavaStream js) {
+			super(js);
+		}
+
+		@Override
+		public @NonNull Boolean generate(@NonNull CGLibraryOperationCallExp cgOperationCallExp) {
+			assert !cgOperationCallExp.getReferredOperation().isIsInvalidating();
+			assert !cgOperationCallExp.getReferredOperation().isIsValidating();
+			boolean hasDeclaration = false;
+			//
+			//	Trivial source cases
+			//
+			final CGValuedElement cgSource = cgOperationCallExp.getSource();
+			if (cgSource.isFalse()) {
+				appendAssignBooleanLiteral(hasDeclaration, cgOperationCallExp, true);
+				return true;
+			}
+			if (cgSource.isTrue()) {
+				appendAssignBooleanLiteral(hasDeclaration, cgOperationCallExp, false);
+				return true;
+			}
+			//
+			//	Real case
+			//
+			if (!js.appendLocalStatements(cgSource)) {
+				return false;
+			}
+			hasDeclaration = appendDeclaration(hasDeclaration, cgOperationCallExp);
+			appendIfEqualsBoolean0(cgSource, false);
+			appendAssignBooleanLiteral(hasDeclaration, cgOperationCallExp, true);
+			appendElse();
+			appendAssignBooleanLiteral(hasDeclaration, cgOperationCallExp, false);
+			appendEndIf();
+			return true;
+		}
+
+		@Override
+		public@NonNull Class<? extends LibraryOperation> getLibraryOperationClass() {
+			return BooleanNotOperation2.class;
+		}
+	}
\ No newline at end of file
diff --git a/examples/org.eclipse.ocl.examples.codegen/src/org/eclipse/ocl/examples/codegen/java/operation/NotOperationHandler.java b/examples/org.eclipse.ocl.examples.codegen/src/org/eclipse/ocl/examples/codegen/java/operation/NotOperationHandler.java
new file mode 100644
index 0000000..349d508
--- /dev/null
+++ b/examples/org.eclipse.ocl.examples.codegen/src/org/eclipse/ocl/examples/codegen/java/operation/NotOperationHandler.java
@@ -0,0 +1,76 @@
+/*******************************************************************************
+ * 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.library.LibraryOperation;
+import org.eclipse.ocl.pivot.library.logical.BooleanNotOperation;
+
+/**
+ * NotOperationHandler generates inline code for the not Boolean operation.
+ */
+public class NotOperationHandler extends AbstractLibraryOperationHandler
+	{
+		public NotOperationHandler( @NonNull JavaStream js) {
+			super(js);
+		}
+
+		@Override
+		public @NonNull Boolean generate(@NonNull CGLibraryOperationCallExp cgOperationCallExp) {
+			assert cgOperationCallExp.getReferredOperation().isIsValidating();
+			boolean hasDeclaration = false;
+			//
+			//	Trivial source cases
+			//
+			final CGValuedElement cgSource = cgOperationCallExp.getSource();
+			if (appendThrowIfInvalid(cgSource, "not source")) {
+				return false;
+			}
+			if (cgSource.isFalse()) {
+				appendAssignBooleanLiteral(hasDeclaration, cgOperationCallExp, true);
+				return true;
+			}
+			if (cgSource.isTrue()) {
+				appendAssignBooleanLiteral(hasDeclaration, cgOperationCallExp, false);
+				return true;
+			}
+			if (cgSource.isNull()) {
+				appendAssignNullLiteral(hasDeclaration, cgOperationCallExp);
+				return true;
+			}
+			//
+			//	Real case
+			//
+			if (!js.appendLocalStatements(cgSource)) {
+				return false;
+			}
+			appendThrowIfMayBeInvalid(cgSource);
+			hasDeclaration = appendDeclaration(hasDeclaration, cgOperationCallExp);
+			appendIfEqualsBoolean0(cgSource, false);
+			appendAssignBooleanLiteral(hasDeclaration, cgOperationCallExp, true);
+			appendElse();
+			appendIfEqualsBoolean0(cgSource, true);
+			appendAssignBooleanLiteral(hasDeclaration, cgOperationCallExp, false);
+			appendElse();
+			appendAssignNullLiteral(hasDeclaration, cgOperationCallExp);
+			appendEndIf();
+			appendEndIf();
+			return true;
+		}
+
+		@Override
+		public@NonNull Class<? extends LibraryOperation> getLibraryOperationClass() {
+			return BooleanNotOperation.class;
+		}
+	}
\ No newline at end of file
diff --git a/examples/org.eclipse.ocl.examples.codegen/src/org/eclipse/ocl/examples/codegen/java/operation/OrOperation2Handler.java b/examples/org.eclipse.ocl.examples.codegen/src/org/eclipse/ocl/examples/codegen/java/operation/OrOperation2Handler.java
new file mode 100644
index 0000000..f28c008
--- /dev/null
+++ b/examples/org.eclipse.ocl.examples.codegen/src/org/eclipse/ocl/examples/codegen/java/operation/OrOperation2Handler.java
@@ -0,0 +1,115 @@
+/*******************************************************************************
+ * 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.library.LibraryOperation;
+import org.eclipse.ocl.pivot.library.logical.BooleanOrOperation2;
+
+/**
+ * OrOperation2Handler generates inline code for the or2 Boolean operation.
+ */
+public class OrOperation2Handler extends AbstractLibraryOperationHandler
+	{
+		public OrOperation2Handler( @NonNull JavaStream js) {
+			super(js);
+		}
+
+		@Override
+		public @NonNull Boolean generate(@NonNull CGLibraryOperationCallExp cgOperationCallExp) {
+			assert !cgOperationCallExp.getReferredOperation().isIsInvalidating();
+			assert !cgOperationCallExp.getReferredOperation().isIsValidating();
+			boolean hasDeclaration = false;
+			//
+			//	Trivial source cases
+			//
+			final CGValuedElement cgSource = cgOperationCallExp.getSource();
+			if (appendThrowIfNull(cgSource, "or2 source")) {
+				return false;
+			}
+			if (appendThrowIfInvalid(cgSource, "or2 source")) {
+				return false;
+			}
+			if (cgSource.isTrue()) {
+				appendAssignBooleanLiteral(hasDeclaration, cgOperationCallExp, true);
+				return true;
+			}
+			//
+			//	Trivial argument cases
+			//
+			CGValuedElement cgArgument = cgOperationCallExp.getArguments().get(0);
+			assert cgArgument != null;
+			if (appendThrowIfNull(cgArgument, "or2 argument")) {
+				return false;
+			}
+			if (appendThrowIfInvalid(cgArgument, "or2 argument")) {
+				return false;
+			}
+			if (cgArgument.isTrue()) {
+				appendAssignBooleanLiteral(hasDeclaration, cgOperationCallExp, true);
+				return true;
+			}
+			//
+			//	Trivial source+argument case
+			//
+			if (cgSource.isFalse() && cgArgument.isFalse()) {
+				appendAssignBooleanLiteral(hasDeclaration, cgOperationCallExp, false);
+				return true;
+			}
+			//
+			//	Real case
+			//
+			boolean hasConstantSource = cgSource.isTrue();
+			if (!hasConstantSource) {
+				if (!js.appendLocalStatements(cgSource)) {
+					return false;
+				}
+				appendThrowIfMayBeNull(cgSource, "or2 source");
+				appendThrowIfMayBeInvalid(cgSource);
+				hasDeclaration = appendDeclaration(hasDeclaration, cgOperationCallExp);
+				appendIfEqualsBoolean0(cgSource, true);
+				appendAssignBooleanLiteral(hasDeclaration, cgOperationCallExp, true);
+				appendElse();
+			}
+			try {
+				if (!js.appendLocalStatements(cgArgument)) {
+					return false;
+				}
+				if (appendThrowIfNull(cgArgument, "or2 argument")) {
+					return !hasConstantSource;
+				}
+				if (appendThrowIfInvalid(cgArgument, "or2 argument")) {
+					return !hasConstantSource;
+				}
+				if (cgArgument.isFalse()) {
+					appendAssignBooleanLiteral(hasDeclaration, cgOperationCallExp, false);
+					return true;
+				}
+				appendThrowIfMayBeNull(cgArgument, "or2 argument");
+				appendThrowIfMayBeInvalid(cgArgument);
+				appendAssignValue(hasDeclaration, cgOperationCallExp, cgArgument);
+				return true;
+			}
+			finally {
+				if (!hasConstantSource) {
+					appendEndIf();
+				}
+			}
+		}
+
+		@Override
+		public@NonNull Class<? extends LibraryOperation> getLibraryOperationClass() {
+			return BooleanOrOperation2.class;
+		}
+	}
\ No newline at end of file
diff --git a/examples/org.eclipse.ocl.examples.codegen/src/org/eclipse/ocl/examples/codegen/java/operation/OrOperationHandler.java b/examples/org.eclipse.ocl.examples.codegen/src/org/eclipse/ocl/examples/codegen/java/operation/OrOperationHandler.java
new file mode 100644
index 0000000..072f246
--- /dev/null
+++ b/examples/org.eclipse.ocl.examples.codegen/src/org/eclipse/ocl/examples/codegen/java/operation/OrOperationHandler.java
@@ -0,0 +1,144 @@
+/*******************************************************************************
+ * 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.library.LibraryOperation;
+import org.eclipse.ocl.pivot.library.logical.BooleanOrOperation;
+
+/**
+ * OrOperationHandler generates inline code for the or Boolean operation.
+ */
+public class OrOperationHandler extends AbstractLibraryOperationHandler
+	{
+		public OrOperationHandler( @NonNull JavaStream js) {
+			super(js);
+		}
+
+		@Override
+		public @NonNull Boolean generate(@NonNull CGLibraryOperationCallExp cgOperationCallExp) {
+			assert cgOperationCallExp.getReferredOperation().isIsValidating();
+			boolean hasDeclaration = false;
+			//
+			//	Short-circuit cases
+			//
+			final CGValuedElement cgSource = cgOperationCallExp.getSource();
+			CGValuedElement cgArgument = cgOperationCallExp.getArguments().get(0);
+			assert cgArgument != null;
+			if (cgSource.isTrue() || cgArgument.isTrue()) {
+				appendAssignBooleanLiteral(hasDeclaration, cgOperationCallExp, true);
+				return true;
+			}
+			//
+			//	Constant cases
+			//
+			if (cgSource.isConstant() && cgArgument.isConstant()) {
+				if (appendThrowIfInvalid(cgSource, "or source")) {
+					return false;
+				}
+				if (appendThrowIfInvalid(cgArgument, "or argument")) {
+					return false;
+				}
+				if (cgSource.isNull() || cgArgument.isNull()) {
+					appendAssignNullLiteral(hasDeclaration, cgOperationCallExp);
+					return true;
+				}
+				assert cgSource.isFalse() && cgArgument.isFalse();
+				appendAssignBooleanLiteral(hasDeclaration, cgOperationCallExp, false);
+				return true;
+			}
+			//
+			//	Real cases that require first term evaluation
+			//
+			if (!js.appendLocalStatements(cgSource)) {
+				return false;
+			}
+			hasDeclaration = appendDeclaration(hasDeclaration, cgOperationCallExp);
+			boolean mayBeTrue1 = appendIfEqualsBoolean1(cgSource, true);
+			if (mayBeTrue1) {
+				appendAssignBooleanLiteral(hasDeclaration, cgOperationCallExp, true);
+				appendElse();
+			}
+			try {
+				//
+				//	Real cases that require second term evaluation too
+				//
+				if (!js.appendLocalStatements(cgArgument)) {
+					return false;
+				}
+				if (cgSource.isFalse()) {
+					appendAssignValue(hasDeclaration, cgOperationCallExp, cgArgument);
+					return true;
+				}
+				if (cgArgument.isFalse()) {
+					appendAssignValue(hasDeclaration, cgOperationCallExp, cgSource);
+					return true;
+				}
+				boolean mayBeTrue2 = appendIfEqualsBoolean1(cgArgument, true);
+				if (mayBeTrue2) {
+					appendAssignBooleanLiteral(hasDeclaration, cgOperationCallExp, true);
+					appendElse();
+				}
+				if (!appendThrowIfInvalid(cgSource, "or source") && !appendThrowIfInvalid(cgSource, "or argument")) {
+					appendThrowIfMayBeInvalid(cgSource);
+					appendThrowIfMayBeInvalid(cgArgument);
+					if (cgSource.isNull() || cgArgument.isNull()) {
+						appendAssignNullLiteral(hasDeclaration, cgOperationCallExp);
+					}
+					else if (cgSource.isNonNull()){
+						if (cgArgument.isNonNull()){
+							appendAssignBooleanLiteral(hasDeclaration, cgOperationCallExp, false);
+						}
+						else {
+							appendIfEqualsNull(cgArgument);
+							appendAssignNullLiteral(hasDeclaration, cgOperationCallExp);
+							appendElse();
+							appendAssignBooleanLiteral(hasDeclaration, cgOperationCallExp, false);
+							appendEndIf();
+						}
+					}
+					else {
+						if (cgArgument.isNonNull()){
+							appendIfEqualsNull(cgSource);
+							appendAssignNullLiteral(hasDeclaration, cgOperationCallExp);
+							appendElse();
+							appendAssignBooleanLiteral(hasDeclaration, cgOperationCallExp, false);
+							appendEndIf();
+						}
+						else {
+							appendIfEqualsNull(cgSource, cgArgument);
+							appendAssignNullLiteral(hasDeclaration, cgOperationCallExp);
+							appendElse();
+							appendAssignBooleanLiteral(hasDeclaration, cgOperationCallExp, false);
+							appendEndIf();
+						}
+					}
+				}
+				if (mayBeTrue2) {
+					appendEndIf();
+				}
+			}
+			finally {
+				if (mayBeTrue1) {
+					appendEndIf();
+				}
+			}
+			return true;
+		}
+
+		@Override
+		public@NonNull Class<? extends LibraryOperation> getLibraryOperationClass() {
+			return BooleanOrOperation.class;
+		}
+	}
\ No newline at end of file
diff --git a/examples/org.eclipse.ocl.examples.codegen/src/org/eclipse/ocl/examples/codegen/java/operation/XorOperation2Handler.java b/examples/org.eclipse.ocl.examples.codegen/src/org/eclipse/ocl/examples/codegen/java/operation/XorOperation2Handler.java
new file mode 100644
index 0000000..1428e51
--- /dev/null
+++ b/examples/org.eclipse.ocl.examples.codegen/src/org/eclipse/ocl/examples/codegen/java/operation/XorOperation2Handler.java
@@ -0,0 +1,79 @@
+/*******************************************************************************
+ * 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.library.LibraryOperation;
+import org.eclipse.ocl.pivot.library.logical.BooleanXorOperation2;
+
+/**
+ * XorOperation2Handler generates inline code for the xor2 Boolean operation.
+ */
+public class XorOperation2Handler extends AbstractLibraryOperationHandler
+	{
+		public XorOperation2Handler( @NonNull JavaStream js) {
+			super(js);
+		}
+
+		@Override
+		public @NonNull Boolean generate(@NonNull CGLibraryOperationCallExp cgOperationCallExp) {
+			assert !cgOperationCallExp.getReferredOperation().isIsInvalidating();
+			assert !cgOperationCallExp.getReferredOperation().isIsValidating();
+			boolean hasDeclaration = false;
+			//
+			//	Trivial source cases
+			//
+			final CGValuedElement cgSource = cgOperationCallExp.getSource();
+			//
+			//	Trivial argument cases
+			//
+			CGValuedElement cgArgument = cgOperationCallExp.getArguments().get(0);
+			assert cgArgument != null;
+			//
+			//	Trivial source+argument cases
+			//
+			if (cgSource.isFalse() && cgArgument.isFalse()) {
+				appendAssignBooleanLiteral(hasDeclaration, cgOperationCallExp, false);
+				return true;
+			}
+			if (cgSource.isFalse() && cgArgument.isTrue()) {
+				appendAssignBooleanLiteral(hasDeclaration, cgOperationCallExp, true);
+				return true;
+			}
+			if (cgSource.isTrue() && cgArgument.isFalse()) {
+				appendAssignBooleanLiteral(hasDeclaration, cgOperationCallExp, true);
+				return true;
+			}
+			if (cgSource.isTrue() && cgArgument.isTrue()) {
+				appendAssignBooleanLiteral(hasDeclaration, cgOperationCallExp, false);
+				return true;
+			}
+			//
+			//	Real case
+			//
+			if (!js.appendLocalStatements(cgSource)) {
+				return false;
+			}
+			if (!js.appendLocalStatements(cgArgument)) {
+				return false;
+			}
+			appendAssignXor(hasDeclaration, cgOperationCallExp, cgSource, cgArgument);
+			return true;
+		}
+
+		@Override
+		public@NonNull Class<? extends LibraryOperation> getLibraryOperationClass() {
+			return BooleanXorOperation2.class;
+		}
+	}
\ No newline at end of file
diff --git a/examples/org.eclipse.ocl.examples.codegen/src/org/eclipse/ocl/examples/codegen/java/operation/XorOperationHandler.java b/examples/org.eclipse.ocl.examples.codegen/src/org/eclipse/ocl/examples/codegen/java/operation/XorOperationHandler.java
new file mode 100644
index 0000000..c0f4e0b
--- /dev/null
+++ b/examples/org.eclipse.ocl.examples.codegen/src/org/eclipse/ocl/examples/codegen/java/operation/XorOperationHandler.java
@@ -0,0 +1,120 @@
+/*******************************************************************************
+ * 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.library.LibraryOperation;
+import org.eclipse.ocl.pivot.library.logical.BooleanXorOperation;
+
+/**
+ * XorOperationHandler generates inline code for the xor Boolean operation.
+ */
+public class XorOperationHandler extends AbstractLibraryOperationHandler
+	{
+		public XorOperationHandler( @NonNull JavaStream js) {
+			super(js);
+		}
+
+		@Override
+		public @NonNull Boolean generate(@NonNull CGLibraryOperationCallExp cgOperationCallExp) {
+			assert !cgOperationCallExp.getReferredOperation().isIsValidating();
+			boolean hasDeclaration = false;
+			//
+			//	Trivial source cases
+			//
+			final CGValuedElement cgSource = cgOperationCallExp.getSource();
+			if (appendThrowIfInvalid(cgSource, "xor source")) {
+				return false;
+			}
+			//
+			//	Trivial argument cases
+			//
+			CGValuedElement cgArgument = cgOperationCallExp.getArguments().get(0);
+			assert cgArgument != null;
+			if (appendThrowIfInvalid(cgArgument, "xor argument")) {
+				return false;
+			}
+			//
+			//	Trivial source+argument cases
+			//
+			if (cgSource.isFalse() && cgArgument.isFalse()) {
+				appendAssignBooleanLiteral(hasDeclaration, cgOperationCallExp, false);
+				return true;
+			}
+			if (cgSource.isFalse() && cgArgument.isTrue()) {
+				appendAssignBooleanLiteral(hasDeclaration, cgOperationCallExp, true);
+				return true;
+			}
+			if (cgSource.isTrue() && cgArgument.isFalse()) {
+				appendAssignBooleanLiteral(hasDeclaration, cgOperationCallExp, true);
+				return true;
+			}
+			if (cgSource.isTrue() && cgArgument.isTrue()) {
+				appendAssignBooleanLiteral(hasDeclaration, cgOperationCallExp, false);
+				return true;
+			}
+			//
+			//	Real case
+			//
+			if (!js.appendLocalStatements(cgSource)) {
+				return false;
+			}
+		//	appendThrowIfMayBeInvalid(cgSource);	-- xor is not validating so an InvalidValueException propagates
+			if (!js.appendLocalStatements(cgArgument)) {
+				return false;
+			}
+		//	appendThrowIfMayBeInvalid(cgArgument);	-- xor is not validating so an InvalidValueException propagates
+			if (cgSource.isNull() || cgArgument.isNull()) {
+				appendAssignNullLiteral(hasDeclaration, cgOperationCallExp);
+				return true;
+			}
+
+			if (cgSource.isNonNull()) {
+				if (cgArgument.isNonNull()) {
+					appendAssignXor(hasDeclaration, cgOperationCallExp, cgSource, cgArgument);
+				}
+				else {
+					hasDeclaration = appendDeclaration(hasDeclaration, cgOperationCallExp);
+					appendIfEqualsNull(cgArgument);
+					appendAssignNullLiteral(hasDeclaration, cgOperationCallExp);
+					appendElse();
+					appendAssignXor(hasDeclaration, cgOperationCallExp, cgSource, cgArgument);
+					appendEndIf();
+				}
+			}
+			else {
+				hasDeclaration = appendDeclaration(hasDeclaration, cgOperationCallExp);
+				appendIfEqualsNull(cgSource);
+				appendAssignNullLiteral(hasDeclaration, cgOperationCallExp);
+				appendElse();
+				if (cgArgument.isNonNull()) {
+					appendAssignXor(hasDeclaration, cgOperationCallExp, cgSource, cgArgument);
+				}
+				else {
+					appendIfEqualsNull(cgArgument);
+					appendAssignNullLiteral(hasDeclaration, cgOperationCallExp);
+					appendElse();
+					appendAssignXor(hasDeclaration, cgOperationCallExp, cgSource, cgArgument);
+					appendEndIf();
+				}
+				appendEndIf();
+			}
+			return true;
+		}
+
+		@Override
+		public@NonNull Class<? extends LibraryOperation> getLibraryOperationClass() {
+			return BooleanXorOperation.class;
+		}
+	}
\ No newline at end of file
diff --git a/examples/org.eclipse.ocl.examples.codegen/src/org/eclipse/ocl/examples/codegen/oclinecore/OCLinEcoreCodeGenerator.java b/examples/org.eclipse.ocl.examples.codegen/src/org/eclipse/ocl/examples/codegen/oclinecore/OCLinEcoreCodeGenerator.java
index b1ec011..26bc7e8 100644
--- a/examples/org.eclipse.ocl.examples.codegen/src/org/eclipse/ocl/examples/codegen/oclinecore/OCLinEcoreCodeGenerator.java
+++ b/examples/org.eclipse.ocl.examples.codegen/src/org/eclipse/ocl/examples/codegen/oclinecore/OCLinEcoreCodeGenerator.java
@@ -479,7 +479,7 @@
 		}
 		Variable asSelfVariable = ClassUtil.nonNullState(asSynthesizedQuery.getOwnedContext());
 		Variable asDiagnosticsVariable = asHelper.createParameterVariable("diagnostics", oclAnyType, false);
-		Variable asConstraintNameNameVariable = asHelper.createParameterVariable(JavaConstants.CONSTRAINT_NAME_NAME, stringType, false);
+		Variable asConstraintNameNameVariable = asHelper.createParameterVariable(JavaConstants.CONSTRAINT_NAME_NAME, stringType, true);
 		asSynthesizedQuery.getOwnedParameters().add(asDiagnosticsVariable);
 		Variable asContextVariable = asHelper.createParameterVariable("context", oclAnyType, false);
 		asSynthesizedQuery.getOwnedParameters().add(asContextVariable);
diff --git a/examples/org.eclipse.ocl.examples.codegen/src/org/eclipse/ocl/examples/codegen/utilities/CGUtil.java b/examples/org.eclipse.ocl.examples.codegen/src/org/eclipse/ocl/examples/codegen/utilities/CGUtil.java
index c96aa94..53eb0bb 100644
--- a/examples/org.eclipse.ocl.examples.codegen/src/org/eclipse/ocl/examples/codegen/utilities/CGUtil.java
+++ b/examples/org.eclipse.ocl.examples.codegen/src/org/eclipse/ocl/examples/codegen/utilities/CGUtil.java
@@ -39,6 +39,7 @@
 import org.eclipse.ocl.examples.codegen.cgmodel.CGVariable;
 import org.eclipse.ocl.examples.codegen.cgmodel.CGVariableExp;
 import org.eclipse.ocl.pivot.Constraint;
+import org.eclipse.ocl.pivot.OCLExpression;
 import org.eclipse.ocl.pivot.Variable;
 import org.eclipse.ocl.pivot.VariableDeclaration;
 import org.eclipse.ocl.pivot.ids.ElementId;
@@ -169,6 +170,10 @@
 		return cgParameter;
 	}
 
+	public static @NonNull OCLExpression getAST(@NonNull CGCallExp cgCallExp) {
+		return ClassUtil.nonNullState((OCLExpression)cgCallExp.getAst());
+	}
+
 	public static org.eclipse.ocl.pivot.@NonNull Class getAST(@NonNull CGClass cgClass) {
 		return ClassUtil.nonNullState((org.eclipse.ocl.pivot.Class)cgClass.getAst());
 	}