From a4c49252681b7b1a0953d0c13fc2d4cbf0a98e3e Mon Sep 17 00:00:00 2001
From: Niklas Fors <niklas.fors@cs.lth.se>
Date: Wed, 4 Jul 2018 17:25:15 +0200
Subject: [PATCH] Add part implementation of bidirectional and add test case

---
 .gitignore                 |   4 +-
 spec/jastadd/Analysis.jrag |  11 +-
 spec/jastadd/Backend.jadd  |  94 ++++++++++++--
 spec/jastadd/Errors.jrag   |  10 +-
 test/.gitignore            |   3 +
 test/AllBi.relast          |  15 +++
 test/Makefile              |   8 ++
 test/Test.java             | 248 +++++++++++++++++++++++++++++++++++++
 test/Utils.jadd            |   9 ++
 9 files changed, 385 insertions(+), 17 deletions(-)
 create mode 100644 test/.gitignore
 create mode 100644 test/AllBi.relast
 create mode 100644 test/Makefile
 create mode 100644 test/Test.java
 create mode 100644 test/Utils.jadd

diff --git a/.gitignore b/.gitignore
index 860c498..c52fdba 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,8 +1,8 @@
 .DS_Store
-.class
+*.class
 .vimrc
 .*.swp
-*.relast
+/*.relast
 
 /.settings
 /bin/
diff --git a/spec/jastadd/Analysis.jrag b/spec/jastadd/Analysis.jrag
index 7943957..f71c225 100644
--- a/spec/jastadd/Analysis.jrag
+++ b/spec/jastadd/Analysis.jrag
@@ -32,10 +32,13 @@ aspect ComponentAnalysis {
 	inh TypeDecl Component.enclosingTypeDecl();
 	eq TypeDecl.getChild().enclosingTypeDecl() = this;
 	eq Program.getChild().enclosingTypeDecl() = null;
-	inh TypeDecl RelationComponent.ofTypeDecl();
-	eq Relation.getLeft().ofTypeDecl() = getRight().toTypeDecl();
-	eq Relation.getRight().ofTypeDecl() = getLeft().toTypeDecl();
-	eq Program.getChild().ofTypeDecl() = null;
+	
+	inh RelationComponent RelationComponent.otherSide();
+	eq Relation.getLeft().otherSide() = getRight();
+	eq Relation.getRight().otherSide() = getLeft();
+	eq Program.getChild().otherSide() = null;
+	
+	syn TypeDecl RelationComponent.ofTypeDecl() = otherSide().toTypeDecl();
 
 	syn boolean Component.isAlreadyDeclared()
 		= !isTargetOfDirectedRelation()
diff --git a/spec/jastadd/Backend.jadd b/spec/jastadd/Backend.jadd
index 3506e24..2171c5d 100644
--- a/spec/jastadd/Backend.jadd
+++ b/spec/jastadd/Backend.jadd
@@ -112,13 +112,23 @@ aspect BackendAPI {
 	public void Relation.generateAPI(StringBuilder sb) {
 		sb.append(ind(1) + "// " + prettyPrint() + "\n");
 		getDirection().generateAPI(sb);
+		sb.append("\n");
 	}
 	public abstract void Direction.generateAPI(StringBuilder sb);
-	public void RightDirection.generateAPI(StringBuilder sb) {
-		relation().getLeft().generateDirectedAPI(sb);
+
+
+	inh Relation Direction.relation();
+	eq Relation.getChild().relation() = this;
+	eq Program.getChild().relation() = null;
+
+	public String RelationComponent.nameCapitalized() {
+		return name().substring(0,1).toUpperCase() + name().substring(1);
 	}
-	public void Bidirectional.generateAPI(StringBuilder sb) {
+}
 
+aspect BackendDirectedAPI {
+	public void RightDirection.generateAPI(StringBuilder sb) {
+		relation().getLeft().generateDirectedAPI(sb);
 	}
 
 	public abstract void RelationComponent.generateDirectedAPI(StringBuilder sb);
@@ -180,16 +190,84 @@ aspect BackendAPI {
 		sb.append(ind(2) + "}\n");
 		sb.append(ind(1) + "}\n");
 	}
+}
 
-	inh Relation Direction.relation();
-	eq Relation.getChild().relation() = this;
-	eq Program.getChild().relation() = null;
+aspect BackendBidirectionalAPI {
+	public void Bidirectional.generateAPI(StringBuilder sb) {
+		RelationComponent l = relation().getLeft();
+		RelationComponent r = relation().getRight();
 
-	public String RelationComponent.nameCapitalized() {
-		return name().substring(0,1).toUpperCase() + name().substring(1);
+		if (l.multiplicityOne()) {
+			if (r.multiplicityOne()) {
+				l.generateBiOne(sb, false);
+				r.generateBiOne(sb, false);
+			} else if (r.multiplicityOpt()) {
+				l.generateBiOne(sb, false);
+				r.generateBiOne(sb, true);
+			} else if (r.multiplicityMany()) {
+
+			}
+		} else if (l.multiplicityOpt()) {
+			if (r.multiplicityOne()) {
+				l.generateBiOne(sb, true);
+				r.generateBiOne(sb, false);
+			} else if (r.multiplicityOpt()) {
+				l.generateBiOne(sb, true);
+				r.generateBiOne(sb, true);
+			} else if (r.multiplicityMany()) {
+				
+			}
+		} else if (l.multiplicityMany()) {
+			if (r.multiplicityOne()) {
+
+			} else if (r.multiplicityOpt()) {
+
+			} else if (r.multiplicityMany()) {
+				
+			}
+		}
+	}
+
+	public void RelationComponent.generateBiOne(StringBuilder sb, boolean isOpt) {
+		// Get
+		sb.append(ind(1) + "public " + ofTypeDecl() + " " + toTypeDecl());
+		sb.append("." + name() + "() {\n");
+		sb.append(ind(2) + "return get" + getImplAttributeName() + "();\n");
+		sb.append(ind(1) + "}\n");
+
+		// Set
+		sb.append(ind(1) + "public void " + toTypeDecl());
+		sb.append(".set" + nameCapitalized() + "(" + ofTypeDecl() + " o) {\n");
+		if (!isOpt) {
+			sb.append(ind(2) + "assertNotNull(o);\n");
+		}
+		sb.append(ind(2) + "if (get" + getImplAttributeName() + "() != null) {\n");
+		sb.append(ind(3) + "get" + getImplAttributeName() + "().set" + otherSide().getImplAttributeName() + "(null);\n");
+		sb.append(ind(2) + "}\n");
+		sb.append(ind(2) + "if (o != null && o.get" + otherSide().getImplAttributeName() + "() != null) {\n");
+		sb.append(ind(3) + "o.get" + otherSide().getImplAttributeName() + "().set" + getImplAttributeName() + "(null);\n");
+		sb.append(ind(2) + "}\n");
+		sb.append(ind(2) + "set" + getImplAttributeName() + "(o);\n");
+		if (isOpt) {
+			sb.append(ind(2) + "if (o != null) {\n");
+			sb.append(ind(3) + "o.set" + otherSide().getImplAttributeName() + "(this);\n");
+			sb.append(ind(2) + "}\n");
+		} else {
+			sb.append(ind(2) + "o.set" + otherSide().getImplAttributeName() + "(this);\n");
+		}
+		sb.append(ind(1) + "}\n");
+
+		if (isOpt) {
+			// has
+			sb.append(ind(1) + "public boolean " + toTypeDecl());
+			sb.append(".has" + nameCapitalized() + "() {\n");
+			sb.append(ind(2) + "return " + name() + "() != null;\n");
+			sb.append(ind(1) + "}\n");
+		}
 	}
 }
 
+
 aspect PrettyPrint {
 	public String Relation.prettyPrint() {
 		return "rel "
diff --git a/spec/jastadd/Errors.jrag b/spec/jastadd/Errors.jrag
index b97c825..e3f5d60 100644
--- a/spec/jastadd/Errors.jrag
+++ b/spec/jastadd/Errors.jrag
@@ -32,7 +32,7 @@ aspect Errors {
 
 	RelationComponent contributes
 		error("The target of a directed relation may only have multiplicity 1")
-		when isTargetOfDirectedRelation() && !hasMultiplicityOne()
+		when isTargetOfDirectedRelation() && !multiplicityOne()
 		to Program.errors();
 }
 
@@ -44,8 +44,12 @@ aspect HelpAttributes {
 	eq Program.getChild().isToken() = false;
 	eq TokenComponent.getTypeUse().isToken() = true;
 
-	syn boolean RelationComponent.hasMultiplicityOne() = false;
-	eq OneRelationComponent.hasMultiplicityOne() = true;
+	syn boolean RelationComponent.multiplicityOne() = false;
+	eq OneRelationComponent.multiplicityOne() = true;
+	syn boolean RelationComponent.multiplicityOpt() = false;
+	eq OptionalRelationComponent.multiplicityOpt() = true;
+	syn boolean RelationComponent.multiplicityMany() = false;
+	eq ManyRelationComponent.multiplicityMany() = true;
 }
 
 aspect ErrorMessage {
diff --git a/test/.gitignore b/test/.gitignore
new file mode 100644
index 0000000..c9c2125
--- /dev/null
+++ b/test/.gitignore
@@ -0,0 +1,3 @@
+/AST/*
+/AllBiGen.ast
+/AllBiGen.jadd
diff --git a/test/AllBi.relast b/test/AllBi.relast
new file mode 100644
index 0000000..03f9ec1
--- /dev/null
+++ b/test/AllBi.relast
@@ -0,0 +1,15 @@
+Root ::= A* B*;
+A ::= <Name>;
+B ::= <Name>;
+
+rel A.b1 <-> B.a1;
+rel A.b2 <-> B.a2?;
+rel A.b3 <-> B.a3*;
+
+rel A.b4? <-> B.a4;
+rel A.b5? <-> B.a5?;
+rel A.b6? <-> B.a6*;
+
+rel A.b7* <-> B.a7;
+rel A.b8* <-> B.a8?;
+rel A.b9* <-> B.a9*;
diff --git a/test/Makefile b/test/Makefile
new file mode 100644
index 0000000..1f34553
--- /dev/null
+++ b/test/Makefile
@@ -0,0 +1,8 @@
+all: compile run
+compile:
+	(cd .. && ant jar)
+	java -jar ../compiler.jar AllBi.relast --file
+	java -jar ../tools/jastadd2.jar --package=AST AllBiGen.ast AllBiGen.jadd Utils.jadd
+run:
+	javac AST/*.java Test.java
+	java Test
\ No newline at end of file
diff --git a/test/Test.java b/test/Test.java
new file mode 100644
index 0000000..38ae795
--- /dev/null
+++ b/test/Test.java
@@ -0,0 +1,248 @@
+import AST.*;
+
+public class Test {
+	private Root r;
+	private A a1;
+	private A a2;
+	private A a3;
+	private B b1;
+	private B b2;
+	private B b3;
+
+	public static void main(String args[]) {
+		new Test().test();
+	}
+
+	public void test() {
+		test1();
+		test2();
+		test3();
+		test4();
+		test5();
+		test6();
+		test7();
+		test8();
+		test9();
+	}
+
+
+
+	/**
+	 * rel A.b1 <-> B.a1;
+	 */
+	private void test1() {
+		test11();
+		test12();
+	}
+	private void test11() {
+		// Init
+		setup();
+		a1.setB1(b1);
+		a2.setB1(b2);
+
+		// Change
+		a2.setB1(b1);
+
+		assertNull(a1.b1());
+		assertSame(a2.b1(), b1);
+		assertSame(b1.a1(), a2);
+		assertNull(b2.a1());
+	}
+	private void test12() {
+		// Init
+		setup();
+		a1.setB1(b2);
+
+		// Change
+		a2.setB1(b2);
+
+		assertNull(a1.b1());
+		assertSame(a2.b1(), b2);
+		assertNull(b1.a1());
+		assertSame(b2.a1(), a2);
+	}
+
+
+
+	/**
+	 * rel A.b2 <-> B.a2?;
+	 */
+	private void test2() {
+		test21();
+		test22();
+	}
+	private void test21() {
+		// Init
+		setup();
+		a1.setB2(b1);
+		a2.setB2(b2);
+
+		// Change
+		a2.setB2(b1);
+
+		assertNull(a1.b2());
+		assertSame(a2.b2(), b1);
+		assertSame(b1.a2(), a2);
+		assertNull(b2.a2());
+	}
+	private void test22() {
+		// Init
+		setup();
+		a1.setB2(b2);
+
+		// Change
+		a2.setB2(b2);
+
+		assertNull(a1.b2());
+		assertSame(a2.b2(), b2);
+		assertNull(b1.a2());
+		assertSame(b2.a2(), a2);
+	}
+
+
+
+	/**
+	 * rel A.b3 <-> B.a3*;
+	 */
+	private void test3() {
+	}
+
+
+
+	/**
+	 * rel A.b4? <-> B.a4;
+	 */
+	private void test4() {
+		test41();
+		test42();
+	}
+	private void test41() {
+		// Init
+		setup();
+		a1.setB4(b1);
+		a2.setB4(b2);
+
+		// Change
+		a2.setB4(b1);
+
+		assertNull(a1.b4());
+		assertSame(a2.b4(), b1);
+		assertSame(b1.a4(), a2);
+		assertNull(b2.a4());
+	}
+	private void test42() {
+		// Init
+		setup();
+		a1.setB4(b2);
+
+		// Change
+		a2.setB4(b2);
+
+		assertNull(a1.b4());
+		assertSame(a2.b4(), b2);
+		assertNull(b1.a4());
+		assertSame(b2.a4(), a2);
+	}
+
+
+
+	/**
+	 * rel A.b5? <-> B.a5?;
+	 */
+	private void test5() {
+		test51();
+		test52();
+	}
+	private void test51() {
+		// Init
+		setup();
+		a1.setB5(b1);
+		a2.setB5(b2);
+
+		// Change
+		a2.setB5(b1);
+
+		assertNull(a1.b5());
+		assertSame(a2.b5(), b1);
+		assertSame(b1.a5(), a2);
+		assertNull(b2.a5());
+	}
+	private void test52() {
+		// Init
+		setup();
+		a1.setB5(b2);
+
+		// Change
+		a2.setB5(b2);
+
+		assertNull(a1.b5());
+		assertSame(a2.b5(), b2);
+		assertNull(b1.a5());
+		assertSame(b2.a5(), a2);
+	}
+
+
+
+	/**
+	 * rel A.b6? <-> B.a6*;
+	 */
+	private void test6() {
+	}
+
+
+
+	/**
+	 * rel A.b7* <-> B.a7;
+	 */
+	private void test7() {
+	}
+
+
+
+	/**
+	 * rel A.b8* <-> B.a8?;
+	 */
+	private void test8() {
+	}
+
+
+
+	/**
+	 * rel A.b9* <-> B.a9*;
+	 */
+	private void test9() {
+	}
+
+
+
+	private void setup() {
+		r = new Root();
+		a1 = new A("a1");
+		a2 = new A("a2");
+		a3 = new A("a3");
+		b1 = new B("b1");
+		b2 = new B("b2");
+		b3 = new B("b3");
+
+		r.addA(a1);
+		r.addA(a2);
+		r.addA(a3);
+		r.addB(b1);
+		r.addB(b2);
+		r.addB(b3);
+	}
+
+	private void assertNull(Object obj) {
+		check(obj == null);
+	}
+	private void assertSame(Object o1, Object o2) {
+		check(o1 == o2);
+	}
+
+	private void check(boolean b) {
+		if (!b) {
+			throw new RuntimeException();
+		}
+
+	}
+}
\ No newline at end of file
diff --git a/test/Utils.jadd b/test/Utils.jadd
new file mode 100644
index 0000000..d7acb1b
--- /dev/null
+++ b/test/Utils.jadd
@@ -0,0 +1,9 @@
+aspect Utils {
+	public String A.toString() {
+		return getName();
+	}
+
+	public String B.toString() {
+		return getName();
+	}
+}
\ No newline at end of file
-- 
GitLab