From 91e8ea54169dbb21b01e700252ac3518d5622079 Mon Sep 17 00:00:00 2001 From: Fedor Date: Mon, 27 Sep 2021 23:01:42 +0300 Subject: [PATCH 01/14] Creating gradle structure --- lab-02-dependency-injection/build.gradle | 17 +++++++++++++++++ lab-02-dependency-injection/settings.gradle | 1 + .../src/main/java/com/_30something/DI/Main.java | 7 +++++++ .../com/_30something_/tests/DI/MainTests.java | 10 ++++++++++ 4 files changed, 35 insertions(+) create mode 100644 lab-02-dependency-injection/build.gradle create mode 100644 lab-02-dependency-injection/settings.gradle create mode 100644 lab-02-dependency-injection/src/main/java/com/_30something/DI/Main.java create mode 100644 lab-02-dependency-injection/src/test/java/com/_30something_/tests/DI/MainTests.java diff --git a/lab-02-dependency-injection/build.gradle b/lab-02-dependency-injection/build.gradle new file mode 100644 index 0000000..c80bc1b --- /dev/null +++ b/lab-02-dependency-injection/build.gradle @@ -0,0 +1,17 @@ +plugins { + id 'java-library' +} + +repositories { + mavenCentral() +} + +dependencies { + api 'javax.inject:javax.inject:1' + testImplementation 'org.junit.jupiter:junit-jupiter-api:5.8.0' + testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.8.0' +} + +test { + useJUnitPlatform() +} diff --git a/lab-02-dependency-injection/settings.gradle b/lab-02-dependency-injection/settings.gradle new file mode 100644 index 0000000..b68ca36 --- /dev/null +++ b/lab-02-dependency-injection/settings.gradle @@ -0,0 +1 @@ +rootProject.name = 'lab-02-dependency-injection' diff --git a/lab-02-dependency-injection/src/main/java/com/_30something/DI/Main.java b/lab-02-dependency-injection/src/main/java/com/_30something/DI/Main.java new file mode 100644 index 0000000..91c6735 --- /dev/null +++ b/lab-02-dependency-injection/src/main/java/com/_30something/DI/Main.java @@ -0,0 +1,7 @@ +package com._30something.DI; + +public class Main { + public static void main(String[] args) { + System.out.println("Hello world"); + } +} diff --git a/lab-02-dependency-injection/src/test/java/com/_30something_/tests/DI/MainTests.java b/lab-02-dependency-injection/src/test/java/com/_30something_/tests/DI/MainTests.java new file mode 100644 index 0000000..4d0a92d --- /dev/null +++ b/lab-02-dependency-injection/src/test/java/com/_30something_/tests/DI/MainTests.java @@ -0,0 +1,10 @@ +package com._30something_.tests.DI; + +import org.junit.jupiter.api.Test; + +public class MainTests { + @Test + public void testFirst() { + + } +} From 6462688f1a04922b131465a6f2d1b05a970c9777 Mon Sep 17 00:00:00 2001 From: Fedor Date: Sun, 10 Oct 2021 14:34:28 +0300 Subject: [PATCH 02/14] First stable version of DI created --- .../src/main/java/com/_30something/DI/DI.java | 104 ++++++++++++++++++ .../main/java/com/_30something/DI/Main.java | 2 +- .../com/_30something_/tests/DI/MainTests.java | 56 +++++++++- 3 files changed, 159 insertions(+), 3 deletions(-) create mode 100644 lab-02-dependency-injection/src/main/java/com/_30something/DI/DI.java diff --git a/lab-02-dependency-injection/src/main/java/com/_30something/DI/DI.java b/lab-02-dependency-injection/src/main/java/com/_30something/DI/DI.java new file mode 100644 index 0000000..13f8cd2 --- /dev/null +++ b/lab-02-dependency-injection/src/main/java/com/_30something/DI/DI.java @@ -0,0 +1,104 @@ +package com._30something.DI; + +import javax.inject.Inject; +import java.lang.reflect.*; +import java.util.*; + +public class DI { + private boolean registrationCompleted = false; + private final HashMap, Class> associatedImplementations = new HashMap<>(); + private final HashMap, Constructor> associatedConstructors = new HashMap<>(); + + public void registerClass(Class newClass) { + try { + if (registrationCompleted) { + throw new Exception("Registration completed for current DI"); + } + if (newClass.isInterface()) { + throw new Exception("Interface registered without implementation"); + } + if (associatedConstructors.containsKey(newClass)) { + throw new Exception("Double class registration"); + } + List> constructors_list = Arrays.stream(newClass.getDeclaredConstructors()).toList(); + int injectedConstructorsCounter = 0; + Constructor supposedConstructor = null; + for (Constructor constructor : constructors_list) { + if (constructor.isAnnotationPresent(Inject.class)) { + injectedConstructorsCounter++; + supposedConstructor = constructor; + } + } + if (injectedConstructorsCounter == 0) { + throw new Exception("Injected constructor of " + newClass + " not found"); + } + if (injectedConstructorsCounter > 1) { + throw new Exception("Multiple injected constructors found in " + newClass); + } + if (!Objects.equals(Modifier.toString(supposedConstructor.getModifiers()), "public")) { + throw new Exception("Supposed constructor of " + newClass + " must be public only"); + } + associatedConstructors.put(newClass, supposedConstructor); + } catch (Exception exception) { + exception.printStackTrace(); + } + } + + public void registerClass(Class newInterface, Class newImplementation) { + try { + if (newImplementation.isInterface()) { + throw new Exception("Attempt to register interface as implementation"); + } + if (!newInterface.isInterface()) { + throw new Exception("Attempt to register implementation for non-interface class"); + } + if (associatedImplementations.containsKey(newInterface)) { + throw new Exception("Attempt to register new implementation for interface"); + } + boolean interfaceImplemented = false; + for (Class currentInterface : Arrays.stream(newImplementation.getInterfaces()).toList()) { + if (currentInterface == newInterface) { + interfaceImplemented = true; + break; + } + } + if (!interfaceImplemented) { + throw new Exception("Implementation doesn't correspond to interface"); + } + registerClass(newImplementation); + associatedImplementations.put(newInterface, newImplementation); + } catch (Exception exception) { + exception.printStackTrace(); + } + } + + public void completeRegistration() { + registrationCompleted = true; + } + + public T resolveClass(Class newClass) { + try { + if (!registrationCompleted) { + throw new Exception("Registration isn't completed for current DI"); + } + if (!associatedConstructors.containsKey(newClass) && !associatedImplementations.containsKey(newClass)) { + throw new Exception("Requested class not found"); + } + if (newClass.isInterface()) { + Class implementation = associatedImplementations.get(newClass); + return newClass.cast(resolveClass(implementation)); + } + ArrayList createdInstances = new ArrayList<>(); + Constructor constructor = associatedConstructors.get(newClass); + for (Parameter parameter : constructor.getParameters()) { + Class argClass = parameter.getType(); + createdInstances.add(resolveClass(argClass)); + } + constructor.setAccessible(true); + return newClass.cast(constructor.newInstance(createdInstances.toArray())); + } catch (Exception exception) { + exception.printStackTrace(); + return null; + } + } +} diff --git a/lab-02-dependency-injection/src/main/java/com/_30something/DI/Main.java b/lab-02-dependency-injection/src/main/java/com/_30something/DI/Main.java index 91c6735..2a6b018 100644 --- a/lab-02-dependency-injection/src/main/java/com/_30something/DI/Main.java +++ b/lab-02-dependency-injection/src/main/java/com/_30something/DI/Main.java @@ -2,6 +2,6 @@ public class Main { public static void main(String[] args) { - System.out.println("Hello world"); + } } diff --git a/lab-02-dependency-injection/src/test/java/com/_30something_/tests/DI/MainTests.java b/lab-02-dependency-injection/src/test/java/com/_30something_/tests/DI/MainTests.java index 4d0a92d..13e7eda 100644 --- a/lab-02-dependency-injection/src/test/java/com/_30something_/tests/DI/MainTests.java +++ b/lab-02-dependency-injection/src/test/java/com/_30something_/tests/DI/MainTests.java @@ -1,10 +1,62 @@ package com._30something_.tests.DI; +import com._30something.DI.DI; +import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; +import javax.inject.Inject; + +class Car { + public final int speed; + + @Inject + public Car() { + this.speed = 0; + } + + public String start() { + return "Car makes beep"; + } +} + +class Plane { + public int getWeight() { + return weight; + } + + public final int weight; + + @Inject + public Plane(Car car) { + this.weight = 42; + } +} + +class Train { + public final int weight = 10; + public final int height; + + @Inject + public Train() { + this.height = 15; + } +} + public class MainTests { @Test - public void testFirst() { - + public void testSimpleFirst() { + DI myDi = new DI(); + myDi.registerClass(Car.class); + myDi.registerClass(Plane.class); + myDi.registerClass(Train.class); + myDi.completeRegistration(); + Car myCar = myDi.resolveClass(Car.class); + Plane myPlane = myDi.resolveClass(Plane.class); + Train myTrain = myDi.resolveClass(Train.class); + Assertions.assertNotNull(myCar); + Assertions.assertNotNull(myPlane); + Assertions.assertNotNull(myTrain); + Assertions.assertNotNull(myCar); + Assertions.assertEquals(myCar.start(), "Car makes beep"); } } From 21048f8a10136b4fc7fc7e9f6ebf4a3982b59fc3 Mon Sep 17 00:00:00 2001 From: Fedor Date: Sun, 10 Oct 2021 15:49:18 +0300 Subject: [PATCH 03/14] Checks to DI added & added support of singletons (expected) & tests templates created --- .../src/main/java/com/_30something/DI/DI.java | 31 ++++++++++++++++--- .../main/java/com/_30something/DI/Main.java | 7 ----- .../tests/DI/InterfacesTests.java | 5 +++ .../_30something_/tests/DI/MixedTests.java | 5 +++ .../DI/{MainTests.java => SimpleTests.java} | 11 ++++--- .../tests/DI/SingletonTests.java | 23 ++++++++++++++ 6 files changed, 67 insertions(+), 15 deletions(-) delete mode 100644 lab-02-dependency-injection/src/main/java/com/_30something/DI/Main.java create mode 100644 lab-02-dependency-injection/src/test/java/com/_30something_/tests/DI/InterfacesTests.java create mode 100644 lab-02-dependency-injection/src/test/java/com/_30something_/tests/DI/MixedTests.java rename lab-02-dependency-injection/src/test/java/com/_30something_/tests/DI/{MainTests.java => SimpleTests.java} (92%) create mode 100644 lab-02-dependency-injection/src/test/java/com/_30something_/tests/DI/SingletonTests.java diff --git a/lab-02-dependency-injection/src/main/java/com/_30something/DI/DI.java b/lab-02-dependency-injection/src/main/java/com/_30something/DI/DI.java index 13f8cd2..eed53be 100644 --- a/lab-02-dependency-injection/src/main/java/com/_30something/DI/DI.java +++ b/lab-02-dependency-injection/src/main/java/com/_30something/DI/DI.java @@ -1,6 +1,7 @@ package com._30something.DI; import javax.inject.Inject; +import javax.inject.Singleton; import java.lang.reflect.*; import java.util.*; @@ -8,6 +9,7 @@ public class DI { private boolean registrationCompleted = false; private final HashMap, Class> associatedImplementations = new HashMap<>(); private final HashMap, Constructor> associatedConstructors = new HashMap<>(); + private final HashMap, Object> singletonsInstances = new HashMap<>(); public void registerClass(Class newClass) { try { @@ -73,7 +75,22 @@ public void registerClass(Class newInterface, Class newImplementation) { } public void completeRegistration() { - registrationCompleted = true; + try { + for (Constructor constructor : associatedConstructors.values()) { + for (Parameter parameter : constructor.getParameters()) { + if (!associatedConstructors.containsKey(parameter.getType()) && + !associatedImplementations.containsKey(parameter.getType())) { + throw new Exception("Arguments of injected constructor " + constructor + " aren't registered"); + } + } + if (!constructor.isAnnotationPresent(Inject.class)) { + throw new Exception("Constructor " + constructor + " must be marked with @Inject"); + } + } + registrationCompleted = true; + } catch (Exception exception) { + exception.printStackTrace(); + } } public T resolveClass(Class newClass) { @@ -88,14 +105,20 @@ public T resolveClass(Class newClass) { Class implementation = associatedImplementations.get(newClass); return newClass.cast(resolveClass(implementation)); } + if (singletonsInstances.containsKey(newClass)) { + return newClass.cast(singletonsInstances.get(newClass)); + } ArrayList createdInstances = new ArrayList<>(); Constructor constructor = associatedConstructors.get(newClass); for (Parameter parameter : constructor.getParameters()) { - Class argClass = parameter.getType(); - createdInstances.add(resolveClass(argClass)); + createdInstances.add(resolveClass(parameter.getType())); } constructor.setAccessible(true); - return newClass.cast(constructor.newInstance(createdInstances.toArray())); + T newInstance = newClass.cast(constructor.newInstance(createdInstances.toArray())); + if (newClass.isAnnotationPresent(Singleton.class)) { + singletonsInstances.put(newClass, newInstance); + } + return newInstance; } catch (Exception exception) { exception.printStackTrace(); return null; diff --git a/lab-02-dependency-injection/src/main/java/com/_30something/DI/Main.java b/lab-02-dependency-injection/src/main/java/com/_30something/DI/Main.java deleted file mode 100644 index 2a6b018..0000000 --- a/lab-02-dependency-injection/src/main/java/com/_30something/DI/Main.java +++ /dev/null @@ -1,7 +0,0 @@ -package com._30something.DI; - -public class Main { - public static void main(String[] args) { - - } -} diff --git a/lab-02-dependency-injection/src/test/java/com/_30something_/tests/DI/InterfacesTests.java b/lab-02-dependency-injection/src/test/java/com/_30something_/tests/DI/InterfacesTests.java new file mode 100644 index 0000000..d5d9457 --- /dev/null +++ b/lab-02-dependency-injection/src/test/java/com/_30something_/tests/DI/InterfacesTests.java @@ -0,0 +1,5 @@ +package com._30something_.tests.DI; + +public class InterfacesTests { + +} diff --git a/lab-02-dependency-injection/src/test/java/com/_30something_/tests/DI/MixedTests.java b/lab-02-dependency-injection/src/test/java/com/_30something_/tests/DI/MixedTests.java new file mode 100644 index 0000000..3bf6b0e --- /dev/null +++ b/lab-02-dependency-injection/src/test/java/com/_30something_/tests/DI/MixedTests.java @@ -0,0 +1,5 @@ +package com._30something_.tests.DI; + +public class MixedTests { + +} diff --git a/lab-02-dependency-injection/src/test/java/com/_30something_/tests/DI/MainTests.java b/lab-02-dependency-injection/src/test/java/com/_30something_/tests/DI/SimpleTests.java similarity index 92% rename from lab-02-dependency-injection/src/test/java/com/_30something_/tests/DI/MainTests.java rename to lab-02-dependency-injection/src/test/java/com/_30something_/tests/DI/SimpleTests.java index 13e7eda..4417faa 100644 --- a/lab-02-dependency-injection/src/test/java/com/_30something_/tests/DI/MainTests.java +++ b/lab-02-dependency-injection/src/test/java/com/_30something_/tests/DI/SimpleTests.java @@ -3,7 +3,6 @@ import com._30something.DI.DI; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; - import javax.inject.Inject; class Car { @@ -27,7 +26,7 @@ public int getWeight() { public final int weight; @Inject - public Plane(Car car) { + public Plane() { this.weight = 42; } } @@ -42,7 +41,7 @@ public Train() { } } -public class MainTests { +public class SimpleTests { @Test public void testSimpleFirst() { DI myDi = new DI(); @@ -56,7 +55,11 @@ public void testSimpleFirst() { Assertions.assertNotNull(myCar); Assertions.assertNotNull(myPlane); Assertions.assertNotNull(myTrain); - Assertions.assertNotNull(myCar); Assertions.assertEquals(myCar.start(), "Car makes beep"); } + + @Test + public void testSimpleSecond() { + + } } diff --git a/lab-02-dependency-injection/src/test/java/com/_30something_/tests/DI/SingletonTests.java b/lab-02-dependency-injection/src/test/java/com/_30something_/tests/DI/SingletonTests.java new file mode 100644 index 0000000..456ef00 --- /dev/null +++ b/lab-02-dependency-injection/src/test/java/com/_30something_/tests/DI/SingletonTests.java @@ -0,0 +1,23 @@ +package com._30something_.tests.DI; + +import javax.inject.Inject; +import javax.inject.Singleton; + +@Singleton +class Factory { + public Car car; + public Plane plane; + public Train train; + public final Factory instance = new Factory(new Car(), new Train(), new Plane()); + + @Inject + public Factory(Car car, Train train, Plane plane) { + this.car = car; + this.train = train; + this.plane = plane; + } +} + +public class SingletonTests { + +} From 295188011abbcb563b87f91364af6bbf44af84ef Mon Sep 17 00:00:00 2001 From: Fedor Date: Sun, 10 Oct 2021 15:49:18 +0300 Subject: [PATCH 04/14] Checks to DI added & added support of singletons (expected) --- .../src/main/java/com/_30something/DI/DI.java | 31 ++++++++++++++++--- .../main/java/com/_30something/DI/Main.java | 7 ----- .../tests/DI/InterfacesTests.java | 5 +++ .../_30something_/tests/DI/MixedTests.java | 5 +++ .../DI/{MainTests.java => SimpleTests.java} | 11 ++++--- .../tests/DI/SingletonTests.java | 23 ++++++++++++++ 6 files changed, 67 insertions(+), 15 deletions(-) delete mode 100644 lab-02-dependency-injection/src/main/java/com/_30something/DI/Main.java create mode 100644 lab-02-dependency-injection/src/test/java/com/_30something_/tests/DI/InterfacesTests.java create mode 100644 lab-02-dependency-injection/src/test/java/com/_30something_/tests/DI/MixedTests.java rename lab-02-dependency-injection/src/test/java/com/_30something_/tests/DI/{MainTests.java => SimpleTests.java} (92%) create mode 100644 lab-02-dependency-injection/src/test/java/com/_30something_/tests/DI/SingletonTests.java diff --git a/lab-02-dependency-injection/src/main/java/com/_30something/DI/DI.java b/lab-02-dependency-injection/src/main/java/com/_30something/DI/DI.java index 13f8cd2..eed53be 100644 --- a/lab-02-dependency-injection/src/main/java/com/_30something/DI/DI.java +++ b/lab-02-dependency-injection/src/main/java/com/_30something/DI/DI.java @@ -1,6 +1,7 @@ package com._30something.DI; import javax.inject.Inject; +import javax.inject.Singleton; import java.lang.reflect.*; import java.util.*; @@ -8,6 +9,7 @@ public class DI { private boolean registrationCompleted = false; private final HashMap, Class> associatedImplementations = new HashMap<>(); private final HashMap, Constructor> associatedConstructors = new HashMap<>(); + private final HashMap, Object> singletonsInstances = new HashMap<>(); public void registerClass(Class newClass) { try { @@ -73,7 +75,22 @@ public void registerClass(Class newInterface, Class newImplementation) { } public void completeRegistration() { - registrationCompleted = true; + try { + for (Constructor constructor : associatedConstructors.values()) { + for (Parameter parameter : constructor.getParameters()) { + if (!associatedConstructors.containsKey(parameter.getType()) && + !associatedImplementations.containsKey(parameter.getType())) { + throw new Exception("Arguments of injected constructor " + constructor + " aren't registered"); + } + } + if (!constructor.isAnnotationPresent(Inject.class)) { + throw new Exception("Constructor " + constructor + " must be marked with @Inject"); + } + } + registrationCompleted = true; + } catch (Exception exception) { + exception.printStackTrace(); + } } public T resolveClass(Class newClass) { @@ -88,14 +105,20 @@ public T resolveClass(Class newClass) { Class implementation = associatedImplementations.get(newClass); return newClass.cast(resolveClass(implementation)); } + if (singletonsInstances.containsKey(newClass)) { + return newClass.cast(singletonsInstances.get(newClass)); + } ArrayList createdInstances = new ArrayList<>(); Constructor constructor = associatedConstructors.get(newClass); for (Parameter parameter : constructor.getParameters()) { - Class argClass = parameter.getType(); - createdInstances.add(resolveClass(argClass)); + createdInstances.add(resolveClass(parameter.getType())); } constructor.setAccessible(true); - return newClass.cast(constructor.newInstance(createdInstances.toArray())); + T newInstance = newClass.cast(constructor.newInstance(createdInstances.toArray())); + if (newClass.isAnnotationPresent(Singleton.class)) { + singletonsInstances.put(newClass, newInstance); + } + return newInstance; } catch (Exception exception) { exception.printStackTrace(); return null; diff --git a/lab-02-dependency-injection/src/main/java/com/_30something/DI/Main.java b/lab-02-dependency-injection/src/main/java/com/_30something/DI/Main.java deleted file mode 100644 index 2a6b018..0000000 --- a/lab-02-dependency-injection/src/main/java/com/_30something/DI/Main.java +++ /dev/null @@ -1,7 +0,0 @@ -package com._30something.DI; - -public class Main { - public static void main(String[] args) { - - } -} diff --git a/lab-02-dependency-injection/src/test/java/com/_30something_/tests/DI/InterfacesTests.java b/lab-02-dependency-injection/src/test/java/com/_30something_/tests/DI/InterfacesTests.java new file mode 100644 index 0000000..d5d9457 --- /dev/null +++ b/lab-02-dependency-injection/src/test/java/com/_30something_/tests/DI/InterfacesTests.java @@ -0,0 +1,5 @@ +package com._30something_.tests.DI; + +public class InterfacesTests { + +} diff --git a/lab-02-dependency-injection/src/test/java/com/_30something_/tests/DI/MixedTests.java b/lab-02-dependency-injection/src/test/java/com/_30something_/tests/DI/MixedTests.java new file mode 100644 index 0000000..3bf6b0e --- /dev/null +++ b/lab-02-dependency-injection/src/test/java/com/_30something_/tests/DI/MixedTests.java @@ -0,0 +1,5 @@ +package com._30something_.tests.DI; + +public class MixedTests { + +} diff --git a/lab-02-dependency-injection/src/test/java/com/_30something_/tests/DI/MainTests.java b/lab-02-dependency-injection/src/test/java/com/_30something_/tests/DI/SimpleTests.java similarity index 92% rename from lab-02-dependency-injection/src/test/java/com/_30something_/tests/DI/MainTests.java rename to lab-02-dependency-injection/src/test/java/com/_30something_/tests/DI/SimpleTests.java index 13e7eda..4417faa 100644 --- a/lab-02-dependency-injection/src/test/java/com/_30something_/tests/DI/MainTests.java +++ b/lab-02-dependency-injection/src/test/java/com/_30something_/tests/DI/SimpleTests.java @@ -3,7 +3,6 @@ import com._30something.DI.DI; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; - import javax.inject.Inject; class Car { @@ -27,7 +26,7 @@ public int getWeight() { public final int weight; @Inject - public Plane(Car car) { + public Plane() { this.weight = 42; } } @@ -42,7 +41,7 @@ public Train() { } } -public class MainTests { +public class SimpleTests { @Test public void testSimpleFirst() { DI myDi = new DI(); @@ -56,7 +55,11 @@ public void testSimpleFirst() { Assertions.assertNotNull(myCar); Assertions.assertNotNull(myPlane); Assertions.assertNotNull(myTrain); - Assertions.assertNotNull(myCar); Assertions.assertEquals(myCar.start(), "Car makes beep"); } + + @Test + public void testSimpleSecond() { + + } } diff --git a/lab-02-dependency-injection/src/test/java/com/_30something_/tests/DI/SingletonTests.java b/lab-02-dependency-injection/src/test/java/com/_30something_/tests/DI/SingletonTests.java new file mode 100644 index 0000000..456ef00 --- /dev/null +++ b/lab-02-dependency-injection/src/test/java/com/_30something_/tests/DI/SingletonTests.java @@ -0,0 +1,23 @@ +package com._30something_.tests.DI; + +import javax.inject.Inject; +import javax.inject.Singleton; + +@Singleton +class Factory { + public Car car; + public Plane plane; + public Train train; + public final Factory instance = new Factory(new Car(), new Train(), new Plane()); + + @Inject + public Factory(Car car, Train train, Plane plane) { + this.car = car; + this.train = train; + this.plane = plane; + } +} + +public class SingletonTests { + +} From e9aa24532a84e98492f73fc67fb415dc474e7a17 Mon Sep 17 00:00:00 2001 From: Fedor Date: Tue, 12 Oct 2021 22:44:18 +0300 Subject: [PATCH 05/14] Simple tests + exceptions added & DI updated --- .../DI/ClassRegistrationException.java | 7 + .../src/main/java/com/_30something/DI/DI.java | 183 ++++++++---------- .../DI/InterfaceRegistrationException.java | 7 + .../tests/DI/InterfacesTests.java | 10 + .../_30something_/tests/DI/SimpleTests.java | 162 +++++++++++++++- 5 files changed, 269 insertions(+), 100 deletions(-) create mode 100644 lab-02-dependency-injection/src/main/java/com/_30something/DI/ClassRegistrationException.java create mode 100644 lab-02-dependency-injection/src/main/java/com/_30something/DI/InterfaceRegistrationException.java diff --git a/lab-02-dependency-injection/src/main/java/com/_30something/DI/ClassRegistrationException.java b/lab-02-dependency-injection/src/main/java/com/_30something/DI/ClassRegistrationException.java new file mode 100644 index 0000000..a4083d6 --- /dev/null +++ b/lab-02-dependency-injection/src/main/java/com/_30something/DI/ClassRegistrationException.java @@ -0,0 +1,7 @@ +package com._30something.DI; + +public class ClassRegistrationException extends Exception { + public ClassRegistrationException(String message) { + super(message); + } +} diff --git a/lab-02-dependency-injection/src/main/java/com/_30something/DI/DI.java b/lab-02-dependency-injection/src/main/java/com/_30something/DI/DI.java index eed53be..36cd076 100644 --- a/lab-02-dependency-injection/src/main/java/com/_30something/DI/DI.java +++ b/lab-02-dependency-injection/src/main/java/com/_30something/DI/DI.java @@ -1,5 +1,6 @@ package com._30something.DI; +import java.security.AccessControlException; import javax.inject.Inject; import javax.inject.Singleton; import java.lang.reflect.*; @@ -11,117 +12,103 @@ public class DI { private final HashMap, Constructor> associatedConstructors = new HashMap<>(); private final HashMap, Object> singletonsInstances = new HashMap<>(); - public void registerClass(Class newClass) { - try { - if (registrationCompleted) { - throw new Exception("Registration completed for current DI"); - } - if (newClass.isInterface()) { - throw new Exception("Interface registered without implementation"); - } - if (associatedConstructors.containsKey(newClass)) { - throw new Exception("Double class registration"); - } - List> constructors_list = Arrays.stream(newClass.getDeclaredConstructors()).toList(); - int injectedConstructorsCounter = 0; - Constructor supposedConstructor = null; - for (Constructor constructor : constructors_list) { - if (constructor.isAnnotationPresent(Inject.class)) { - injectedConstructorsCounter++; - supposedConstructor = constructor; - } - } - if (injectedConstructorsCounter == 0) { - throw new Exception("Injected constructor of " + newClass + " not found"); - } - if (injectedConstructorsCounter > 1) { - throw new Exception("Multiple injected constructors found in " + newClass); - } - if (!Objects.equals(Modifier.toString(supposedConstructor.getModifiers()), "public")) { - throw new Exception("Supposed constructor of " + newClass + " must be public only"); + public void registerClass(Class newClass) throws InterfaceRegistrationException, ClassRegistrationException { + if (registrationCompleted) { + throw new AccessControlException("Registration completed for current DI"); + } + if (newClass.isInterface()) { + throw new InterfaceRegistrationException("Interface registered without implementation"); + } + if (associatedConstructors.containsKey(newClass)) { + throw new ClassRegistrationException("Double class registration"); + } + List> constructors_list = Arrays.stream(newClass.getDeclaredConstructors()).toList(); + int injectedConstructorsCounter = 0; + Constructor supposedConstructor = null; + for (Constructor constructor : constructors_list) { + if (constructor.isAnnotationPresent(Inject.class)) { + injectedConstructorsCounter++; + supposedConstructor = constructor; } - associatedConstructors.put(newClass, supposedConstructor); - } catch (Exception exception) { - exception.printStackTrace(); } + if (injectedConstructorsCounter == 0) { + throw new ClassRegistrationException("Injected constructor of " + newClass + " not found"); + } + if (injectedConstructorsCounter > 1) { + throw new ClassRegistrationException("Multiple injected constructors found in " + newClass); + } + if (!Objects.equals(Modifier.toString(supposedConstructor.getModifiers()), "public")) { + throw new ClassRegistrationException("Supposed constructor of " + newClass + " must be public only"); + } + associatedConstructors.put(newClass, supposedConstructor); } - public void registerClass(Class newInterface, Class newImplementation) { - try { - if (newImplementation.isInterface()) { - throw new Exception("Attempt to register interface as implementation"); - } - if (!newInterface.isInterface()) { - throw new Exception("Attempt to register implementation for non-interface class"); - } - if (associatedImplementations.containsKey(newInterface)) { - throw new Exception("Attempt to register new implementation for interface"); - } - boolean interfaceImplemented = false; - for (Class currentInterface : Arrays.stream(newImplementation.getInterfaces()).toList()) { - if (currentInterface == newInterface) { - interfaceImplemented = true; - break; - } - } - if (!interfaceImplemented) { - throw new Exception("Implementation doesn't correspond to interface"); + public void registerClass(Class newInterface, Class newImplementation) + throws InterfaceRegistrationException, ClassRegistrationException { + if (newImplementation.isInterface()) { + throw new InterfaceRegistrationException("Attempt to register interface as implementation"); + } + if (!newInterface.isInterface()) { + throw new InterfaceRegistrationException("Attempt to register implementation for non-interface class"); + } + if (associatedImplementations.containsKey(newInterface)) { + throw new InterfaceRegistrationException("Attempt to register new implementation for interface"); + } + boolean interfaceImplemented = false; + for (Class currentInterface : Arrays.stream(newImplementation.getInterfaces()).toList()) { + if (currentInterface == newInterface) { + interfaceImplemented = true; + break; } - registerClass(newImplementation); - associatedImplementations.put(newInterface, newImplementation); - } catch (Exception exception) { - exception.printStackTrace(); } + if (!interfaceImplemented) { + throw new InterfaceRegistrationException("Implementation doesn't correspond to interface"); + } + registerClass(newImplementation); + associatedImplementations.put(newInterface, newImplementation); } - public void completeRegistration() { - try { - for (Constructor constructor : associatedConstructors.values()) { - for (Parameter parameter : constructor.getParameters()) { - if (!associatedConstructors.containsKey(parameter.getType()) && - !associatedImplementations.containsKey(parameter.getType())) { - throw new Exception("Arguments of injected constructor " + constructor + " aren't registered"); - } - } - if (!constructor.isAnnotationPresent(Inject.class)) { - throw new Exception("Constructor " + constructor + " must be marked with @Inject"); + public void completeRegistration() throws ClassRegistrationException { + for (Constructor constructor : associatedConstructors.values()) { + for (Parameter parameter : constructor.getParameters()) { + if (!associatedConstructors.containsKey(parameter.getType()) && + !associatedImplementations.containsKey(parameter.getType())) { + throw new ClassRegistrationException( + "Arguments of injected constructor " + constructor + " aren't registered"); } } - registrationCompleted = true; - } catch (Exception exception) { - exception.printStackTrace(); + if (!constructor.isAnnotationPresent(Inject.class)) { + throw new ClassRegistrationException("Constructor " + constructor + " must be marked with @Inject"); + } } + registrationCompleted = true; } - public T resolveClass(Class newClass) { - try { - if (!registrationCompleted) { - throw new Exception("Registration isn't completed for current DI"); - } - if (!associatedConstructors.containsKey(newClass) && !associatedImplementations.containsKey(newClass)) { - throw new Exception("Requested class not found"); - } - if (newClass.isInterface()) { - Class implementation = associatedImplementations.get(newClass); - return newClass.cast(resolveClass(implementation)); - } - if (singletonsInstances.containsKey(newClass)) { - return newClass.cast(singletonsInstances.get(newClass)); - } - ArrayList createdInstances = new ArrayList<>(); - Constructor constructor = associatedConstructors.get(newClass); - for (Parameter parameter : constructor.getParameters()) { - createdInstances.add(resolveClass(parameter.getType())); - } - constructor.setAccessible(true); - T newInstance = newClass.cast(constructor.newInstance(createdInstances.toArray())); - if (newClass.isAnnotationPresent(Singleton.class)) { - singletonsInstances.put(newClass, newInstance); - } - return newInstance; - } catch (Exception exception) { - exception.printStackTrace(); - return null; + public T resolveClass(Class newClass) throws ClassNotFoundException, InvocationTargetException, + InstantiationException, IllegalAccessException { + if (!registrationCompleted) { + throw new AccessControlException("Registration isn't completed for current DI"); + } + if (!associatedConstructors.containsKey(newClass) && !associatedImplementations.containsKey(newClass)) { + throw new ClassNotFoundException("Requested class not found"); + } + if (newClass.isInterface()) { + Class implementation = associatedImplementations.get(newClass); + return newClass.cast(resolveClass(implementation)); + } + if (singletonsInstances.containsKey(newClass)) { + return newClass.cast(singletonsInstances.get(newClass)); + } + ArrayList createdInstances = new ArrayList<>(); + Constructor constructor = associatedConstructors.get(newClass); + for (Parameter parameter : constructor.getParameters()) { + createdInstances.add(resolveClass(parameter.getType())); + } + constructor.setAccessible(true); + T newInstance = newClass.cast(constructor.newInstance(createdInstances.toArray())); + if (newClass.isAnnotationPresent(Singleton.class)) { + singletonsInstances.put(newClass, newInstance); } + return newInstance; } } diff --git a/lab-02-dependency-injection/src/main/java/com/_30something/DI/InterfaceRegistrationException.java b/lab-02-dependency-injection/src/main/java/com/_30something/DI/InterfaceRegistrationException.java new file mode 100644 index 0000000..3e9d557 --- /dev/null +++ b/lab-02-dependency-injection/src/main/java/com/_30something/DI/InterfaceRegistrationException.java @@ -0,0 +1,7 @@ +package com._30something.DI; + +public class InterfaceRegistrationException extends Exception { + public InterfaceRegistrationException(String message) { + super(message); + } +} diff --git a/lab-02-dependency-injection/src/test/java/com/_30something_/tests/DI/InterfacesTests.java b/lab-02-dependency-injection/src/test/java/com/_30something_/tests/DI/InterfacesTests.java index d5d9457..9fb369c 100644 --- a/lab-02-dependency-injection/src/test/java/com/_30something_/tests/DI/InterfacesTests.java +++ b/lab-02-dependency-injection/src/test/java/com/_30something_/tests/DI/InterfacesTests.java @@ -1,5 +1,15 @@ package com._30something_.tests.DI; +import com._30something.DI.ClassRegistrationException; +import com._30something.DI.InterfaceRegistrationException; +import org.junit.jupiter.api.Test; + +import java.lang.reflect.InvocationTargetException; + public class InterfacesTests { + @Test + public void InterfacesTestsMain() throws ClassRegistrationException, InterfaceRegistrationException, + ClassNotFoundException, InvocationTargetException, InstantiationException, IllegalAccessException { + } } diff --git a/lab-02-dependency-injection/src/test/java/com/_30something_/tests/DI/SimpleTests.java b/lab-02-dependency-injection/src/test/java/com/_30something_/tests/DI/SimpleTests.java index 4417faa..bec91e0 100644 --- a/lab-02-dependency-injection/src/test/java/com/_30something_/tests/DI/SimpleTests.java +++ b/lab-02-dependency-injection/src/test/java/com/_30something_/tests/DI/SimpleTests.java @@ -1,5 +1,10 @@ package com._30something_.tests.DI; +import com._30something.DI.ClassRegistrationException; +import com._30something.DI.InterfaceRegistrationException; +import java.lang.reflect.InvocationTargetException; +import java.security.AccessControlException; + import com._30something.DI.DI; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; @@ -41,9 +46,88 @@ public Train() { } } +class Bicycle { + public Bicycle() {} +} + +class Bicycle1 { + @Inject + private Bicycle1() {} +} + +class Bicycle2 { + private final Car supportCar; + + @Inject + public Bicycle2(Car supportCar) { + this.supportCar = supportCar; + } + + public Car getSupportCar() { + return supportCar; + } +} + +class Bicycle3 { + public final String type; + public final int model; + + @Inject + public Bicycle3(int model) { + this.model = model; + type = "Standard"; + } + + @Inject + public Bicycle3(String type, int model) { + this.type = type; + this.model = model; + } +} + +class Bicycle4 extends Bicycle2 { + @Inject + public Bicycle4(Car supportCar) { + super(supportCar); + } +} + +class Bicycle5 { + @Inject + public Integer gearsNumber; + + @Inject + public Bicycle5() { + gearsNumber = 10; + } +} + +class Bicycle6 { + @Inject + public Bicycle6() { + System.out.println("Bicycle no. 6 created ^_^"); + } +} + +class BicyclesCollection { + public Bicycle6 bicycle6; + public Bicycle5 bicycle5; + public Bicycle4 bicycle4; + public Bicycle2 bicycle2; + + @Inject + public BicyclesCollection(Bicycle6 bicycle6, Bicycle5 bicycle5, Bicycle4 bicycle4, Bicycle2 bicycle2) { + this.bicycle6 = bicycle6; + this.bicycle5 = bicycle5; + this.bicycle4 = bicycle4; + this.bicycle2 = bicycle2; + } +} + public class SimpleTests { @Test - public void testSimpleFirst() { + public void testSimpleFirst() throws ClassRegistrationException, InterfaceRegistrationException, + ClassNotFoundException, InvocationTargetException, InstantiationException, IllegalAccessException { DI myDi = new DI(); myDi.registerClass(Car.class); myDi.registerClass(Plane.class); @@ -56,10 +140,84 @@ public void testSimpleFirst() { Assertions.assertNotNull(myPlane); Assertions.assertNotNull(myTrain); Assertions.assertEquals(myCar.start(), "Car makes beep"); + Assertions.assertEquals(myPlane.getWeight(), 42); + Assertions.assertEquals(myTrain.weight, 10); } @Test - public void testSimpleSecond() { + public void testSimpleSecond() throws ClassRegistrationException, InterfaceRegistrationException, + ClassNotFoundException, InvocationTargetException, InstantiationException, IllegalAccessException { + DI myDi = new DI(); + myDi.registerClass(Car.class); + Car newCar = new Car(); + Assertions.assertThrows(ClassRegistrationException.class, () -> myDi.registerClass(Car.class)); + Assertions.assertThrows(ClassRegistrationException.class, () -> myDi.registerClass(Bicycle.class)); + Assertions.assertThrows(ClassRegistrationException.class, () -> myDi.registerClass(Bicycle1.class)); + Assertions.assertThrows(ClassRegistrationException.class, () -> myDi.registerClass(Bicycle3.class)); + Bicycle2 myBicycle = new Bicycle2(newCar); + Bicycle3 myStandardBicycle = new Bicycle3(42); + Bicycle3 myNewStandardBicycle = new Bicycle3("Cool", 42); + myDi.registerClass(Bicycle2.class); + Assertions.assertThrows(ClassRegistrationException.class, () -> myDi.registerClass(Bicycle3.class)); + Assertions.assertThrows(AccessControlException.class, () -> myDi.resolveClass(Car.class)); + myDi.completeRegistration(); + Assertions.assertThrows(AccessControlException.class, () -> myDi.registerClass(Car.class)); + Assertions.assertThrows(AccessControlException.class, () -> myDi.registerClass(Plane.class)); + Assertions.assertThrows(ClassNotFoundException.class, () -> myDi.resolveClass(Plane.class)); + Assertions.assertThrows(ClassNotFoundException.class, () -> myDi.resolveClass(Train.class)); + Bicycle2 newMyBicycle = myDi.resolveClass(Bicycle2.class); + Assertions.assertNotNull(newMyBicycle); + Assertions.assertEquals(myBicycle.getSupportCar(), newCar); + Assertions.assertNotNull(newMyBicycle.getSupportCar()); + Assertions.assertEquals(myStandardBicycle.model, myNewStandardBicycle.model); + Assertions.assertThrows(ClassNotFoundException.class, () -> myDi.resolveClass(Bicycle3.class)); + Assertions.assertEquals(myStandardBicycle.model, myNewStandardBicycle.model); + } + @Test + public void testSimpleThird() throws ClassRegistrationException, InterfaceRegistrationException, + ClassNotFoundException, InvocationTargetException, InstantiationException, IllegalAccessException { + DI firstDi = new DI(); + firstDi.registerClass(Bicycle2.class); + firstDi.registerClass(Plane.class); + firstDi.registerClass(Train.class); + Assertions.assertThrows(ClassRegistrationException.class, firstDi::completeRegistration); + Assertions.assertThrows(AccessControlException.class, () -> firstDi.resolveClass(Car.class)); + Assertions.assertThrows(AccessControlException.class, () -> firstDi.resolveClass(Bicycle2.class)); + Assertions.assertThrows(ClassRegistrationException.class, () -> firstDi.registerClass(Train.class)); + Assertions.assertThrows(ClassRegistrationException.class, () -> firstDi.registerClass(Plane.class)); + firstDi.registerClass(Car.class); + firstDi.completeRegistration(); + Assertions.assertThrows(AccessControlException.class, () -> firstDi.registerClass(Car.class)); + Assertions.assertThrows(AccessControlException.class, () -> firstDi.registerClass(Bicycle.class)); + Assertions.assertThrows(ClassNotFoundException.class, () -> firstDi.resolveClass(Bicycle.class)); + Assertions.assertThrows(AccessControlException.class, () -> firstDi.registerClass(Train.class)); + Bicycle2 newBicycle = new Bicycle2(firstDi.resolveClass(Car.class)); + Assertions.assertNotNull(newBicycle); + Assertions.assertEquals(newBicycle.getSupportCar().getClass(), Car.class); + DI secondDi = new DI(); + secondDi.registerClass(Bicycle4.class); + Assertions.assertThrows(ClassRegistrationException.class, () -> secondDi.registerClass(Bicycle4.class)); + Assertions.assertThrows(AccessControlException.class, () -> secondDi.resolveClass(Bicycle4.class)); + secondDi.registerClass(BicyclesCollection.class); + Assertions.assertThrows(ClassRegistrationException.class, secondDi::completeRegistration); + secondDi.registerClass(Bicycle6.class); + Assertions.assertThrows(ClassRegistrationException.class, secondDi::completeRegistration); + secondDi.registerClass(Bicycle5.class); + Assertions.assertThrows(ClassRegistrationException.class, secondDi::completeRegistration); + secondDi.registerClass(Bicycle2.class); + Assertions.assertThrows(ClassRegistrationException.class, () -> secondDi.registerClass(Bicycle4.class)); + Assertions.assertThrows(ClassRegistrationException.class, secondDi::completeRegistration); + secondDi.registerClass(Car.class); + secondDi.completeRegistration(); + BicyclesCollection collection = secondDi.resolveClass(BicyclesCollection.class); + Assertions.assertNotNull(collection); + collection.bicycle2 = secondDi.resolveClass(Bicycle2.class); + Assertions.assertNotNull(collection.bicycle2); + Assertions.assertEquals(collection.bicycle2.getSupportCar().speed, 0); + collection.bicycle6 = secondDi.resolveClass(Bicycle6.class); + Assertions.assertThrows(ClassNotFoundException.class, () -> secondDi.resolveClass(Bicycle3.class)); + collection.bicycle5 = secondDi.resolveClass(Bicycle5.class); + Assertions.assertEquals(collection.bicycle5.gearsNumber, 10); } } From 3c6c83e01ad4312dfdbb50e75010bd5f734eb623 Mon Sep 17 00:00:00 2001 From: Fedor Date: Wed, 13 Oct 2021 10:28:20 +0300 Subject: [PATCH 06/14] DI fix & some test sets added --- .../src/main/java/com/_30something/DI/DI.java | 17 +- .../tests/DI/InterfacesTests.java | 147 +++++++++++++++++- .../_30something_/tests/DI/MixedTests.java | 19 +++ .../tests/DI/SingletonTests.java | 91 ++++++++++- 4 files changed, 255 insertions(+), 19 deletions(-) diff --git a/lab-02-dependency-injection/src/main/java/com/_30something/DI/DI.java b/lab-02-dependency-injection/src/main/java/com/_30something/DI/DI.java index 36cd076..b15659c 100644 --- a/lab-02-dependency-injection/src/main/java/com/_30something/DI/DI.java +++ b/lab-02-dependency-injection/src/main/java/com/_30something/DI/DI.java @@ -1,6 +1,7 @@ package com._30something.DI; import java.security.AccessControlException; + import javax.inject.Inject; import javax.inject.Singleton; import java.lang.reflect.*; @@ -37,7 +38,8 @@ public void registerClass(Class newClass) throws InterfaceRegistrationExcepti if (injectedConstructorsCounter > 1) { throw new ClassRegistrationException("Multiple injected constructors found in " + newClass); } - if (!Objects.equals(Modifier.toString(supposedConstructor.getModifiers()), "public")) { + if (!newClass.isAnnotationPresent(Singleton.class) && + !Objects.equals(Modifier.toString(supposedConstructor.getModifiers()), "public")) { throw new ClassRegistrationException("Supposed constructor of " + newClass + " must be public only"); } associatedConstructors.put(newClass, supposedConstructor); @@ -54,17 +56,12 @@ public void registerClass(Class newInterface, Class newImplementation) if (associatedImplementations.containsKey(newInterface)) { throw new InterfaceRegistrationException("Attempt to register new implementation for interface"); } - boolean interfaceImplemented = false; - for (Class currentInterface : Arrays.stream(newImplementation.getInterfaces()).toList()) { - if (currentInterface == newInterface) { - interfaceImplemented = true; - break; - } - } - if (!interfaceImplemented) { + if (!Arrays.stream(newImplementation.getInterfaces()).toList().contains(newInterface)) { throw new InterfaceRegistrationException("Implementation doesn't correspond to interface"); } - registerClass(newImplementation); + if (!associatedConstructors.containsKey(newImplementation)) { + registerClass(newImplementation); + } associatedImplementations.put(newInterface, newImplementation); } diff --git a/lab-02-dependency-injection/src/test/java/com/_30something_/tests/DI/InterfacesTests.java b/lab-02-dependency-injection/src/test/java/com/_30something_/tests/DI/InterfacesTests.java index 9fb369c..f031a51 100644 --- a/lab-02-dependency-injection/src/test/java/com/_30something_/tests/DI/InterfacesTests.java +++ b/lab-02-dependency-injection/src/test/java/com/_30something_/tests/DI/InterfacesTests.java @@ -2,14 +2,155 @@ import com._30something.DI.ClassRegistrationException; import com._30something.DI.InterfaceRegistrationException; +import java.lang.reflect.InvocationTargetException; +import java.security.AccessControlException; + +import com._30something.DI.DI; +import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; +import javax.inject.Inject; -import java.lang.reflect.InvocationTargetException; +interface Graph { + Integer getSize(); + void add(); +} + +interface Fake { + Integer getSize(); + void add(); +} + +class Tree implements Graph { + @Inject + public Integer size = 0; + @Inject + public String info; + + @Inject + public Tree() { + info = "Tree"; + } + + @Override + public Integer getSize() { + return size; + } + + @Override + public void add() { + size++; + } +} + +class SubTree { + public String newInfo; + + @Inject + public SubTree(Tree parentTree) { + newInfo = parentTree.toString(); + } +} + +class Path implements Graph { + @Inject + public Integer size = 0; + @Inject + public String info; + + @Inject + public Path() { + info = "Path"; + } + + @Override + public Integer getSize() { + return size; + } + + @Override + public void add() { + size++; + } +} + +class Cactus implements Graph { + @Inject + public Integer size = 0; + @Inject + public String info; + + public Cactus() { + info = "Cactus"; + } + + @Override + public Integer getSize() { + return size; + } + + @Override + public void add() { + size++; + } +} + +class Node { + @Inject + public Integer data; + + @Inject + public Node() { + data = 42; + } +} public class InterfacesTests { @Test - public void InterfacesTestsMain() throws ClassRegistrationException, InterfaceRegistrationException, + public void interfacesTestsMain() throws ClassRegistrationException, InterfaceRegistrationException, ClassNotFoundException, InvocationTargetException, InstantiationException, IllegalAccessException { - + DI interfacesDi = new DI(); + Assertions.assertThrows(InterfaceRegistrationException.class, () -> interfacesDi.registerClass(Graph.class)); + Assertions.assertThrows(InterfaceRegistrationException.class, () -> interfacesDi.registerClass(Fake.class)); + Assertions.assertThrows(InterfaceRegistrationException.class, + () -> interfacesDi.registerClass(Graph.class, Graph.class)); + Assertions.assertThrows(InterfaceRegistrationException.class, + () -> interfacesDi.registerClass(Tree.class, Path.class)); + Assertions.assertThrows(InterfaceRegistrationException.class, + () -> interfacesDi.registerClass(Tree.class, Graph.class)); + Assertions.assertThrows(ClassRegistrationException.class, + () -> interfacesDi.registerClass(Graph.class, Cactus.class)); + Assertions.assertThrows(InterfaceRegistrationException.class, + () -> interfacesDi.registerClass(Graph.class, Fake.class)); + Assertions.assertThrows(InterfaceRegistrationException.class, + () -> interfacesDi.registerClass(Fake.class, Tree.class)); + interfacesDi.registerClass(Tree.class); + interfacesDi.registerClass(Graph.class, Tree.class); + Assertions.assertThrows(InterfaceRegistrationException.class, + () -> interfacesDi.registerClass(Graph.class, Path.class)); + interfacesDi.registerClass(Path.class); + Assertions.assertThrows(AccessControlException.class, () -> interfacesDi.resolveClass(Tree.class)); + Assertions.assertThrows(AccessControlException.class, () -> interfacesDi.resolveClass(Graph.class)); + Assertions.assertThrows(AccessControlException.class, () -> interfacesDi.resolveClass(Path.class)); + interfacesDi.registerClass(Node.class); + interfacesDi.registerClass(SubTree.class); + interfacesDi.completeRegistration(); + Assertions.assertThrows(ClassNotFoundException.class, () -> interfacesDi.resolveClass(Fake.class)); + Assertions.assertDoesNotThrow(() -> { + Tree myTree = interfacesDi.resolveClass(Tree.class); + Tree newTree = (Tree) interfacesDi.resolveClass(Graph.class); + Assertions.assertNotNull(myTree); + Assertions.assertNotNull(newTree); + Assertions.assertEquals(myTree.getClass(), Tree.class); + Assertions.assertEquals(newTree.getClass(), Tree.class); + Assertions.assertEquals(myTree.size, 0); + Assertions.assertEquals(myTree.info, "Tree"); + Assertions.assertEquals(newTree.size, 0); + Assertions.assertEquals(newTree.info, "Tree"); + }); + Assertions.assertThrows(AccessControlException.class, () -> interfacesDi.registerClass(Cactus.class)); + Assertions.assertThrows(ClassNotFoundException.class, () -> interfacesDi.resolveClass(Cactus.class)); + Assertions.assertEquals(interfacesDi.resolveClass(Node.class).data, 42); + SubTree mySubtree = interfacesDi.resolveClass(SubTree.class); + Assertions.assertNotNull(mySubtree); } } diff --git a/lab-02-dependency-injection/src/test/java/com/_30something_/tests/DI/MixedTests.java b/lab-02-dependency-injection/src/test/java/com/_30something_/tests/DI/MixedTests.java index 3bf6b0e..a6a95b1 100644 --- a/lab-02-dependency-injection/src/test/java/com/_30something_/tests/DI/MixedTests.java +++ b/lab-02-dependency-injection/src/test/java/com/_30something_/tests/DI/MixedTests.java @@ -1,5 +1,24 @@ package com._30something_.tests.DI; +import com._30something.DI.ClassRegistrationException; +import com._30something.DI.InterfaceRegistrationException; +import java.lang.reflect.InvocationTargetException; + +import com._30something.DI.DI; +import org.junit.jupiter.api.Test; + public class MixedTests { + @Test + public void mixedTestsFirst() throws ClassRegistrationException, InterfaceRegistrationException, + ClassNotFoundException, InvocationTargetException, InstantiationException, IllegalAccessException { + DI myDi = new DI(); + + } + + @Test + public void mixedTestsSecond() throws ClassRegistrationException, InterfaceRegistrationException, + ClassNotFoundException, InvocationTargetException, InstantiationException, IllegalAccessException { + DI myDi = new DI(); + } } diff --git a/lab-02-dependency-injection/src/test/java/com/_30something_/tests/DI/SingletonTests.java b/lab-02-dependency-injection/src/test/java/com/_30something_/tests/DI/SingletonTests.java index 456ef00..4426bbe 100644 --- a/lab-02-dependency-injection/src/test/java/com/_30something_/tests/DI/SingletonTests.java +++ b/lab-02-dependency-injection/src/test/java/com/_30something_/tests/DI/SingletonTests.java @@ -1,23 +1,102 @@ package com._30something_.tests.DI; +import com._30something.DI.ClassRegistrationException; +import com._30something.DI.InterfaceRegistrationException; +import java.lang.reflect.InvocationTargetException; +import java.security.AccessControlException; + +import com._30something.DI.DI; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; import javax.inject.Inject; import javax.inject.Singleton; @Singleton class Factory { - public Car car; - public Plane plane; - public Train train; - public final Factory instance = new Factory(new Car(), new Train(), new Plane()); + private final Car car; + private final Plane plane; + private final Train train; + private final Bicycle5 bicycle; + private final Bus bus; + public static final Factory instance = new Factory( + new Car(), new Train(), new Plane(), new Bicycle5(), Bus.instance); @Inject - public Factory(Car car, Train train, Plane plane) { + private Factory(Car car, Train train, Plane plane, Bicycle5 bicycle5, Bus bus) { this.car = car; this.train = train; this.plane = plane; + this.bicycle = bicycle5; + this.bus = bus; + } + + public Car getCar() { + return car; + } + + public Bus getBus() { + return bus; } } -public class SingletonTests { +@Singleton +class Bus { + @Inject + private Integer capacity; + public static final Bus instance = new Bus(); + @Inject + private Bus() { + this.capacity = 50; + } +} + +public class SingletonTests { + @Test + public void singletonTestsMain() throws ClassRegistrationException, InterfaceRegistrationException, + ClassNotFoundException, InvocationTargetException, InstantiationException, IllegalAccessException { + DI singletonDI = new DI(); + singletonDI.registerClass(Car.class); + singletonDI.registerClass(Plane.class); + singletonDI.registerClass(Train.class); + singletonDI.registerClass(Bicycle5.class); + singletonDI.registerClass(Factory.class); + Assertions.assertThrows(ClassRegistrationException.class, () -> singletonDI.registerClass(Factory.class)); + Assertions.assertThrows(ClassRegistrationException.class, singletonDI::completeRegistration); + singletonDI.registerClass(Bus.class); + singletonDI.completeRegistration(); + Assertions.assertThrows(AccessControlException.class, () -> singletonDI.registerClass(Factory.class)); + Car myCar1 = singletonDI.resolveClass(Car.class); + Car myCar2 = singletonDI.resolveClass(Car.class); + Assertions.assertNotNull(myCar1); + Assertions.assertNotNull(myCar2); + Assertions.assertNotEquals(myCar1, myCar2); + Factory factory = singletonDI.resolveClass(Factory.class); + Factory newFactory = singletonDI.resolveClass(Factory.class); + Assertions.assertNotNull(factory); + Assertions.assertNotNull(newFactory); + Assertions.assertNotNull(Factory.instance); + Assertions.assertEquals(factory, newFactory); + Car myFactoryCar1 = factory.getCar(); + Car myFactoryCar2 = factory.getCar(); + Car myFactoryCar3 = newFactory.getCar(); + Assertions.assertNotNull(myFactoryCar1); + Assertions.assertNotNull(myFactoryCar2); + Assertions.assertNotNull(myFactoryCar3); + Assertions.assertEquals(myFactoryCar1, myFactoryCar2); + Assertions.assertEquals(myFactoryCar1, myFactoryCar3); + Assertions.assertEquals(myFactoryCar2, myFactoryCar2); + Assertions.assertThrows(AccessControlException.class, () -> singletonDI.registerClass(Bicycle.class)); + Assertions.assertThrows(ClassNotFoundException.class, () -> singletonDI.resolveClass(Bicycle.class)); + Bus bus1 = factory.getBus(); + Bus bus2 = factory.getBus(); + Assertions.assertNotNull(bus1); + Assertions.assertNotNull(bus2); + Assertions.assertEquals(bus1, bus2); + Bus bus3 = singletonDI.resolveClass(Bus.class); + Bus bus4 = singletonDI.resolveClass(Bus.class); + Assertions.assertNotNull(bus3); + Assertions.assertNotNull(bus4); + Assertions.assertEquals(bus3, bus4); + } } From 3ea4ad80f777a9b22413d1a7a2b847335713a273 Mon Sep 17 00:00:00 2001 From: Fedor Date: Wed, 13 Oct 2021 18:53:32 +0300 Subject: [PATCH 07/14] Tests & DI completed --- .../src/main/java/com/_30something/DI/DI.java | 6 + .../_30something_/tests/DI/MixedTests.java | 123 +++++++++++++++++- 2 files changed, 127 insertions(+), 2 deletions(-) diff --git a/lab-02-dependency-injection/src/main/java/com/_30something/DI/DI.java b/lab-02-dependency-injection/src/main/java/com/_30something/DI/DI.java index b15659c..a6d7c6f 100644 --- a/lab-02-dependency-injection/src/main/java/com/_30something/DI/DI.java +++ b/lab-02-dependency-injection/src/main/java/com/_30something/DI/DI.java @@ -47,6 +47,9 @@ public void registerClass(Class newClass) throws InterfaceRegistrationExcepti public void registerClass(Class newInterface, Class newImplementation) throws InterfaceRegistrationException, ClassRegistrationException { + if (registrationCompleted) { + throw new AccessControlException("Registration completed for current DI"); + } if (newImplementation.isInterface()) { throw new InterfaceRegistrationException("Attempt to register interface as implementation"); } @@ -66,6 +69,9 @@ public void registerClass(Class newInterface, Class newImplementation) } public void completeRegistration() throws ClassRegistrationException { + if (registrationCompleted) { + return; + } for (Constructor constructor : associatedConstructors.values()) { for (Parameter parameter : constructor.getParameters()) { if (!associatedConstructors.containsKey(parameter.getType()) && diff --git a/lab-02-dependency-injection/src/test/java/com/_30something_/tests/DI/MixedTests.java b/lab-02-dependency-injection/src/test/java/com/_30something_/tests/DI/MixedTests.java index a6a95b1..b3934dd 100644 --- a/lab-02-dependency-injection/src/test/java/com/_30something_/tests/DI/MixedTests.java +++ b/lab-02-dependency-injection/src/test/java/com/_30something_/tests/DI/MixedTests.java @@ -3,22 +3,141 @@ import com._30something.DI.ClassRegistrationException; import com._30something.DI.InterfaceRegistrationException; import java.lang.reflect.InvocationTargetException; +import java.security.AccessControlException; import com._30something.DI.DI; +import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; +import javax.inject.Inject; +import javax.inject.Singleton; + +class Class1 { + public Class2 class2; + public String info; + + @Inject + public Class1(Class2 class2) { + this.class2 = class2; + info = "Hello, I'm class1 :)"; + } +} + +interface Class2 { + String getInfo(); +} + +class Class3 implements Class2 { + @Inject + public Double specialImportantInfo; + + @Inject + public Class3() { + specialImportantInfo = 42.422442; + } + + @Override + public String getInfo() { + return "Hello, I'm class3 :)"; + } +} + +class Class4 { + public Class1 class1; + public Class5 class5; + + @Inject + public Class4() { + class1 = new Class1(new Class3()); + class5 = Class5.instance; + } +} + +@Singleton +class Class5 { + public static final Class5 instance = new Class5(); + + @Inject + private Class5() {} + + public Class5 getInstance() { + return instance; + } +} public class MixedTests { @Test public void mixedTestsFirst() throws ClassRegistrationException, InterfaceRegistrationException, ClassNotFoundException, InvocationTargetException, InstantiationException, IllegalAccessException { DI myDi = new DI(); - + Assertions.assertThrows(ClassRegistrationException.class, () -> myDi.registerClass(Cactus.class)); + myDi.registerClass(Bicycle2.class); + Assertions.assertThrows(ClassRegistrationException.class, () -> myDi.registerClass(Bicycle.class)); + Assertions.assertThrows(InterfaceRegistrationException.class, () -> myDi.registerClass(Graph.class)); + Assertions.assertThrows(InterfaceRegistrationException.class, + () -> myDi.registerClass(Graph.class, Factory.class)); + Assertions.assertThrows(InterfaceRegistrationException.class, + () -> myDi.registerClass(Graph.class, Graph.class)); + myDi.registerClass(Graph.class, Tree.class); + Assertions.assertThrows(ClassRegistrationException.class, myDi::completeRegistration); + myDi.registerClass(Car.class); + myDi.registerClass(Plane.class); + Assertions.assertThrows(AccessControlException.class, () -> myDi.resolveClass(Graph.class)); + myDi.registerClass(Factory.class); + Assertions.assertThrows(AccessControlException.class, () -> myDi.resolveClass(Tree.class)); + Assertions.assertThrows(ClassRegistrationException.class, myDi::completeRegistration); + myDi.registerClass(Train.class); + myDi.registerClass(Bus.class); + myDi.registerClass(Bicycle5.class); + myDi.completeRegistration(); + Assertions.assertDoesNotThrow(() -> { + myDi.completeRegistration(); + myDi.completeRegistration(); + Graph graphRealization = myDi.resolveClass(Graph.class); + Tree currentTree = myDi.resolveClass(Tree.class); + Assertions.assertEquals(graphRealization.getClass(), Tree.class); + Assertions.assertNotNull(graphRealization); + Assertions.assertNotNull(currentTree); + }); + Assertions.assertThrows(AccessControlException.class, () -> myDi.registerClass(Bicycle4.class)); + Assertions.assertThrows(AccessControlException.class, () -> myDi.registerClass(Bicycle4.class)); + Factory factory = myDi.resolveClass(Factory.class); + Bus bus = myDi.resolveClass(Bus.class); + Assertions.assertEquals(bus, factory.getBus()); + Car car = myDi.resolveClass(Car.class); + Assertions.assertNotEquals(car, myDi.resolveClass(Car.class)); + Assertions.assertNotEquals(car, factory.getCar()); + Assertions.assertNotEquals(car, myDi.resolveClass(Factory.class).getCar()); } @Test public void mixedTestsSecond() throws ClassRegistrationException, InterfaceRegistrationException, ClassNotFoundException, InvocationTargetException, InstantiationException, IllegalAccessException { DI myDi = new DI(); - + myDi.registerClass(Class4.class); + myDi.registerClass(Class1.class); + Assertions.assertThrows(ClassRegistrationException.class, myDi::completeRegistration); + myDi.registerClass(Class2.class, Class3.class); + Assertions.assertThrows(ClassRegistrationException.class, () -> myDi.registerClass(Class3.class)); + myDi.registerClass(Class5.class); + Assertions.assertThrows(ClassRegistrationException.class, () -> myDi.registerClass(Class5.class)); + myDi.completeRegistration(); + Assertions.assertThrows(AccessControlException.class, () -> myDi.registerClass(Class1.class)); + Assertions.assertThrows(AccessControlException.class, () -> myDi.registerClass(Class2.class, Class3.class)); + Class1 class1 = myDi.resolveClass(Class1.class); + Class2 class2 = myDi.resolveClass(Class2.class); + Class3 class3 = myDi.resolveClass(Class3.class); + Class4 class4 = myDi.resolveClass(Class4.class); + Class5 class5 = myDi.resolveClass(Class5.class); + Assertions.assertNotNull(class1); + Assertions.assertNotNull(class3); + Assertions.assertNotNull(class5); + Assertions.assertEquals(class5, myDi.resolveClass(Class5.class)); + Assertions.assertNotEquals(class2, class3); + Assertions.assertEquals(class2.getInfo(), "Hello, I'm class3 :)"); + Assertions.assertNotEquals(class2, myDi.resolveClass(Class2.class)); + Assertions.assertNotEquals(class4.class5, class5); + Assertions.assertEquals(class4.class5, myDi.resolveClass(Class4.class).class5); + Assertions.assertEquals(class4.class1.info, "Hello, I'm class1 :)"); + Assertions.assertEquals(class5.getInstance(), class4.class5); } } From 6aaf520154e7fe57d61f4e9fa6aa7aa4b7e3f7b5 Mon Sep 17 00:00:00 2001 From: Fedor Date: Thu, 2 Dec 2021 10:41:41 +0300 Subject: [PATCH 08/14] Structure creation --- lab-02-dependency-injection/build.gradle | 4 +- lab-03-calc-gui/lab3_compose/build.gradle.kts | 41 +++++++++++++++++++ .../lab3_compose/settings.gradle.kts | 10 +++++ .../lab3_compose/src/main/kotlin/Main.kt | 32 +++++++++++++++ lab-03-calc-gui/lab3_swing/build.gradle | 19 +++++++++ lab-03-calc-gui/lab3_swing/settings.gradle | 3 ++ .../java/com/_30something/calcGUI/Main.java | 7 ++++ 7 files changed, 114 insertions(+), 2 deletions(-) create mode 100644 lab-03-calc-gui/lab3_compose/build.gradle.kts create mode 100644 lab-03-calc-gui/lab3_compose/settings.gradle.kts create mode 100644 lab-03-calc-gui/lab3_compose/src/main/kotlin/Main.kt create mode 100644 lab-03-calc-gui/lab3_swing/build.gradle create mode 100644 lab-03-calc-gui/lab3_swing/settings.gradle create mode 100644 lab-03-calc-gui/lab3_swing/src/main/java/com/_30something/calcGUI/Main.java diff --git a/lab-02-dependency-injection/build.gradle b/lab-02-dependency-injection/build.gradle index c80bc1b..4a012ea 100644 --- a/lab-02-dependency-injection/build.gradle +++ b/lab-02-dependency-injection/build.gradle @@ -8,8 +8,8 @@ repositories { dependencies { api 'javax.inject:javax.inject:1' - testImplementation 'org.junit.jupiter:junit-jupiter-api:5.8.0' - testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.8.0' + testImplementation 'org.junit.jupiter:junit-jupiter-api:5.8.1' + testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.8.1' } test { diff --git a/lab-03-calc-gui/lab3_compose/build.gradle.kts b/lab-03-calc-gui/lab3_compose/build.gradle.kts new file mode 100644 index 0000000..a7ad00a --- /dev/null +++ b/lab-03-calc-gui/lab3_compose/build.gradle.kts @@ -0,0 +1,41 @@ +import org.jetbrains.compose.compose +import org.jetbrains.compose.desktop.application.dsl.TargetFormat +import org.jetbrains.kotlin.gradle.tasks.KotlinCompile + +plugins { + kotlin("jvm") version "1.5.31" + id("org.jetbrains.compose") version "1.0.0" +} + +group = "me.fedor" +version = "1.0" + +repositories { + google() + mavenCentral() + maven("https://maven.pkg.jetbrains.space/public/p/compose/dev") +} + +dependencies { + testImplementation("org.jetbrains.kotlin:kotlin-test:1.6.0") + implementation(compose.desktop.currentOs) +} + +tasks.test { + useJUnitPlatform() +} + +tasks.withType() { + kotlinOptions.jvmTarget = "11" +} + +compose.desktop { + application { + mainClass = "MainKt" + nativeDistributions { + targetFormats(TargetFormat.Dmg, TargetFormat.Msi, TargetFormat.Deb) + packageName = "lab3_compose" + packageVersion = "1.0.0" + } + } +} \ No newline at end of file diff --git a/lab-03-calc-gui/lab3_compose/settings.gradle.kts b/lab-03-calc-gui/lab3_compose/settings.gradle.kts new file mode 100644 index 0000000..21a5bba --- /dev/null +++ b/lab-03-calc-gui/lab3_compose/settings.gradle.kts @@ -0,0 +1,10 @@ +pluginManagement { + repositories { + google() + gradlePluginPortal() + maven("https://maven.pkg.jetbrains.space/public/p/compose/dev") + } + +} +rootProject.name = "lab3_compose" + diff --git a/lab-03-calc-gui/lab3_compose/src/main/kotlin/Main.kt b/lab-03-calc-gui/lab3_compose/src/main/kotlin/Main.kt new file mode 100644 index 0000000..f3c066f --- /dev/null +++ b/lab-03-calc-gui/lab3_compose/src/main/kotlin/Main.kt @@ -0,0 +1,32 @@ +// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +import androidx.compose.material.MaterialTheme +import androidx.compose.desktop.ui.tooling.preview.Preview +import androidx.compose.material.Button +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.window.Window +import androidx.compose.ui.window.application + +@Composable +@Preview +fun App() { + var text by remember { mutableStateOf("Hello, World!") } + + MaterialTheme { + Button(onClick = { + text = "Hello, Desktop!" + }) { + Text(text) + } + } +} + +fun main() = application { + Window(onCloseRequest = ::exitApplication) { + App() + } +} diff --git a/lab-03-calc-gui/lab3_swing/build.gradle b/lab-03-calc-gui/lab3_swing/build.gradle new file mode 100644 index 0000000..3cae2d7 --- /dev/null +++ b/lab-03-calc-gui/lab3_swing/build.gradle @@ -0,0 +1,19 @@ +plugins { + id 'java' +} + +group 'org.example' +version '1.0-SNAPSHOT' + +repositories { + mavenCentral() +} + +dependencies { + testImplementation 'org.junit.jupiter:junit-jupiter-api:5.8.1' + testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.8.1' +} + +test { + useJUnitPlatform() +} \ No newline at end of file diff --git a/lab-03-calc-gui/lab3_swing/settings.gradle b/lab-03-calc-gui/lab3_swing/settings.gradle new file mode 100644 index 0000000..0612f6c --- /dev/null +++ b/lab-03-calc-gui/lab3_swing/settings.gradle @@ -0,0 +1,3 @@ +rootProject.name = 'lab3_swing' +include 'src' + diff --git a/lab-03-calc-gui/lab3_swing/src/main/java/com/_30something/calcGUI/Main.java b/lab-03-calc-gui/lab3_swing/src/main/java/com/_30something/calcGUI/Main.java new file mode 100644 index 0000000..9faf5ac --- /dev/null +++ b/lab-03-calc-gui/lab3_swing/src/main/java/com/_30something/calcGUI/Main.java @@ -0,0 +1,7 @@ +package com._30something.calcGUI; + +public class Main { + public static void main(String[] args) { + System.out.println("Hello world"); + } +} From 1798ed4d64e0e912ea28e2e3281e37187518712c Mon Sep 17 00:00:00 2001 From: Fedor Date: Thu, 2 Dec 2021 10:43:10 +0300 Subject: [PATCH 09/14] Structure creation --- .../src/main/kotlin/{ => com/_30something/calcGUI}/Main.kt | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename lab-03-calc-gui/lab3_compose/src/main/kotlin/{ => com/_30something/calcGUI}/Main.kt (100%) diff --git a/lab-03-calc-gui/lab3_compose/src/main/kotlin/Main.kt b/lab-03-calc-gui/lab3_compose/src/main/kotlin/com/_30something/calcGUI/Main.kt similarity index 100% rename from lab-03-calc-gui/lab3_compose/src/main/kotlin/Main.kt rename to lab-03-calc-gui/lab3_compose/src/main/kotlin/com/_30something/calcGUI/Main.kt From afce78837789c540b632a66c357c47c58496accd Mon Sep 17 00:00:00 2001 From: Fedor Date: Thu, 9 Dec 2021 00:28:11 +0300 Subject: [PATCH 10/14] Structure fixes --- lab-03-calc-gui/lab3_compose/build.gradle.kts | 41 +-- .../com/_30something/lib_calc/BinOpKind.java | 9 + .../lib_calc/BinaryExpression.java | 7 + .../lib_calc/BinaryExpressionImpl.java | 34 +++ .../lib_calc/ComputeExpressionVisitor.java | 53 ++++ .../DebugRepresentationExpressionVisitor.java | 36 +++ .../_30something/lib_calc/DepthVisitor.java | 35 +++ .../com/_30something/lib_calc/Expression.java | 5 + .../lib_calc/ExpressionParseException.java | 7 + .../lib_calc/ExpressionVisitor.java | 8 + .../com/_30something/lib_calc/Literal.java | 5 + .../_30something/lib_calc/LiteralImpl.java | 20 ++ .../java/com/_30something/lib_calc/Main.java | 37 +++ .../lib_calc/ParenthesisExpression.java | 5 + .../lib_calc/ParenthesisExpressionImpl.java | 20 ++ .../com/_30something/lib_calc/Parser.java | 12 + .../com/_30something/lib_calc/ParserImpl.java | 247 ++++++++++++++++++ .../_30something/lib_calc/RequestVisitor.java | 55 ++++ .../java/com/_30something/lib_calc/Tests.java | 59 +++++ .../lib_calc/ToStringVisitor.java | 40 +++ .../com/_30something/lib_calc/Variable.java | 5 + .../_30something/lib_calc/VariableImpl.java | 20 ++ .../lab3_compose/settings.gradle.kts | 2 + .../kotlin/com/_30something/calcGUI/Main.kt | 32 --- lab-03-calc-gui/lab3_swing/build.gradle | 19 -- lab-03-calc-gui/lab3_swing/settings.gradle | 3 - .../java/com/_30something/calcGUI/Main.java | 7 - 27 files changed, 725 insertions(+), 98 deletions(-) create mode 100644 lab-03-calc-gui/lab3_compose/lib-calc/src/main/java/com/_30something/lib_calc/BinOpKind.java create mode 100644 lab-03-calc-gui/lab3_compose/lib-calc/src/main/java/com/_30something/lib_calc/BinaryExpression.java create mode 100644 lab-03-calc-gui/lab3_compose/lib-calc/src/main/java/com/_30something/lib_calc/BinaryExpressionImpl.java create mode 100644 lab-03-calc-gui/lab3_compose/lib-calc/src/main/java/com/_30something/lib_calc/ComputeExpressionVisitor.java create mode 100644 lab-03-calc-gui/lab3_compose/lib-calc/src/main/java/com/_30something/lib_calc/DebugRepresentationExpressionVisitor.java create mode 100644 lab-03-calc-gui/lab3_compose/lib-calc/src/main/java/com/_30something/lib_calc/DepthVisitor.java create mode 100644 lab-03-calc-gui/lab3_compose/lib-calc/src/main/java/com/_30something/lib_calc/Expression.java create mode 100644 lab-03-calc-gui/lab3_compose/lib-calc/src/main/java/com/_30something/lib_calc/ExpressionParseException.java create mode 100644 lab-03-calc-gui/lab3_compose/lib-calc/src/main/java/com/_30something/lib_calc/ExpressionVisitor.java create mode 100644 lab-03-calc-gui/lab3_compose/lib-calc/src/main/java/com/_30something/lib_calc/Literal.java create mode 100644 lab-03-calc-gui/lab3_compose/lib-calc/src/main/java/com/_30something/lib_calc/LiteralImpl.java create mode 100644 lab-03-calc-gui/lab3_compose/lib-calc/src/main/java/com/_30something/lib_calc/Main.java create mode 100644 lab-03-calc-gui/lab3_compose/lib-calc/src/main/java/com/_30something/lib_calc/ParenthesisExpression.java create mode 100644 lab-03-calc-gui/lab3_compose/lib-calc/src/main/java/com/_30something/lib_calc/ParenthesisExpressionImpl.java create mode 100644 lab-03-calc-gui/lab3_compose/lib-calc/src/main/java/com/_30something/lib_calc/Parser.java create mode 100644 lab-03-calc-gui/lab3_compose/lib-calc/src/main/java/com/_30something/lib_calc/ParserImpl.java create mode 100644 lab-03-calc-gui/lab3_compose/lib-calc/src/main/java/com/_30something/lib_calc/RequestVisitor.java create mode 100644 lab-03-calc-gui/lab3_compose/lib-calc/src/main/java/com/_30something/lib_calc/Tests.java create mode 100644 lab-03-calc-gui/lab3_compose/lib-calc/src/main/java/com/_30something/lib_calc/ToStringVisitor.java create mode 100644 lab-03-calc-gui/lab3_compose/lib-calc/src/main/java/com/_30something/lib_calc/Variable.java create mode 100644 lab-03-calc-gui/lab3_compose/lib-calc/src/main/java/com/_30something/lib_calc/VariableImpl.java delete mode 100644 lab-03-calc-gui/lab3_compose/src/main/kotlin/com/_30something/calcGUI/Main.kt delete mode 100644 lab-03-calc-gui/lab3_swing/build.gradle delete mode 100644 lab-03-calc-gui/lab3_swing/settings.gradle delete mode 100644 lab-03-calc-gui/lab3_swing/src/main/java/com/_30something/calcGUI/Main.java diff --git a/lab-03-calc-gui/lab3_compose/build.gradle.kts b/lab-03-calc-gui/lab3_compose/build.gradle.kts index a7ad00a..7c80799 100644 --- a/lab-03-calc-gui/lab3_compose/build.gradle.kts +++ b/lab-03-calc-gui/lab3_compose/build.gradle.kts @@ -1,41 +1,8 @@ -import org.jetbrains.compose.compose -import org.jetbrains.compose.desktop.application.dsl.TargetFormat -import org.jetbrains.kotlin.gradle.tasks.KotlinCompile - -plugins { - kotlin("jvm") version "1.5.31" - id("org.jetbrains.compose") version "1.0.0" -} - group = "me.fedor" version = "1.0" -repositories { - google() - mavenCentral() - maven("https://maven.pkg.jetbrains.space/public/p/compose/dev") -} - -dependencies { - testImplementation("org.jetbrains.kotlin:kotlin-test:1.6.0") - implementation(compose.desktop.currentOs) -} - -tasks.test { - useJUnitPlatform() -} - -tasks.withType() { - kotlinOptions.jvmTarget = "11" -} - -compose.desktop { - application { - mainClass = "MainKt" - nativeDistributions { - targetFormats(TargetFormat.Dmg, TargetFormat.Msi, TargetFormat.Deb) - packageName = "lab3_compose" - packageVersion = "1.0.0" - } +allprojects { + repositories { + mavenCentral() } -} \ No newline at end of file +} diff --git a/lab-03-calc-gui/lab3_compose/lib-calc/src/main/java/com/_30something/lib_calc/BinOpKind.java b/lab-03-calc-gui/lab3_compose/lib-calc/src/main/java/com/_30something/lib_calc/BinOpKind.java new file mode 100644 index 0000000..b36d21c --- /dev/null +++ b/lab-03-calc-gui/lab3_compose/lib-calc/src/main/java/com/_30something/lib_calc/BinOpKind.java @@ -0,0 +1,9 @@ +package com._30something.lib_calc; + +public enum BinOpKind { + ADD, + SUBTRACT, + MULTIPLY, + DIVIDE, + DEFAULT, +} diff --git a/lab-03-calc-gui/lab3_compose/lib-calc/src/main/java/com/_30something/lib_calc/BinaryExpression.java b/lab-03-calc-gui/lab3_compose/lib-calc/src/main/java/com/_30something/lib_calc/BinaryExpression.java new file mode 100644 index 0000000..bc31600 --- /dev/null +++ b/lab-03-calc-gui/lab3_compose/lib-calc/src/main/java/com/_30something/lib_calc/BinaryExpression.java @@ -0,0 +1,7 @@ +package com._30something.lib_calc; + +public interface BinaryExpression extends Expression { + Expression getLeft(); + Expression getRight(); + BinOpKind getOperation(); +} diff --git a/lab-03-calc-gui/lab3_compose/lib-calc/src/main/java/com/_30something/lib_calc/BinaryExpressionImpl.java b/lab-03-calc-gui/lab3_compose/lib-calc/src/main/java/com/_30something/lib_calc/BinaryExpressionImpl.java new file mode 100644 index 0000000..faf8597 --- /dev/null +++ b/lab-03-calc-gui/lab3_compose/lib-calc/src/main/java/com/_30something/lib_calc/BinaryExpressionImpl.java @@ -0,0 +1,34 @@ +package com._30something.lib_calc; + +public class BinaryExpressionImpl implements BinaryExpression { + + private final Expression left; + private final Expression right; + private final BinOpKind operation; + + public BinaryExpressionImpl(Expression left, Expression right, BinOpKind operation) { + this.left = left; + this.right = right; + this.operation = operation; + } + + @Override + public Expression getLeft() { + return left; + } + + @Override + public Expression getRight() { + return right; + } + + @Override + public BinOpKind getOperation() { + return operation; + } + + @Override + public Object accept(ExpressionVisitor visitor) { + return visitor.visitBinaryExpression(this); + } +} diff --git a/lab-03-calc-gui/lab3_compose/lib-calc/src/main/java/com/_30something/lib_calc/ComputeExpressionVisitor.java b/lab-03-calc-gui/lab3_compose/lib-calc/src/main/java/com/_30something/lib_calc/ComputeExpressionVisitor.java new file mode 100644 index 0000000..95f6954 --- /dev/null +++ b/lab-03-calc-gui/lab3_compose/lib-calc/src/main/java/com/_30something/lib_calc/ComputeExpressionVisitor.java @@ -0,0 +1,53 @@ +package com._30something.lib_calc; + +import java.util.Map; + +public class ComputeExpressionVisitor implements ExpressionVisitor { + + Map map; + + public ComputeExpressionVisitor(Map map) { + this.map = map; + } + + @Override + public Object visitBinaryExpression(BinaryExpression expr) { + BinOpKind operation = expr.getOperation(); + Double leftRes = (Double) expr.getLeft().accept(this); + Double rightRes = (Double) expr.getRight().accept(this); + switch (operation) { + case ADD: { + return leftRes + rightRes; + } + case SUBTRACT: { + return leftRes - rightRes; + } + case MULTIPLY: { + return leftRes * rightRes; + } + case DIVIDE: { + if (rightRes == 0) throw new ArithmeticException("Division by zero found"); + return leftRes / rightRes; + } + case DEFAULT: { + return 0; + } + } + return null; + } + + @Override + public Object visitLiteral(Literal expr) { + return expr.getValue(); + } + + @Override + public Object visitParenthesis(ParenthesisExpression expr) { + return expr.getExpr().accept(this); + } + + @Override + public Object visitVariable(Variable expr) { + return map.get(expr.getName()); + } +} diff --git a/lab-03-calc-gui/lab3_compose/lib-calc/src/main/java/com/_30something/lib_calc/DebugRepresentationExpressionVisitor.java b/lab-03-calc-gui/lab3_compose/lib-calc/src/main/java/com/_30something/lib_calc/DebugRepresentationExpressionVisitor.java new file mode 100644 index 0000000..5f04a5b --- /dev/null +++ b/lab-03-calc-gui/lab3_compose/lib-calc/src/main/java/com/_30something/lib_calc/DebugRepresentationExpressionVisitor.java @@ -0,0 +1,36 @@ +package com._30something.lib_calc; + +public class DebugRepresentationExpressionVisitor implements ExpressionVisitor { + + @Override + public Object visitBinaryExpression(BinaryExpression expr) { + String leftRes = (String) expr.getLeft().accept(this); + String rightRes = (String) expr.getRight().accept(this); + String operationPrefix; + if (expr.getOperation() == BinOpKind.ADD) { + operationPrefix = "add"; + } else if (expr.getOperation() == BinOpKind.SUBTRACT) { + operationPrefix = "sub"; + } else if (expr.getOperation() == BinOpKind.MULTIPLY) { + operationPrefix = "mul"; + } else { + operationPrefix = "div"; + } + return operationPrefix + "(" + leftRes + ", " + rightRes + ")"; + } + + @Override + public Object visitLiteral(Literal expr) { + return "'" + expr.getValue() + "'"; + } + + @Override + public Object visitParenthesis(ParenthesisExpression expr) { + return "paran-expr(" + expr.getExpr().accept(this) + ")"; + } + + @Override + public Object visitVariable(Variable expr) { + return "var[" + expr.getName() + "]"; + } +} diff --git a/lab-03-calc-gui/lab3_compose/lib-calc/src/main/java/com/_30something/lib_calc/DepthVisitor.java b/lab-03-calc-gui/lab3_compose/lib-calc/src/main/java/com/_30something/lib_calc/DepthVisitor.java new file mode 100644 index 0000000..6148c96 --- /dev/null +++ b/lab-03-calc-gui/lab3_compose/lib-calc/src/main/java/com/_30something/lib_calc/DepthVisitor.java @@ -0,0 +1,35 @@ +package com._30something.lib_calc; + +/** + * Visitor class used to count the depth of expression tree. + * In fact counts the distance from current vertex to the farthest leaf plus 1. + * This distance for root matches with the depth of tree. + * + * @author 30something + * @version 1.0 + */ + +public class DepthVisitor implements ExpressionVisitor { + + @Override + public Object visitBinaryExpression(BinaryExpression expr) { + Integer leftRes = (Integer) expr.getLeft().accept(this); + Integer rightRes = (Integer) expr.getRight().accept(this); + return Math.max(leftRes, rightRes) + 1; + } + + @Override + public Object visitLiteral(Literal expr) { + return 1; + } + + @Override + public Object visitParenthesis(ParenthesisExpression expr) { + return (Integer) expr.getExpr().accept(this) + 1; + } + + @Override + public Object visitVariable(Variable expr) { + return 1; + } +} diff --git a/lab-03-calc-gui/lab3_compose/lib-calc/src/main/java/com/_30something/lib_calc/Expression.java b/lab-03-calc-gui/lab3_compose/lib-calc/src/main/java/com/_30something/lib_calc/Expression.java new file mode 100644 index 0000000..4cfc38e --- /dev/null +++ b/lab-03-calc-gui/lab3_compose/lib-calc/src/main/java/com/_30something/lib_calc/Expression.java @@ -0,0 +1,5 @@ +package com._30something.lib_calc; + +public interface Expression { + Object accept(ExpressionVisitor visitor); +} diff --git a/lab-03-calc-gui/lab3_compose/lib-calc/src/main/java/com/_30something/lib_calc/ExpressionParseException.java b/lab-03-calc-gui/lab3_compose/lib-calc/src/main/java/com/_30something/lib_calc/ExpressionParseException.java new file mode 100644 index 0000000..b1e87f2 --- /dev/null +++ b/lab-03-calc-gui/lab3_compose/lib-calc/src/main/java/com/_30something/lib_calc/ExpressionParseException.java @@ -0,0 +1,7 @@ +package com._30something.lib_calc; + +public class ExpressionParseException extends Exception { + public ExpressionParseException(String message) { + super(message); + } +} diff --git a/lab-03-calc-gui/lab3_compose/lib-calc/src/main/java/com/_30something/lib_calc/ExpressionVisitor.java b/lab-03-calc-gui/lab3_compose/lib-calc/src/main/java/com/_30something/lib_calc/ExpressionVisitor.java new file mode 100644 index 0000000..574caa4 --- /dev/null +++ b/lab-03-calc-gui/lab3_compose/lib-calc/src/main/java/com/_30something/lib_calc/ExpressionVisitor.java @@ -0,0 +1,8 @@ +package com._30something.lib_calc; + +public interface ExpressionVisitor { + Object visitBinaryExpression(BinaryExpression expr); + Object visitLiteral(Literal expr); + Object visitParenthesis(ParenthesisExpression expr); + Object visitVariable(Variable expr); +} diff --git a/lab-03-calc-gui/lab3_compose/lib-calc/src/main/java/com/_30something/lib_calc/Literal.java b/lab-03-calc-gui/lab3_compose/lib-calc/src/main/java/com/_30something/lib_calc/Literal.java new file mode 100644 index 0000000..36481c5 --- /dev/null +++ b/lab-03-calc-gui/lab3_compose/lib-calc/src/main/java/com/_30something/lib_calc/Literal.java @@ -0,0 +1,5 @@ +package com._30something.lib_calc; + +public interface Literal extends Expression { + double getValue(); +} diff --git a/lab-03-calc-gui/lab3_compose/lib-calc/src/main/java/com/_30something/lib_calc/LiteralImpl.java b/lab-03-calc-gui/lab3_compose/lib-calc/src/main/java/com/_30something/lib_calc/LiteralImpl.java new file mode 100644 index 0000000..93d139b --- /dev/null +++ b/lab-03-calc-gui/lab3_compose/lib-calc/src/main/java/com/_30something/lib_calc/LiteralImpl.java @@ -0,0 +1,20 @@ +package com._30something.lib_calc; + +public class LiteralImpl implements Literal { + + private final Double value; + + public LiteralImpl(Double value) { + this.value = value; + } + + @Override + public Object accept(ExpressionVisitor visitor) { + return visitor.visitLiteral(this); + } + + @Override + public double getValue() { + return value; + } +} diff --git a/lab-03-calc-gui/lab3_compose/lib-calc/src/main/java/com/_30something/lib_calc/Main.java b/lab-03-calc-gui/lab3_compose/lib-calc/src/main/java/com/_30something/lib_calc/Main.java new file mode 100644 index 0000000..a03b233 --- /dev/null +++ b/lab-03-calc-gui/lab3_compose/lib-calc/src/main/java/com/_30something/lib_calc/Main.java @@ -0,0 +1,37 @@ +package com._30something.lib_calc; + +import java.util.Map; +import java.util.Scanner; + +public class Main { + public static void main(String[] args) { + Scanner in = new Scanner(System.in); + ParserImpl parser = new ParserImpl(); + DebugRepresentationExpressionVisitor debugVisitor = new DebugRepresentationExpressionVisitor(); + DepthVisitor depthVisitor = new DepthVisitor(); + ToStringVisitor toStringVisitor = ToStringVisitor.INSTANCE; + boolean correctInput = false; + while (!correctInput) { + try { + System.out.print("Enter expression: "); + Expression expr = parser.parseExpression(in.nextLine()); + System.out.print("Tree: "); + System.out.println((String) (expr.accept(debugVisitor))); + System.out.print("Expr-tree depth: "); + System.out.println(expr.accept(depthVisitor)); + System.out.print("Reconstructed expression: "); + System.out.println((String) (expr.accept(toStringVisitor))); + RequestVisitor requestVisitor = new RequestVisitor(in); + expr.accept(requestVisitor); + Map variablesMap = requestVisitor.getVariablesMap(); + ComputeExpressionVisitor computeVisitor = new ComputeExpressionVisitor(variablesMap); + System.out.print("Result: "); + System.out.println(expr.accept(computeVisitor)); + correctInput = true; + } catch (Exception exc) { + System.out.println(exc.getMessage()); + System.out.println("Please, input the expression again"); + } + } + } +} diff --git a/lab-03-calc-gui/lab3_compose/lib-calc/src/main/java/com/_30something/lib_calc/ParenthesisExpression.java b/lab-03-calc-gui/lab3_compose/lib-calc/src/main/java/com/_30something/lib_calc/ParenthesisExpression.java new file mode 100644 index 0000000..45c8852 --- /dev/null +++ b/lab-03-calc-gui/lab3_compose/lib-calc/src/main/java/com/_30something/lib_calc/ParenthesisExpression.java @@ -0,0 +1,5 @@ +package com._30something.lib_calc; + +public interface ParenthesisExpression extends Expression { + Expression getExpr(); +} diff --git a/lab-03-calc-gui/lab3_compose/lib-calc/src/main/java/com/_30something/lib_calc/ParenthesisExpressionImpl.java b/lab-03-calc-gui/lab3_compose/lib-calc/src/main/java/com/_30something/lib_calc/ParenthesisExpressionImpl.java new file mode 100644 index 0000000..c6eb54b --- /dev/null +++ b/lab-03-calc-gui/lab3_compose/lib-calc/src/main/java/com/_30something/lib_calc/ParenthesisExpressionImpl.java @@ -0,0 +1,20 @@ +package com._30something.lib_calc; + +public class ParenthesisExpressionImpl implements ParenthesisExpression { + + private final Expression childExpr; + + public ParenthesisExpressionImpl(Expression childExpr) { + this.childExpr = childExpr; + } + + @Override + public Object accept(ExpressionVisitor visitor) { + return visitor.visitParenthesis(this); + } + + @Override + public Expression getExpr() { + return childExpr; + } +} diff --git a/lab-03-calc-gui/lab3_compose/lib-calc/src/main/java/com/_30something/lib_calc/Parser.java b/lab-03-calc-gui/lab3_compose/lib-calc/src/main/java/com/_30something/lib_calc/Parser.java new file mode 100644 index 0000000..3ddff75 --- /dev/null +++ b/lab-03-calc-gui/lab3_compose/lib-calc/src/main/java/com/_30something/lib_calc/Parser.java @@ -0,0 +1,12 @@ +package com._30something.lib_calc; + +public interface Parser { + /** + * Parses expression from the string. + * If the string doesn't represent a valid expression, then throws ExpressionParseException. + * + * @param input the input string. + * @return parsed expression tree. + */ + Expression parseExpression(String input) throws ExpressionParseException; +} diff --git a/lab-03-calc-gui/lab3_compose/lib-calc/src/main/java/com/_30something/lib_calc/ParserImpl.java b/lab-03-calc-gui/lab3_compose/lib-calc/src/main/java/com/_30something/lib_calc/ParserImpl.java new file mode 100644 index 0000000..310e8c0 --- /dev/null +++ b/lab-03-calc-gui/lab3_compose/lib-calc/src/main/java/com/_30something/lib_calc/ParserImpl.java @@ -0,0 +1,247 @@ +package com._30something.lib_calc; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.Stack; + +public class ParserImpl implements Parser { + + public enum CharTypes { + NUMBER, + VARIABLE, + OPERATOR, + DEFAULT, + } + + public static class Token { + public String string; + public CharTypes type; + + public Token(String string, CharTypes type) { + this.string = string; + this.type = type; + } + } + + public CharTypes charType(char c) { + if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) return CharTypes.VARIABLE; + if ((c >= '0' && c <= '9') || c == '.') return CharTypes.NUMBER; + if (c == '-' || c == '+' || c == '*' || c == '/' || c == '(' || c == ')') return CharTypes.OPERATOR; + return CharTypes.DEFAULT; + } + + public List tokenize(String input) { + List tokensList = new ArrayList<>(); + Token tempToken; + char[] chars = input.toCharArray(); + for (int currentPtr = 0; currentPtr < chars.length; ) { + CharTypes tokenType = charType(chars[currentPtr]); + if (tokenType == CharTypes.VARIABLE || tokenType == CharTypes.NUMBER) { + StringBuilder tokenName = new StringBuilder(); + while (currentPtr < chars.length && charType(chars[currentPtr]) == tokenType) { + tokenName.append(chars[currentPtr]); + currentPtr++; + } + tempToken = new Token(tokenName.toString(), tokenType); + tokensList.add(tempToken); + } else { + tempToken = new Token(Character.toString(chars[currentPtr]), tokenType); + tokensList.add(tempToken); + currentPtr++; + } + } + return tokensList; + } + + /** + * Validates order of tokens in expression and constructs new tokens. + * It translates variables like '-x' as '-1 * x'. + * But lefts numbers like '-123' as '-123' and ignores unary plus. + * + * @param tokens - raw tokens + * @return newTokens - verified tokens + * @throws ExpressionParseException - parsing exception + */ + public List verifyTokens(List tokens) throws ExpressionParseException { + List newTokens = new ArrayList<>(); + Token pastToken = new Token("", CharTypes.DEFAULT); + int leftBrackets = 0; + boolean unaryMinus = false; + for (Token token : tokens) { + if (Objects.equals(token.string, " ")) { + continue; + } + if (token.type == CharTypes.DEFAULT || Objects.equals(token.string, ".")) { + throw new ExpressionParseException("Unexpected token found: '" + token.string + "'"); + } + if (token.type == CharTypes.OPERATOR) { + if (Objects.equals(token.string, "(")) { + leftBrackets++; + if (unaryMinus) { + newTokens.add(new Token("-1", CharTypes.NUMBER)); + newTokens.add(new Token("*", CharTypes.OPERATOR)); + unaryMinus = false; + } else if (Objects.equals(pastToken.string, ")") || pastToken.type == CharTypes.VARIABLE || + pastToken.type == CharTypes.NUMBER) { + throw new ExpressionParseException( + "Wrong order of operators found (left bracket after right bracket or literal)"); + } + newTokens.add(token); + } else if (pastToken.type == CharTypes.DEFAULT) { + if (Objects.equals(token.string, "-")) { + unaryMinus = true; + } else if (!Objects.equals(token.string, "+")) { + throw new ExpressionParseException( + "Wrong order of operators found (operator in the start of expression)"); + } + } else if (Objects.equals(token.string, ")")) { + leftBrackets--; + if (leftBrackets < 0) { + throw new ExpressionParseException("Overflow with right brackets found"); + } + if (pastToken.type == CharTypes.OPERATOR && !Objects.equals(pastToken.string, ")")) { + throw new ExpressionParseException( + "Wrong order of operators found (right bracket not after literal or right bracket)"); + } + newTokens.add(token); + } else { + if (pastToken.type == CharTypes.OPERATOR) { + if (!Objects.equals(pastToken.string, ")") && !Objects.equals(pastToken.string, "(")) { + throw new ExpressionParseException( + "Wrong order of operators found (operator after operator)"); + } else if (Objects.equals(pastToken.string, "(")) { + if (Objects.equals(token.string, "*") || Objects.equals(token.string, "/")) { + throw new ExpressionParseException( + "Wrong order of operators found (wrong operator after left bracket)"); + } else if (Objects.equals(token.string, "-")) { + unaryMinus = true; + } + } else { + newTokens.add(token); + } + } else { + newTokens.add(token); + } + } + } else { + if (pastToken.type == CharTypes.NUMBER || pastToken.type == CharTypes.VARIABLE) { + throw new ExpressionParseException( + "Wrong order of operators found (literal after literal)"); + } + if (Objects.equals(pastToken.string, ")")) { + throw new ExpressionParseException( + "Wrong order of operators found (literal after right bracket)"); + } + if (token.type == CharTypes.NUMBER && token.string.chars().filter(c -> c == '.').count() > 1) { + throw new ExpressionParseException("Two dots in float number found: '" + token.string + "'"); + } + if (unaryMinus) { + if (token.type == CharTypes.NUMBER) { + newTokens.add(new Token("-" + token.string, token.type)); + } else { + newTokens.add(new Token("-1", CharTypes.NUMBER)); + newTokens.add(new Token("*", CharTypes.OPERATOR)); + newTokens.add(token); + } + unaryMinus = false; + } else { + newTokens.add(token); + } + } + pastToken = token; + } + if (pastToken.type != CharTypes.NUMBER && pastToken.type != CharTypes.VARIABLE && + !Objects.equals(pastToken.string, ")")) { + throw new ExpressionParseException("Wrong order of operators found (operator in the end of expression)"); + } + if (leftBrackets > 0) { + throw new ExpressionParseException("Overflow with left brackets found"); + } + if (newTokens.isEmpty()) { + throw new ExpressionParseException("Expression is empty or insignificant"); + } + return newTokens; + } + + public List buildPolishNotation(List tokens) { + Stack operators = new Stack<>(); + List newList = new ArrayList<>(); + for (Token token : tokens) { + if (token.type == CharTypes.OPERATOR) { + if (Objects.equals(token.string, "(")) { + operators.add(token); + newList.add(token); + } else if (Objects.equals(token.string, ")")) { + while (!Objects.equals(operators.peek().string, "(")) { + newList.add(operators.peek()); + operators.pop(); + } + operators.pop(); + newList.add(token); + } else if (Objects.equals(token.string, "*") || Objects.equals(token.string, "/")) { + while (!operators.empty() && (Objects.equals(operators.peek().string, "*") || + Objects.equals(operators.peek().string, "/"))) { + newList.add(operators.peek()); + operators.pop(); + } + operators.push(token); + } else { + while (!operators.empty() && !Objects.equals(operators.peek().string, "(")) { + newList.add(operators.peek()); + operators.pop(); + } + operators.push(token); + } + } else { + newList.add(token); + } + } + while (!operators.empty()) { + newList.add(operators.peek()); + operators.pop(); + } + return newList; + } + + public Expression buildExpression(List tokens) throws ExpressionParseException { + Stack expressions = new Stack<>(); + for (Token token : tokens) { + if (token.type == CharTypes.OPERATOR) { + if (Objects.equals(token.string, ")")) { + Expression lower_expr = expressions.peek(); + expressions.pop(); + expressions.push(new ParenthesisExpressionImpl(lower_expr)); + } else if (!Objects.equals(token.string, "(")) { + Expression right_expr = expressions.peek(); + expressions.pop(); + Expression left_expr = expressions.peek(); + expressions.pop(); + BinOpKind operation; + if (Objects.equals(token.string, "+")) operation = BinOpKind.ADD; + else if (Objects.equals(token.string, "-")) operation = BinOpKind.SUBTRACT; + else if (Objects.equals(token.string, "*")) operation = BinOpKind.MULTIPLY; + else operation = BinOpKind.DIVIDE; + expressions.push(new BinaryExpressionImpl(left_expr, right_expr, operation)); + } + } else if (token.type == CharTypes.NUMBER) { + expressions.push(new LiteralImpl(Double.parseDouble(token.string))); + } else { + expressions.push(new VariableImpl(token.string)); + } + } + if (expressions.size() > 1) { + // In case if method 'verifiedTokens' didn't find any errors in expression + throw new ExpressionParseException("Wrong order of operands found"); + } + return expressions.peek(); + } + + @Override + public Expression parseExpression(String input) throws ExpressionParseException { + List rawTokens = tokenize(input); + List verifiedTokens = verifyTokens(rawTokens); + List polishNotationTokens = buildPolishNotation(verifiedTokens); + return buildExpression(polishNotationTokens); + } +} diff --git a/lab-03-calc-gui/lab3_compose/lib-calc/src/main/java/com/_30something/lib_calc/RequestVisitor.java b/lab-03-calc-gui/lab3_compose/lib-calc/src/main/java/com/_30something/lib_calc/RequestVisitor.java new file mode 100644 index 0000000..36dbca2 --- /dev/null +++ b/lab-03-calc-gui/lab3_compose/lib-calc/src/main/java/com/_30something/lib_calc/RequestVisitor.java @@ -0,0 +1,55 @@ +package com._30something.lib_calc; + +import java.util.HashMap; +import java.util.Map; +import java.util.Scanner; + +public class RequestVisitor implements ExpressionVisitor { + + Map map = new HashMap<>(); + Scanner in; + + public RequestVisitor(Scanner in) { + this.in = in; + } + + @Override + public Object visitBinaryExpression(BinaryExpression expr) { + expr.getLeft().accept(this); + expr.getRight().accept(this); + return null; + } + + @Override + public Object visitLiteral(Literal expr) { + return null; + } + + @Override + public Object visitParenthesis(ParenthesisExpression expr) { + return expr.getExpr().accept(this); + } + + @Override + public Object visitVariable(Variable expr) { + String varName = expr.getName(); + if (!map.containsKey(varName)) { + boolean correctInput = false; + while (!correctInput) { + try { + System.out.printf("Enter value for '%s': ", varName); + map.put(varName, Double.parseDouble(in.nextLine())); + correctInput = true; + } catch (Exception exc) { + System.out.println("Unable to convert input string to value"); + System.out.println("Please input value again"); + } + } + } + return null; + } + + public Map getVariablesMap() { + return map; + } +} diff --git a/lab-03-calc-gui/lab3_compose/lib-calc/src/main/java/com/_30something/lib_calc/Tests.java b/lab-03-calc-gui/lab3_compose/lib-calc/src/main/java/com/_30something/lib_calc/Tests.java new file mode 100644 index 0000000..2bf739c --- /dev/null +++ b/lab-03-calc-gui/lab3_compose/lib-calc/src/main/java/com/_30something/lib_calc/Tests.java @@ -0,0 +1,59 @@ +package com._30something.lib_calc; + +import java.io.*; +import java.util.Objects; +import java.util.Scanner; + +/** + * All tests are kept in tests.txt in root directory. One test consists of 3 lines. + * In order not to complicate the tests with the presence of variables, please don't use them. + * Tests format (4 lines): + * 'Entered expression' + * 'Expected parsed to string expression' + * 'Computed result' + * 'Line with one space' + */ +public class Tests { + public static void main(String[] args) { + try { + int testNumber = 0; + int successfulTests = 0; + FileReader input = new FileReader("lab-01-expr-calc/tests.txt"); + Scanner in = new Scanner(input); + Parser parser = new ParserImpl(); + ComputeExpressionVisitor computeVisitor = new ComputeExpressionVisitor(null); + ToStringVisitor toStringVisitor = ToStringVisitor.INSTANCE; + while (in.hasNextLine()) { + boolean successfulTest = true; + testNumber++; + String expr = in.nextLine(); + String expectedParsedExpr = in.nextLine(); + Double expectedResult = Double.parseDouble(in.nextLine()); + in.nextLine(); + Expression parsedExpr = parser.parseExpression(expr); + String parsedToStringExpr = (String) parsedExpr.accept(toStringVisitor); + if (!Objects.equals(expectedParsedExpr, parsedToStringExpr)) { + System.out.printf("Error found in parsing of expression in test #%d%n", testNumber); + System.out.printf("Expected parsed expression: %s%n", expectedParsedExpr); + System.out.printf("Received parsed expression: %s%n", parsedToStringExpr); + successfulTest = false; + } + Double result = (Double) parsedExpr.accept(computeVisitor); + if (!expectedResult.equals(result)) { + System.out.printf("Error found in computing result of expression in test #%d%n", testNumber); + System.out.printf("Expected result: %s%n", expectedResult); + System.out.printf("Received result: %s%n", result); + successfulTest = false; + } + if (successfulTest) { + successfulTests++; + } + } + System.out.printf("Successfully checked tests: %d/%d", successfulTests, testNumber); + input.close(); + } catch (Exception exc) { + System.out.println("Error occurred while reading/performing tests. Message:"); + System.out.println(exc.getMessage()); + } + } +} diff --git a/lab-03-calc-gui/lab3_compose/lib-calc/src/main/java/com/_30something/lib_calc/ToStringVisitor.java b/lab-03-calc-gui/lab3_compose/lib-calc/src/main/java/com/_30something/lib_calc/ToStringVisitor.java new file mode 100644 index 0000000..0563e4e --- /dev/null +++ b/lab-03-calc-gui/lab3_compose/lib-calc/src/main/java/com/_30something/lib_calc/ToStringVisitor.java @@ -0,0 +1,40 @@ +package com._30something.lib_calc; + +public class ToStringVisitor implements ExpressionVisitor { + + public static final ToStringVisitor INSTANCE = new ToStringVisitor(); + + private ToStringVisitor() {} + + @Override + public Object visitBinaryExpression(BinaryExpression expr) { + String leftRes = (String) expr.getLeft().accept(this); + String rightRes = (String) expr.getRight().accept(this); + String operation; + if (expr.getOperation() == BinOpKind.ADD) { + operation = "+"; + } else if (expr.getOperation() == BinOpKind.SUBTRACT) { + operation = "-"; + } else if (expr.getOperation() == BinOpKind.MULTIPLY) { + operation = "*"; + } else { + operation = "/"; + } + return leftRes + " " + operation + " " + rightRes; + } + + @Override + public Object visitLiteral(Literal expr) { + return Double.toString(expr.getValue()); + } + + @Override + public Object visitParenthesis(ParenthesisExpression expr) { + return "(" + expr.getExpr().accept(this) + ")"; + } + + @Override + public Object visitVariable(Variable expr) { + return expr.getName(); + } +} diff --git a/lab-03-calc-gui/lab3_compose/lib-calc/src/main/java/com/_30something/lib_calc/Variable.java b/lab-03-calc-gui/lab3_compose/lib-calc/src/main/java/com/_30something/lib_calc/Variable.java new file mode 100644 index 0000000..5885474 --- /dev/null +++ b/lab-03-calc-gui/lab3_compose/lib-calc/src/main/java/com/_30something/lib_calc/Variable.java @@ -0,0 +1,5 @@ +package com._30something.lib_calc; + +public interface Variable extends Expression { + String getName(); +} diff --git a/lab-03-calc-gui/lab3_compose/lib-calc/src/main/java/com/_30something/lib_calc/VariableImpl.java b/lab-03-calc-gui/lab3_compose/lib-calc/src/main/java/com/_30something/lib_calc/VariableImpl.java new file mode 100644 index 0000000..ec8eb06 --- /dev/null +++ b/lab-03-calc-gui/lab3_compose/lib-calc/src/main/java/com/_30something/lib_calc/VariableImpl.java @@ -0,0 +1,20 @@ +package com._30something.lib_calc; + +public class VariableImpl implements Variable { + + private final String name; + + public VariableImpl(String name) { + this.name = name; + } + + @Override + public Object accept(ExpressionVisitor visitor) { + return visitor.visitVariable(this); + } + + @Override + public String getName() { + return name; + } +} diff --git a/lab-03-calc-gui/lab3_compose/settings.gradle.kts b/lab-03-calc-gui/lab3_compose/settings.gradle.kts index 21a5bba..584aaac 100644 --- a/lab-03-calc-gui/lab3_compose/settings.gradle.kts +++ b/lab-03-calc-gui/lab3_compose/settings.gradle.kts @@ -8,3 +8,5 @@ pluginManagement { } rootProject.name = "lab3_compose" +include(":calc-gui") +include(":lib-calc") diff --git a/lab-03-calc-gui/lab3_compose/src/main/kotlin/com/_30something/calcGUI/Main.kt b/lab-03-calc-gui/lab3_compose/src/main/kotlin/com/_30something/calcGUI/Main.kt deleted file mode 100644 index f3c066f..0000000 --- a/lab-03-calc-gui/lab3_compose/src/main/kotlin/com/_30something/calcGUI/Main.kt +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. -import androidx.compose.material.MaterialTheme -import androidx.compose.desktop.ui.tooling.preview.Preview -import androidx.compose.material.Button -import androidx.compose.material.Text -import androidx.compose.runtime.Composable -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.setValue -import androidx.compose.ui.window.Window -import androidx.compose.ui.window.application - -@Composable -@Preview -fun App() { - var text by remember { mutableStateOf("Hello, World!") } - - MaterialTheme { - Button(onClick = { - text = "Hello, Desktop!" - }) { - Text(text) - } - } -} - -fun main() = application { - Window(onCloseRequest = ::exitApplication) { - App() - } -} diff --git a/lab-03-calc-gui/lab3_swing/build.gradle b/lab-03-calc-gui/lab3_swing/build.gradle deleted file mode 100644 index 3cae2d7..0000000 --- a/lab-03-calc-gui/lab3_swing/build.gradle +++ /dev/null @@ -1,19 +0,0 @@ -plugins { - id 'java' -} - -group 'org.example' -version '1.0-SNAPSHOT' - -repositories { - mavenCentral() -} - -dependencies { - testImplementation 'org.junit.jupiter:junit-jupiter-api:5.8.1' - testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.8.1' -} - -test { - useJUnitPlatform() -} \ No newline at end of file diff --git a/lab-03-calc-gui/lab3_swing/settings.gradle b/lab-03-calc-gui/lab3_swing/settings.gradle deleted file mode 100644 index 0612f6c..0000000 --- a/lab-03-calc-gui/lab3_swing/settings.gradle +++ /dev/null @@ -1,3 +0,0 @@ -rootProject.name = 'lab3_swing' -include 'src' - diff --git a/lab-03-calc-gui/lab3_swing/src/main/java/com/_30something/calcGUI/Main.java b/lab-03-calc-gui/lab3_swing/src/main/java/com/_30something/calcGUI/Main.java deleted file mode 100644 index 9faf5ac..0000000 --- a/lab-03-calc-gui/lab3_swing/src/main/java/com/_30something/calcGUI/Main.java +++ /dev/null @@ -1,7 +0,0 @@ -package com._30something.calcGUI; - -public class Main { - public static void main(String[] args) { - System.out.println("Hello world"); - } -} From 9b868a1f1014856a5354a68f6d2c9ce5e9edd64b Mon Sep 17 00:00:00 2001 From: Fedor Date: Sun, 12 Dec 2021 02:13:33 +0300 Subject: [PATCH 11/14] Buttons & some fields added --- .../lab3_compose/calc-gui/build.gradle.kts | 41 +++ .../kotlin/com/_30something/calc_gui/Main.kt | 295 ++++++++++++++++++ .../lab3_compose/lib-calc/build.gradle.kts | 25 ++ 3 files changed, 361 insertions(+) create mode 100644 lab-03-calc-gui/lab3_compose/calc-gui/build.gradle.kts create mode 100644 lab-03-calc-gui/lab3_compose/calc-gui/src/main/kotlin/com/_30something/calc_gui/Main.kt create mode 100644 lab-03-calc-gui/lab3_compose/lib-calc/build.gradle.kts diff --git a/lab-03-calc-gui/lab3_compose/calc-gui/build.gradle.kts b/lab-03-calc-gui/lab3_compose/calc-gui/build.gradle.kts new file mode 100644 index 0000000..6cc22e1 --- /dev/null +++ b/lab-03-calc-gui/lab3_compose/calc-gui/build.gradle.kts @@ -0,0 +1,41 @@ +import org.jetbrains.compose.compose +import org.jetbrains.compose.desktop.application.dsl.TargetFormat +import org.jetbrains.kotlin.gradle.tasks.KotlinCompile + +plugins { + kotlin("jvm") version "1.5.31" + id("org.jetbrains.compose") version "1.0.0" +} + +group = "me.fedor" +version = "1.0" + +repositories { + google() + maven("https://maven.pkg.jetbrains.space/public/p/compose/dev") +} + +dependencies { + implementation(project(":lib-calc")) + testImplementation(kotlin("test")) + implementation(compose.desktop.currentOs) +} + +tasks.test { + useJUnitPlatform() +} + +tasks.withType { + kotlinOptions.jvmTarget = "11" +} + +compose.desktop { + application { + mainClass = "MainKt" + nativeDistributions { + targetFormats(TargetFormat.Dmg, TargetFormat.Msi, TargetFormat.Deb) + packageName = "calc-gui" + packageVersion = "1.0.0" + } + } +} \ No newline at end of file diff --git a/lab-03-calc-gui/lab3_compose/calc-gui/src/main/kotlin/com/_30something/calc_gui/Main.kt b/lab-03-calc-gui/lab3_compose/calc-gui/src/main/kotlin/com/_30something/calc_gui/Main.kt new file mode 100644 index 0000000..0b0ad1d --- /dev/null +++ b/lab-03-calc-gui/lab3_compose/calc-gui/src/main/kotlin/com/_30something/calc_gui/Main.kt @@ -0,0 +1,295 @@ +package com._30something.calc_gui + +import androidx.compose.desktop.ui.tooling.preview.Preview +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.* +import androidx.compose.material.* +import androidx.compose.runtime.* +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.platform.LocalDensity +import androidx.compose.ui.unit.Density +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import androidx.compose.ui.window.Window +import androidx.compose.ui.window.application +import com._30something.lib_calc.* +import java.util.* + +@Composable +@Preview +fun app() { + var expression: String by remember { mutableStateOf("") } + + CompositionLocalProvider( + LocalDensity provides Density( + density = 1.5f, + fontScale = 1.5f, + ) + ) { + MaterialTheme { + Box( + modifier = Modifier.fillMaxSize() + ) { + Row( + modifier = Modifier + .align(Alignment.TopStart) + .padding(all = 2.dp) + .fillMaxSize() + ) { + TextField( + modifier = Modifier.padding(all = 2.dp).fillMaxWidth(), + value = expression, + onValueChange = { + expression = it + }, + placeholder = { Text("Enter expression", fontSize = 10.sp) }, + singleLine = true + ) + } + Column( + modifier = Modifier + .align(Alignment.BottomStart) + .fillMaxHeight(fraction = 0.8f) + .fillMaxWidth() + ) { + TextField( + modifier = Modifier.padding(all = 4.dp).fillMaxWidth(fraction = 0.52f), + value = "", + onValueChange = { + + }, + label = { Text("Result:", fontSize = 10.sp) }, + readOnly = true + ) + Row( + modifier = Modifier + .padding(all = 2.dp) + .weight(1f) + ) { + Button( + modifier = Modifier.wrapContentSize().padding(all = 2.dp), + onClick = { + expression += "(" + } + ) { + Text("(") + } + Button( + modifier = Modifier.wrapContentSize().padding(all = 2.dp), + onClick = { + expression += ")" + } + ) { + Text(")") + } + Button( + modifier = Modifier.wrapContentSize().padding(all = 2.dp), + onClick = { + expression = "" + } + ) { + Text("C") + } + Button( + modifier = Modifier.wrapContentSize().padding(all = 2.dp), + onClick = { + // TODO + val parser = ParserImpl() + val debugVisitor = DebugRepresentationExpressionVisitor() + val depthVisitor = DepthVisitor() + val toStringVisitor = ToStringVisitor.INSTANCE + var correctInput = false + try { + val expr = parser.parseExpression(expression) + print("Tree: ") + println(expr.accept(debugVisitor) as String) + print("Expr-tree depth: ") + println(expr.accept(depthVisitor)) + print("Reconstructed expression: ") + println(expr.accept(toStringVisitor) as String) + //val requestVisitor = RequestVisitor(inpt) +// expr.accept(requestVisitor) +// val variablesMap = requestVisitor.variablesMap +// val computeVisitor = ComputeExpressionVisitor(variablesMap) +// print("Result: ") +// println(expr.accept(computeVisitor)) + correctInput = true + } catch (exc: Exception) { + println(exc.message) + println("Please, input the expression again") + correctInput = false + } + } + ) { + Text("=") + } + } + Row( + modifier = Modifier + .padding(all = 2.dp) + .weight(1f) + ) { + Button( + modifier = Modifier.wrapContentSize().padding(all = 2.dp), + onClick = { + expression += "1" + } + ) { + Text("1") + } + Button( + modifier = Modifier.wrapContentSize().padding(all = 2.dp), + onClick = { + expression += "2" + } + ) { + Text("2") + } + Button( + modifier = Modifier.wrapContentSize().padding(all = 2.dp), + onClick = { + expression += "3" + } + ) { + Text("3") + } + Button( + modifier = Modifier.wrapContentSize().padding(all = 2.dp), + onClick = { + expression += "/" + } + ) { + Text("/") + } + } + Row( + modifier = Modifier + .padding(all = 2.dp) + .weight(1f) + ) { + Button( + modifier = Modifier.wrapContentSize().padding(all = 2.dp), + onClick = { + expression += "4" + } + ) { + Text("4") + } + Button( + modifier = Modifier.wrapContentSize().padding(all = 2.dp), + onClick = { + expression += "5" + } + ) { + Text("5") + } + Button( + modifier = Modifier.wrapContentSize().padding(all = 2.dp), + onClick = { + expression += "6" + } + ) { + Text("6") + } + Button( + modifier = Modifier.wrapContentSize().padding(all = 2.dp), + onClick = { + expression += "*" + } + ) { + Text("*") + } + } + Row( + modifier = Modifier + .padding(all = 2.dp) + .weight(1f) + ) { + Button( + modifier = Modifier.wrapContentSize().padding(all = 2.dp), + onClick = { + expression += "7" + } + ) { + Text("7") + } + Button( + modifier = Modifier.wrapContentSize().padding(all = 2.dp), + onClick = { + expression += "8" + } + ) { + Text("8") + } + Button( + modifier = Modifier.wrapContentSize().padding(all = 2.dp), + onClick = { + expression += "9" + } + ) { + Text("9") + } + Button( + modifier = Modifier.wrapContentSize().padding(all = 2.dp), + onClick = { + expression += "-" + } + ) { + Text("-") + } + } + Row( + modifier = Modifier + .padding(all = 2.dp) + .weight(1f) + ) { + Button( + modifier = Modifier.wrapContentSize().padding(all = 2.dp), + onClick = { + expression = expression.dropLast(1) + } + ) { + Text("←") + } + Button( + modifier = Modifier.wrapContentSize().padding(all = 2.dp), + onClick = { + expression += "0" + } + ) { + Text("0") + } + Button( + modifier = Modifier.wrapContentSize().padding(all = 2.dp), + onClick = { + expression += "." + } + ) { + Text(".") + } + Button( + modifier = Modifier.wrapContentSize().padding(all = 2.dp), + onClick = { + expression += "+" + } + ) { + Text("+") + } + } + } + } + } + } +} + +fun main() = application { + Window( + onCloseRequest = ::exitApplication, + title = "Calculator GUI", + resizable = false + ) { + app() + } +} diff --git a/lab-03-calc-gui/lab3_compose/lib-calc/build.gradle.kts b/lab-03-calc-gui/lab3_compose/lib-calc/build.gradle.kts new file mode 100644 index 0000000..27d6914 --- /dev/null +++ b/lab-03-calc-gui/lab3_compose/lib-calc/build.gradle.kts @@ -0,0 +1,25 @@ +import org.jetbrains.kotlin.gradle.tasks.KotlinCompile + +plugins { + kotlin("jvm") version "1.5.21" + application +} + +group = "me.fedor" +version = "1.0" + +dependencies { + testImplementation(kotlin("test")) +} + +tasks.test { + useJUnitPlatform() +} + +tasks.withType { + kotlinOptions.jvmTarget = "11" +} + +application { + mainClass.set("MainKt") +} \ No newline at end of file From a0634cfe8bfab0fa50b206ec25ecce54404190a1 Mon Sep 17 00:00:00 2001 From: Fedor Date: Thu, 16 Dec 2021 00:45:40 +0300 Subject: [PATCH 12/14] Functional added TODO: support variables --- .../kotlin/com/_30something/calc_gui/Main.kt | 224 ++++++++++++++---- .../java/com/_30something/lib_calc/Main.java | 74 +++--- .../_30something/lib_calc/RequestVisitor.java | 30 +-- .../lib-calc/src/main/java/tests.txt | 80 +++++++ 4 files changed, 303 insertions(+), 105 deletions(-) create mode 100644 lab-03-calc-gui/lab3_compose/lib-calc/src/main/java/tests.txt diff --git a/lab-03-calc-gui/lab3_compose/calc-gui/src/main/kotlin/com/_30something/calc_gui/Main.kt b/lab-03-calc-gui/lab3_compose/calc-gui/src/main/kotlin/com/_30something/calc_gui/Main.kt index 0b0ad1d..8e63b7a 100644 --- a/lab-03-calc-gui/lab3_compose/calc-gui/src/main/kotlin/com/_30something/calc_gui/Main.kt +++ b/lab-03-calc-gui/lab3_compose/calc-gui/src/main/kotlin/com/_30something/calc_gui/Main.kt @@ -1,26 +1,41 @@ package com._30something.calc_gui import androidx.compose.desktop.ui.tooling.preview.Preview -import androidx.compose.foundation.background import androidx.compose.foundation.layout.* +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.text.KeyboardActions import androidx.compose.material.* +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Warning import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.awt.ComposeWindow import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.LocalDensity -import androidx.compose.ui.unit.Density -import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.sp -import androidx.compose.ui.window.Window -import androidx.compose.ui.window.application +import androidx.compose.ui.text.TextRange +import androidx.compose.ui.text.input.TextFieldValue +import androidx.compose.ui.unit.* +import androidx.compose.ui.window.* import com._30something.lib_calc.* import java.util.* +import kotlin.collections.ArrayList +import kotlin.collections.HashMap @Composable @Preview fun app() { var expression: String by remember { mutableStateOf("") } + var inputError: Boolean by remember { mutableStateOf(false) } + var inputErrorText: String by remember { mutableStateOf("Error occurred...") } + var resultText: String by remember { mutableStateOf("") } + var treeText: String by remember { mutableStateOf("") } + var treeDepthText: String by remember { mutableStateOf("") } + var reconstructedExprText: String by remember { mutableStateOf("") } + var cursorPos: Int by remember { mutableStateOf(0) } + var literalsSet: HashSet by remember { mutableStateOf(HashSet()) } + var literalsMap: HashMap by remember { mutableStateOf(HashMap()) } + var checkerMap: HashMap by remember { mutableStateOf(HashMap()) } CompositionLocalProvider( LocalDensity provides Density( @@ -38,31 +53,60 @@ fun app() { .padding(all = 2.dp) .fillMaxSize() ) { - TextField( - modifier = Modifier.padding(all = 2.dp).fillMaxWidth(), - value = expression, - onValueChange = { - expression = it - }, - placeholder = { Text("Enter expression", fontSize = 10.sp) }, - singleLine = true - ) + Column { + TextField( + modifier = Modifier.padding(all = 2.dp).fillMaxWidth(), + value = TextFieldValue( + expression, + TextRange(cursorPos), + ), + onValueChange = { + expression = it.text + cursorPos = it.selection.start + }, + trailingIcon = { + if (inputError) { + Icon( + Icons.Default.Warning, "Warning", + tint = MaterialTheme.colors.error + ) + } + }, + placeholder = { Text("Enter expression", fontSize = 10.sp) }, + singleLine = true, + isError = inputError + ) + if (inputError) { + Text( + text = inputErrorText, + color = MaterialTheme.colors.error, + style = MaterialTheme.typography.subtitle1, + fontSize = 10.sp, + modifier = Modifier.padding(all = 2.dp) + ) + } + } } Column( modifier = Modifier .align(Alignment.BottomStart) - .fillMaxHeight(fraction = 0.8f) + .fillMaxHeight(fraction = 0.78f) .fillMaxWidth() ) { - TextField( - modifier = Modifier.padding(all = 4.dp).fillMaxWidth(fraction = 0.52f), - value = "", - onValueChange = { - - }, - label = { Text("Result:", fontSize = 10.sp) }, - readOnly = true - ) + Row { + Column { + TextField( + modifier = Modifier + .padding(all = 4.dp) + .fillMaxWidth(fraction = 0.405f) + .fillMaxHeight(0.2f), + value = resultText, + onValueChange = {}, + label = { Text("Result:", fontSize = 10.sp) }, + readOnly = true, + ) + } + } Row( modifier = Modifier .padding(all = 2.dp) @@ -72,7 +116,8 @@ fun app() { modifier = Modifier.wrapContentSize().padding(all = 2.dp), onClick = { expression += "(" - } + cursorPos = expression.length + }, ) { Text("(") } @@ -80,6 +125,7 @@ fun app() { modifier = Modifier.wrapContentSize().padding(all = 2.dp), onClick = { expression += ")" + cursorPos = expression.length } ) { Text(")") @@ -88,6 +134,14 @@ fun app() { modifier = Modifier.wrapContentSize().padding(all = 2.dp), onClick = { expression = "" + inputErrorText = "" + resultText = "" + treeText = "" + treeDepthText = "" + reconstructedExprText = "" + inputError = false + literalsSet.clear() + checkerMap.clear() } ) { Text("C") @@ -95,31 +149,46 @@ fun app() { Button( modifier = Modifier.wrapContentSize().padding(all = 2.dp), onClick = { - // TODO val parser = ParserImpl() val debugVisitor = DebugRepresentationExpressionVisitor() val depthVisitor = DepthVisitor() val toStringVisitor = ToStringVisitor.INSTANCE - var correctInput = false + val requestVisitor = RequestVisitor() try { val expr = parser.parseExpression(expression) - print("Tree: ") - println(expr.accept(debugVisitor) as String) - print("Expr-tree depth: ") - println(expr.accept(depthVisitor)) - print("Reconstructed expression: ") - println(expr.accept(toStringVisitor) as String) - //val requestVisitor = RequestVisitor(inpt) -// expr.accept(requestVisitor) -// val variablesMap = requestVisitor.variablesMap -// val computeVisitor = ComputeExpressionVisitor(variablesMap) -// print("Result: ") -// println(expr.accept(computeVisitor)) - correctInput = true + treeText = expr.accept(debugVisitor) as String + treeDepthText = expr.accept(depthVisitor).toString() + reconstructedExprText = expr.accept(toStringVisitor) as String + expr.accept(requestVisitor) + literalsSet = requestVisitor.variablesSet + if (checkerMap.size < literalsSet.size) { + println("lllllooooolllll") + inputError = true + inputErrorText = "Please, input all variables (use scroll if needed)" + } else { + println("hahaha") + literalsMap.clear() + for (element in checkerMap) { + try { + literalsMap[element.key] = element.value.toDouble() + } catch (exc: Exception) { + inputError = true + inputErrorText = "Unable to convert input string " + element.value + + " to value for variable " + element.key + + ". Please input value again" + throw Exception("ahhahaha") + } + } + println(inputError) + if (!inputError) { + val computeVisitor = ComputeExpressionVisitor(literalsMap) + val result: Double = expr.accept(computeVisitor) as Double + resultText = result.toString() + } + } } catch (exc: Exception) { - println(exc.message) - println("Please, input the expression again") - correctInput = false + inputError = true + inputErrorText = exc.message + ". Please, input the expression again" } } ) { @@ -135,6 +204,7 @@ fun app() { modifier = Modifier.wrapContentSize().padding(all = 2.dp), onClick = { expression += "1" + cursorPos = expression.length } ) { Text("1") @@ -143,6 +213,7 @@ fun app() { modifier = Modifier.wrapContentSize().padding(all = 2.dp), onClick = { expression += "2" + cursorPos = expression.length } ) { Text("2") @@ -151,6 +222,7 @@ fun app() { modifier = Modifier.wrapContentSize().padding(all = 2.dp), onClick = { expression += "3" + cursorPos = expression.length } ) { Text("3") @@ -159,6 +231,7 @@ fun app() { modifier = Modifier.wrapContentSize().padding(all = 2.dp), onClick = { expression += "/" + cursorPos = expression.length } ) { Text("/") @@ -173,6 +246,7 @@ fun app() { modifier = Modifier.wrapContentSize().padding(all = 2.dp), onClick = { expression += "4" + cursorPos = expression.length } ) { Text("4") @@ -181,6 +255,7 @@ fun app() { modifier = Modifier.wrapContentSize().padding(all = 2.dp), onClick = { expression += "5" + cursorPos = expression.length } ) { Text("5") @@ -189,6 +264,7 @@ fun app() { modifier = Modifier.wrapContentSize().padding(all = 2.dp), onClick = { expression += "6" + cursorPos = expression.length } ) { Text("6") @@ -197,6 +273,7 @@ fun app() { modifier = Modifier.wrapContentSize().padding(all = 2.dp), onClick = { expression += "*" + cursorPos = expression.length } ) { Text("*") @@ -211,6 +288,7 @@ fun app() { modifier = Modifier.wrapContentSize().padding(all = 2.dp), onClick = { expression += "7" + cursorPos = expression.length } ) { Text("7") @@ -219,6 +297,7 @@ fun app() { modifier = Modifier.wrapContentSize().padding(all = 2.dp), onClick = { expression += "8" + cursorPos = expression.length } ) { Text("8") @@ -227,6 +306,7 @@ fun app() { modifier = Modifier.wrapContentSize().padding(all = 2.dp), onClick = { expression += "9" + cursorPos = expression.length } ) { Text("9") @@ -235,6 +315,7 @@ fun app() { modifier = Modifier.wrapContentSize().padding(all = 2.dp), onClick = { expression += "-" + cursorPos = expression.length } ) { Text("-") @@ -257,6 +338,7 @@ fun app() { modifier = Modifier.wrapContentSize().padding(all = 2.dp), onClick = { expression += "0" + cursorPos = expression.length } ) { Text("0") @@ -265,6 +347,7 @@ fun app() { modifier = Modifier.wrapContentSize().padding(all = 2.dp), onClick = { expression += "." + cursorPos = expression.length } ) { Text(".") @@ -273,12 +356,64 @@ fun app() { modifier = Modifier.wrapContentSize().padding(all = 2.dp), onClick = { expression += "+" + cursorPos = expression.length } ) { Text("+") } } } + Box( + modifier = Modifier.align(Alignment.BottomEnd).padding(25.dp, 10.dp), + ) { + Column { + TextField( + modifier = Modifier.fillMaxWidth(fraction = 0.55f).padding(all = 2.dp), + value = treeText, + onValueChange = {}, + label = { Text("Debug tree: ", fontSize = 10.sp) }, + singleLine = true, + readOnly = true + ) + TextField( + modifier = Modifier.fillMaxWidth(fraction = 0.55f).padding(all = 2.dp), + value = treeDepthText, + onValueChange = {}, + label = { Text("Tree depth: ", fontSize = 10.sp) }, + singleLine = true, + readOnly = true + ) + TextField( + modifier = Modifier.fillMaxWidth(fraction = 0.55f).padding(all = 2.dp), + value = reconstructedExprText, + onValueChange = {}, + label = { Text("Reconstructed expression: ", fontSize = 10.sp) }, + singleLine = true, + readOnly = true + ) + LazyColumn( + modifier = Modifier.fillMaxHeight(0.64f).padding(all = 2.dp), + verticalArrangement = Arrangement.spacedBy(4.dp) + ) { + for (element in literalsSet) { + var tempText = if (checkerMap.containsKey(element)) checkerMap[element] ?: "" else "" + println(tempText) + item { + TextField( + modifier = Modifier.fillMaxWidth(fraction = 0.55f), + value = tempText, + onValueChange = { newText: String -> + //tempText = newText + checkerMap[element] = newText + }, + placeholder = { Text("$element = ") }, + singleLine = true + ) + } + } + } + } + } } } } @@ -288,6 +423,7 @@ fun main() = application { Window( onCloseRequest = ::exitApplication, title = "Calculator GUI", + state = WindowState(size = DpSize(1024.dp, 768.dp)), resizable = false ) { app() diff --git a/lab-03-calc-gui/lab3_compose/lib-calc/src/main/java/com/_30something/lib_calc/Main.java b/lab-03-calc-gui/lab3_compose/lib-calc/src/main/java/com/_30something/lib_calc/Main.java index a03b233..52f5b99 100644 --- a/lab-03-calc-gui/lab3_compose/lib-calc/src/main/java/com/_30something/lib_calc/Main.java +++ b/lab-03-calc-gui/lab3_compose/lib-calc/src/main/java/com/_30something/lib_calc/Main.java @@ -1,37 +1,37 @@ -package com._30something.lib_calc; - -import java.util.Map; -import java.util.Scanner; - -public class Main { - public static void main(String[] args) { - Scanner in = new Scanner(System.in); - ParserImpl parser = new ParserImpl(); - DebugRepresentationExpressionVisitor debugVisitor = new DebugRepresentationExpressionVisitor(); - DepthVisitor depthVisitor = new DepthVisitor(); - ToStringVisitor toStringVisitor = ToStringVisitor.INSTANCE; - boolean correctInput = false; - while (!correctInput) { - try { - System.out.print("Enter expression: "); - Expression expr = parser.parseExpression(in.nextLine()); - System.out.print("Tree: "); - System.out.println((String) (expr.accept(debugVisitor))); - System.out.print("Expr-tree depth: "); - System.out.println(expr.accept(depthVisitor)); - System.out.print("Reconstructed expression: "); - System.out.println((String) (expr.accept(toStringVisitor))); - RequestVisitor requestVisitor = new RequestVisitor(in); - expr.accept(requestVisitor); - Map variablesMap = requestVisitor.getVariablesMap(); - ComputeExpressionVisitor computeVisitor = new ComputeExpressionVisitor(variablesMap); - System.out.print("Result: "); - System.out.println(expr.accept(computeVisitor)); - correctInput = true; - } catch (Exception exc) { - System.out.println(exc.getMessage()); - System.out.println("Please, input the expression again"); - } - } - } -} +//package com._30something.lib_calc; +// +//import java.util.Map; +//import java.util.Scanner; +// +//public class Main { +// public static void main(String[] args) { +// Scanner in = new Scanner(System.in); +// ParserImpl parser = new ParserImpl(); +// DebugRepresentationExpressionVisitor debugVisitor = new DebugRepresentationExpressionVisitor(); +// DepthVisitor depthVisitor = new DepthVisitor(); +// ToStringVisitor toStringVisitor = ToStringVisitor.INSTANCE; +// boolean correctInput = false; +// while (!correctInput) { +// try { +// System.out.print("Enter expression: "); +// Expression expr = parser.parseExpression(in.nextLine()); +// System.out.print("Tree: "); +// System.out.println((String) (expr.accept(debugVisitor))); +// System.out.print("Expr-tree depth: "); +// System.out.println(expr.accept(depthVisitor)); +// System.out.print("Reconstructed expression: "); +// System.out.println((String) (expr.accept(toStringVisitor))); +// RequestVisitor requestVisitor = new RequestVisitor(in); +// expr.accept(requestVisitor); +// Map variablesMap = requestVisitor.getVariablesMap(); +// ComputeExpressionVisitor computeVisitor = new ComputeExpressionVisitor(variablesMap); +// System.out.print("Result: "); +// System.out.println(expr.accept(computeVisitor)); +// correctInput = true; +// } catch (Exception exc) { +// System.out.println(exc.getMessage()); +// System.out.println("Please, input the expression again"); +// } +// } +// } +//} diff --git a/lab-03-calc-gui/lab3_compose/lib-calc/src/main/java/com/_30something/lib_calc/RequestVisitor.java b/lab-03-calc-gui/lab3_compose/lib-calc/src/main/java/com/_30something/lib_calc/RequestVisitor.java index 36dbca2..842c83a 100644 --- a/lab-03-calc-gui/lab3_compose/lib-calc/src/main/java/com/_30something/lib_calc/RequestVisitor.java +++ b/lab-03-calc-gui/lab3_compose/lib-calc/src/main/java/com/_30something/lib_calc/RequestVisitor.java @@ -1,17 +1,12 @@ package com._30something.lib_calc; -import java.util.HashMap; -import java.util.Map; -import java.util.Scanner; +import java.util.HashSet; public class RequestVisitor implements ExpressionVisitor { - Map map = new HashMap<>(); - Scanner in; + HashSet literals = new HashSet<>(); - public RequestVisitor(Scanner in) { - this.in = in; - } + public RequestVisitor() {} @Override public Object visitBinaryExpression(BinaryExpression expr) { @@ -32,24 +27,11 @@ public Object visitParenthesis(ParenthesisExpression expr) { @Override public Object visitVariable(Variable expr) { - String varName = expr.getName(); - if (!map.containsKey(varName)) { - boolean correctInput = false; - while (!correctInput) { - try { - System.out.printf("Enter value for '%s': ", varName); - map.put(varName, Double.parseDouble(in.nextLine())); - correctInput = true; - } catch (Exception exc) { - System.out.println("Unable to convert input string to value"); - System.out.println("Please input value again"); - } - } - } + literals.add(expr.getName()); return null; } - public Map getVariablesMap() { - return map; + public HashSet getVariablesSet() { + return literals; } } diff --git a/lab-03-calc-gui/lab3_compose/lib-calc/src/main/java/tests.txt b/lab-03-calc-gui/lab3_compose/lib-calc/src/main/java/tests.txt new file mode 100644 index 0000000..e4c158d --- /dev/null +++ b/lab-03-calc-gui/lab3_compose/lib-calc/src/main/java/tests.txt @@ -0,0 +1,80 @@ +2 + 2 * 2 +2.0 + 2.0 * 2.0 +6 + +8 * 7 - 65 * 0.1 +8.0 * 7.0 - 65.0 * 0.1 +49.5 + +(((5))) +(((5.0))) +5 + +939.18828 - 827738.18 / 38849 + 929123 - 388394 * (18828 + 38399.1) +939.18828 - 827738.18 / 38849.0 + 929123.0 - 388394.0 * (18828.0 + 38399.1) +-22225732236.51827 + +-5.23 +-5.23 +-5.23 + +(-2) * 9 / (6 - 7) +(-2.0) * 9.0 / (6.0 - 7.0) +18 + +0/0.1 +0.0 / 0.1 +0 + +0 + 0.0 / 0.001 - 1 +0.0 + 0.0 / 0.001 - 1.0 +-1 + +0+0 + 0 + 0/0.001 - (-26) +0.0 + 0.0 + 0.0 + 0.0 / 0.001 - (-26.0) +26 + +(825 * 192) + (-192.11 + 5992 / 1929.1 * 26) +(825.0 * 192.0) + (-192.11 + 5992.0 / 1929.1 * 26.0) +158288.64890311545 + +-(-(-((((25.1223 / 82993 - 9930)))))) +-1.0 * (-1.0 * (-1.0 * ((((25.1223 / 82993.0 - 9930.0)))))) +9929.999697296158 + +18820.1882 - 188293.1 * 18829.22 + 93849.1882 - (-19292.11) + 26 * 37394 + (223) + (+1992) - 26.18820 * 7162 / 19928 +18820.1882 - 188293.1 * 18829.22 + 93849.1882 - (-19292.11) + 26.0 * 37394.0 + (223.0) + (1992.0) - 26.1882 * 7162.0 / 19928.0 +-3544305793.3074775 + +((((((((((((((((((((((((((((((((((((((((((-2927.239 * 162373) + 17.1))))))))))))))))))))))))))))))))))))))))) +((((((((((((((((((((((((((((((((((((((((((-2927.239 * 162373.0) + 17.1))))))))))))))))))))))))))))))))))))))))) +-475304561.047 + +-98.12 - 3823 - (-1829) +-98.12 - 3823.0 - (-1829.0) +-2092.12 + +0/(-1) +0.0 / (-1.0) +-0 + +982938 - 2990494 / ((1882 - 277494) + (177282 * 266373 / 277383 - 15) * 1667283) +982938.0 - 2990494.0 / ((1882.0 - 277494.0) + (177282.0 * 266373.0 / 277383.0 - 15.0) * 1667283.0) +982937.9999894635 + +-0.111 * 1772 +-0.111 * 1772.0 +-196.692 + +10220 / 188293 / 2 / (-1) * (-1) * (0 - 1 + 1 - 1 + 1 - 1) +10220.0 / 188293.0 / 2.0 / (-1.0) * (-1.0) * (0.0 - 1.0 + 1.0 - 1.0 + 1.0 - 1.0) +-0.0271385553366296144838098 + +87 - (0.11 * (2883 + ( 1829 - 1992/1882))) +87.0 - (0.11 * (2883.0 + (1829.0 - 1992.0 / 1882.0))) +-431.20357066950055 + ++(-(+(-9921.11))) +(-1.0 * ((-9921.11))) +9921.11 + \ No newline at end of file From ece6a713236dc71d07d64345afcad1d042d2e462 Mon Sep 17 00:00:00 2001 From: Fedor Date: Thu, 16 Dec 2021 10:04:53 +0300 Subject: [PATCH 13/14] Variables functional added --- .../kotlin/com/_30something/calc_gui/Main.kt | 59 +++++++------------ 1 file changed, 22 insertions(+), 37 deletions(-) diff --git a/lab-03-calc-gui/lab3_compose/calc-gui/src/main/kotlin/com/_30something/calc_gui/Main.kt b/lab-03-calc-gui/lab3_compose/calc-gui/src/main/kotlin/com/_30something/calc_gui/Main.kt index 8e63b7a..f786563 100644 --- a/lab-03-calc-gui/lab3_compose/calc-gui/src/main/kotlin/com/_30something/calc_gui/Main.kt +++ b/lab-03-calc-gui/lab3_compose/calc-gui/src/main/kotlin/com/_30something/calc_gui/Main.kt @@ -3,15 +3,12 @@ package com._30something.calc_gui import androidx.compose.desktop.ui.tooling.preview.Preview import androidx.compose.foundation.layout.* import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.foundation.text.KeyboardActions import androidx.compose.material.* import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Warning import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.awt.ComposeWindow -import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.text.TextRange import androidx.compose.ui.text.input.TextFieldValue @@ -19,8 +16,6 @@ import androidx.compose.ui.unit.* import androidx.compose.ui.window.* import com._30something.lib_calc.* import java.util.* -import kotlin.collections.ArrayList -import kotlin.collections.HashMap @Composable @Preview @@ -34,8 +29,8 @@ fun app() { var reconstructedExprText: String by remember { mutableStateOf("") } var cursorPos: Int by remember { mutableStateOf(0) } var literalsSet: HashSet by remember { mutableStateOf(HashSet()) } - var literalsMap: HashMap by remember { mutableStateOf(HashMap()) } - var checkerMap: HashMap by remember { mutableStateOf(HashMap()) } + val literalsMap = mutableStateMapOf() + val checkerMap = mutableStateMapOf() CompositionLocalProvider( LocalDensity provides Density( @@ -142,6 +137,7 @@ fun app() { inputError = false literalsSet.clear() checkerMap.clear() + literalsMap.clear() } ) { Text("C") @@ -162,33 +158,25 @@ fun app() { expr.accept(requestVisitor) literalsSet = requestVisitor.variablesSet if (checkerMap.size < literalsSet.size) { - println("lllllooooolllll") - inputError = true - inputErrorText = "Please, input all variables (use scroll if needed)" - } else { - println("hahaha") - literalsMap.clear() - for (element in checkerMap) { - try { - literalsMap[element.key] = element.value.toDouble() - } catch (exc: Exception) { - inputError = true - inputErrorText = "Unable to convert input string " + element.value + - " to value for variable " + element.key + - ". Please input value again" - throw Exception("ahhahaha") - } - } - println(inputError) - if (!inputError) { - val computeVisitor = ComputeExpressionVisitor(literalsMap) - val result: Double = expr.accept(computeVisitor) as Double - resultText = result.toString() + throw Exception("Please, input all variables (use scroll if needed)") + } + literalsMap.clear() + for (element in checkerMap) { + val value = element.value.text + val key = element.key + try { + literalsMap[key] = value.toDouble() + } catch (exc: Exception) { + throw Exception("Unable to convert input string " + + "'$value' to value for variable '$key'") } } + val computeVisitor = ComputeExpressionVisitor(literalsMap) + val result: Double = expr.accept(computeVisitor) as Double + resultText = result.toString() } catch (exc: Exception) { inputError = true - inputErrorText = exc.message + ". Please, input the expression again" + inputErrorText = exc.message.toString() } } ) { @@ -396,17 +384,14 @@ fun app() { verticalArrangement = Arrangement.spacedBy(4.dp) ) { for (element in literalsSet) { - var tempText = if (checkerMap.containsKey(element)) checkerMap[element] ?: "" else "" - println(tempText) item { TextField( modifier = Modifier.fillMaxWidth(fraction = 0.55f), - value = tempText, - onValueChange = { newText: String -> - //tempText = newText - checkerMap[element] = newText + value = checkerMap[element] ?: TextFieldValue(), + onValueChange = { + checkerMap[element] = it }, - placeholder = { Text("$element = ") }, + label = { Text("$element = ") }, singleLine = true ) } From b77fa56151869025fc76a2ce0e302b4c3bf133db Mon Sep 17 00:00:00 2001 From: Fedor Date: Sun, 19 Dec 2021 23:48:14 +0300 Subject: [PATCH 14/14] Stability improved & repeated code deleted --- .../kotlin/com/_30something/calc_gui/Main.kt | 234 ++++-------------- .../lib_calc/ComputeExpressionVisitor.java | 10 +- .../java/com/_30something/lib_calc/Main.java | 37 --- .../java/com/_30something/lib_calc/Tests.java | 59 ----- .../lib-calc/src/main/java/tests.txt | 80 ------ 5 files changed, 57 insertions(+), 363 deletions(-) delete mode 100644 lab-03-calc-gui/lab3_compose/lib-calc/src/main/java/com/_30something/lib_calc/Main.java delete mode 100644 lab-03-calc-gui/lab3_compose/lib-calc/src/main/java/com/_30something/lib_calc/Tests.java delete mode 100644 lab-03-calc-gui/lab3_compose/lib-calc/src/main/java/tests.txt diff --git a/lab-03-calc-gui/lab3_compose/calc-gui/src/main/kotlin/com/_30something/calc_gui/Main.kt b/lab-03-calc-gui/lab3_compose/calc-gui/src/main/kotlin/com/_30something/calc_gui/Main.kt index f786563..c82ad2b 100644 --- a/lab-03-calc-gui/lab3_compose/calc-gui/src/main/kotlin/com/_30something/calc_gui/Main.kt +++ b/lab-03-calc-gui/lab3_compose/calc-gui/src/main/kotlin/com/_30something/calc_gui/Main.kt @@ -15,7 +15,6 @@ import androidx.compose.ui.text.input.TextFieldValue import androidx.compose.ui.unit.* import androidx.compose.ui.window.* import com._30something.lib_calc.* -import java.util.* @Composable @Preview @@ -28,14 +27,39 @@ fun app() { var treeDepthText: String by remember { mutableStateOf("") } var reconstructedExprText: String by remember { mutableStateOf("") } var cursorPos: Int by remember { mutableStateOf(0) } - var literalsSet: HashSet by remember { mutableStateOf(HashSet()) } + var literalsSet = mutableSetOf() val literalsMap = mutableStateMapOf() val checkerMap = mutableStateMapOf() + @Composable + fun createGUIButton(symbol: Char) { + Button( + modifier = Modifier.wrapContentSize().padding(all = 2.dp), + onClick = { + expression += symbol + cursorPos = expression.length + } + ) { + Text(symbol.toString()) + } + } + + @Composable + fun createAdditionalResultFields(value: String, label: String) { + TextField( + modifier = Modifier.fillMaxWidth(fraction = 0.55f).padding(all = 2.dp), + value = value, + onValueChange = {}, + label = { Text(label, fontSize = 10.sp) }, + singleLine = true, + readOnly = true + ) + } + CompositionLocalProvider( LocalDensity provides Density( density = 1.5f, - fontScale = 1.5f, + fontScale = 1.5f ) ) { MaterialTheme { @@ -92,13 +116,14 @@ fun app() { Column { TextField( modifier = Modifier - .padding(all = 4.dp) + .padding(horizontal = 4.dp, vertical = 10.dp) .fillMaxWidth(fraction = 0.405f) .fillMaxHeight(0.2f), value = resultText, onValueChange = {}, label = { Text("Result:", fontSize = 10.sp) }, - readOnly = true, + singleLine = true, + readOnly = true ) } } @@ -107,24 +132,8 @@ fun app() { .padding(all = 2.dp) .weight(1f) ) { - Button( - modifier = Modifier.wrapContentSize().padding(all = 2.dp), - onClick = { - expression += "(" - cursorPos = expression.length - }, - ) { - Text("(") - } - Button( - modifier = Modifier.wrapContentSize().padding(all = 2.dp), - onClick = { - expression += ")" - cursorPos = expression.length - } - ) { - Text(")") - } + createGUIButton('(') + createGUIButton(')') Button( modifier = Modifier.wrapContentSize().padding(all = 2.dp), onClick = { @@ -174,6 +183,8 @@ fun app() { val computeVisitor = ComputeExpressionVisitor(literalsMap) val result: Double = expr.accept(computeVisitor) as Double resultText = result.toString() + inputError = false + inputErrorText = "" } catch (exc: Exception) { inputError = true inputErrorText = exc.message.toString() @@ -188,126 +199,30 @@ fun app() { .padding(all = 2.dp) .weight(1f) ) { - Button( - modifier = Modifier.wrapContentSize().padding(all = 2.dp), - onClick = { - expression += "1" - cursorPos = expression.length - } - ) { - Text("1") - } - Button( - modifier = Modifier.wrapContentSize().padding(all = 2.dp), - onClick = { - expression += "2" - cursorPos = expression.length - } - ) { - Text("2") - } - Button( - modifier = Modifier.wrapContentSize().padding(all = 2.dp), - onClick = { - expression += "3" - cursorPos = expression.length - } - ) { - Text("3") - } - Button( - modifier = Modifier.wrapContentSize().padding(all = 2.dp), - onClick = { - expression += "/" - cursorPos = expression.length - } - ) { - Text("/") - } + createGUIButton('1') + createGUIButton('2') + createGUIButton('3') + createGUIButton('/') } Row( modifier = Modifier .padding(all = 2.dp) .weight(1f) ) { - Button( - modifier = Modifier.wrapContentSize().padding(all = 2.dp), - onClick = { - expression += "4" - cursorPos = expression.length - } - ) { - Text("4") - } - Button( - modifier = Modifier.wrapContentSize().padding(all = 2.dp), - onClick = { - expression += "5" - cursorPos = expression.length - } - ) { - Text("5") - } - Button( - modifier = Modifier.wrapContentSize().padding(all = 2.dp), - onClick = { - expression += "6" - cursorPos = expression.length - } - ) { - Text("6") - } - Button( - modifier = Modifier.wrapContentSize().padding(all = 2.dp), - onClick = { - expression += "*" - cursorPos = expression.length - } - ) { - Text("*") - } + createGUIButton('4') + createGUIButton('5') + createGUIButton('6') + createGUIButton('*') } Row( modifier = Modifier .padding(all = 2.dp) .weight(1f) ) { - Button( - modifier = Modifier.wrapContentSize().padding(all = 2.dp), - onClick = { - expression += "7" - cursorPos = expression.length - } - ) { - Text("7") - } - Button( - modifier = Modifier.wrapContentSize().padding(all = 2.dp), - onClick = { - expression += "8" - cursorPos = expression.length - } - ) { - Text("8") - } - Button( - modifier = Modifier.wrapContentSize().padding(all = 2.dp), - onClick = { - expression += "9" - cursorPos = expression.length - } - ) { - Text("9") - } - Button( - modifier = Modifier.wrapContentSize().padding(all = 2.dp), - onClick = { - expression += "-" - cursorPos = expression.length - } - ) { - Text("-") - } + createGUIButton('7') + createGUIButton('8') + createGUIButton('9') + createGUIButton('-') } Row( modifier = Modifier @@ -322,63 +237,18 @@ fun app() { ) { Text("←") } - Button( - modifier = Modifier.wrapContentSize().padding(all = 2.dp), - onClick = { - expression += "0" - cursorPos = expression.length - } - ) { - Text("0") - } - Button( - modifier = Modifier.wrapContentSize().padding(all = 2.dp), - onClick = { - expression += "." - cursorPos = expression.length - } - ) { - Text(".") - } - Button( - modifier = Modifier.wrapContentSize().padding(all = 2.dp), - onClick = { - expression += "+" - cursorPos = expression.length - } - ) { - Text("+") - } + createGUIButton('0') + createGUIButton('.') + createGUIButton('+') } } Box( modifier = Modifier.align(Alignment.BottomEnd).padding(25.dp, 10.dp), ) { Column { - TextField( - modifier = Modifier.fillMaxWidth(fraction = 0.55f).padding(all = 2.dp), - value = treeText, - onValueChange = {}, - label = { Text("Debug tree: ", fontSize = 10.sp) }, - singleLine = true, - readOnly = true - ) - TextField( - modifier = Modifier.fillMaxWidth(fraction = 0.55f).padding(all = 2.dp), - value = treeDepthText, - onValueChange = {}, - label = { Text("Tree depth: ", fontSize = 10.sp) }, - singleLine = true, - readOnly = true - ) - TextField( - modifier = Modifier.fillMaxWidth(fraction = 0.55f).padding(all = 2.dp), - value = reconstructedExprText, - onValueChange = {}, - label = { Text("Reconstructed expression: ", fontSize = 10.sp) }, - singleLine = true, - readOnly = true - ) + createAdditionalResultFields(treeText, "Debug tree: ") + createAdditionalResultFields(treeDepthText, "Tree depth: ") + createAdditionalResultFields(reconstructedExprText, "Reconstructed expression: ") LazyColumn( modifier = Modifier.fillMaxHeight(0.64f).padding(all = 2.dp), verticalArrangement = Arrangement.spacedBy(4.dp) diff --git a/lab-03-calc-gui/lab3_compose/lib-calc/src/main/java/com/_30something/lib_calc/ComputeExpressionVisitor.java b/lab-03-calc-gui/lab3_compose/lib-calc/src/main/java/com/_30something/lib_calc/ComputeExpressionVisitor.java index 95f6954..7119e24 100644 --- a/lab-03-calc-gui/lab3_compose/lib-calc/src/main/java/com/_30something/lib_calc/ComputeExpressionVisitor.java +++ b/lab-03-calc-gui/lab3_compose/lib-calc/src/main/java/com/_30something/lib_calc/ComputeExpressionVisitor.java @@ -16,20 +16,20 @@ public Object visitBinaryExpression(BinaryExpression expr) { Double leftRes = (Double) expr.getLeft().accept(this); Double rightRes = (Double) expr.getRight().accept(this); switch (operation) { - case ADD: { + case ADD -> { return leftRes + rightRes; } - case SUBTRACT: { + case SUBTRACT -> { return leftRes - rightRes; } - case MULTIPLY: { + case MULTIPLY -> { return leftRes * rightRes; } - case DIVIDE: { + case DIVIDE -> { if (rightRes == 0) throw new ArithmeticException("Division by zero found"); return leftRes / rightRes; } - case DEFAULT: { + case DEFAULT -> { return 0; } } diff --git a/lab-03-calc-gui/lab3_compose/lib-calc/src/main/java/com/_30something/lib_calc/Main.java b/lab-03-calc-gui/lab3_compose/lib-calc/src/main/java/com/_30something/lib_calc/Main.java deleted file mode 100644 index 52f5b99..0000000 --- a/lab-03-calc-gui/lab3_compose/lib-calc/src/main/java/com/_30something/lib_calc/Main.java +++ /dev/null @@ -1,37 +0,0 @@ -//package com._30something.lib_calc; -// -//import java.util.Map; -//import java.util.Scanner; -// -//public class Main { -// public static void main(String[] args) { -// Scanner in = new Scanner(System.in); -// ParserImpl parser = new ParserImpl(); -// DebugRepresentationExpressionVisitor debugVisitor = new DebugRepresentationExpressionVisitor(); -// DepthVisitor depthVisitor = new DepthVisitor(); -// ToStringVisitor toStringVisitor = ToStringVisitor.INSTANCE; -// boolean correctInput = false; -// while (!correctInput) { -// try { -// System.out.print("Enter expression: "); -// Expression expr = parser.parseExpression(in.nextLine()); -// System.out.print("Tree: "); -// System.out.println((String) (expr.accept(debugVisitor))); -// System.out.print("Expr-tree depth: "); -// System.out.println(expr.accept(depthVisitor)); -// System.out.print("Reconstructed expression: "); -// System.out.println((String) (expr.accept(toStringVisitor))); -// RequestVisitor requestVisitor = new RequestVisitor(in); -// expr.accept(requestVisitor); -// Map variablesMap = requestVisitor.getVariablesMap(); -// ComputeExpressionVisitor computeVisitor = new ComputeExpressionVisitor(variablesMap); -// System.out.print("Result: "); -// System.out.println(expr.accept(computeVisitor)); -// correctInput = true; -// } catch (Exception exc) { -// System.out.println(exc.getMessage()); -// System.out.println("Please, input the expression again"); -// } -// } -// } -//} diff --git a/lab-03-calc-gui/lab3_compose/lib-calc/src/main/java/com/_30something/lib_calc/Tests.java b/lab-03-calc-gui/lab3_compose/lib-calc/src/main/java/com/_30something/lib_calc/Tests.java deleted file mode 100644 index 2bf739c..0000000 --- a/lab-03-calc-gui/lab3_compose/lib-calc/src/main/java/com/_30something/lib_calc/Tests.java +++ /dev/null @@ -1,59 +0,0 @@ -package com._30something.lib_calc; - -import java.io.*; -import java.util.Objects; -import java.util.Scanner; - -/** - * All tests are kept in tests.txt in root directory. One test consists of 3 lines. - * In order not to complicate the tests with the presence of variables, please don't use them. - * Tests format (4 lines): - * 'Entered expression' - * 'Expected parsed to string expression' - * 'Computed result' - * 'Line with one space' - */ -public class Tests { - public static void main(String[] args) { - try { - int testNumber = 0; - int successfulTests = 0; - FileReader input = new FileReader("lab-01-expr-calc/tests.txt"); - Scanner in = new Scanner(input); - Parser parser = new ParserImpl(); - ComputeExpressionVisitor computeVisitor = new ComputeExpressionVisitor(null); - ToStringVisitor toStringVisitor = ToStringVisitor.INSTANCE; - while (in.hasNextLine()) { - boolean successfulTest = true; - testNumber++; - String expr = in.nextLine(); - String expectedParsedExpr = in.nextLine(); - Double expectedResult = Double.parseDouble(in.nextLine()); - in.nextLine(); - Expression parsedExpr = parser.parseExpression(expr); - String parsedToStringExpr = (String) parsedExpr.accept(toStringVisitor); - if (!Objects.equals(expectedParsedExpr, parsedToStringExpr)) { - System.out.printf("Error found in parsing of expression in test #%d%n", testNumber); - System.out.printf("Expected parsed expression: %s%n", expectedParsedExpr); - System.out.printf("Received parsed expression: %s%n", parsedToStringExpr); - successfulTest = false; - } - Double result = (Double) parsedExpr.accept(computeVisitor); - if (!expectedResult.equals(result)) { - System.out.printf("Error found in computing result of expression in test #%d%n", testNumber); - System.out.printf("Expected result: %s%n", expectedResult); - System.out.printf("Received result: %s%n", result); - successfulTest = false; - } - if (successfulTest) { - successfulTests++; - } - } - System.out.printf("Successfully checked tests: %d/%d", successfulTests, testNumber); - input.close(); - } catch (Exception exc) { - System.out.println("Error occurred while reading/performing tests. Message:"); - System.out.println(exc.getMessage()); - } - } -} diff --git a/lab-03-calc-gui/lab3_compose/lib-calc/src/main/java/tests.txt b/lab-03-calc-gui/lab3_compose/lib-calc/src/main/java/tests.txt deleted file mode 100644 index e4c158d..0000000 --- a/lab-03-calc-gui/lab3_compose/lib-calc/src/main/java/tests.txt +++ /dev/null @@ -1,80 +0,0 @@ -2 + 2 * 2 -2.0 + 2.0 * 2.0 -6 - -8 * 7 - 65 * 0.1 -8.0 * 7.0 - 65.0 * 0.1 -49.5 - -(((5))) -(((5.0))) -5 - -939.18828 - 827738.18 / 38849 + 929123 - 388394 * (18828 + 38399.1) -939.18828 - 827738.18 / 38849.0 + 929123.0 - 388394.0 * (18828.0 + 38399.1) --22225732236.51827 - --5.23 --5.23 --5.23 - -(-2) * 9 / (6 - 7) -(-2.0) * 9.0 / (6.0 - 7.0) -18 - -0/0.1 -0.0 / 0.1 -0 - -0 + 0.0 / 0.001 - 1 -0.0 + 0.0 / 0.001 - 1.0 --1 - -0+0 + 0 + 0/0.001 - (-26) -0.0 + 0.0 + 0.0 + 0.0 / 0.001 - (-26.0) -26 - -(825 * 192) + (-192.11 + 5992 / 1929.1 * 26) -(825.0 * 192.0) + (-192.11 + 5992.0 / 1929.1 * 26.0) -158288.64890311545 - --(-(-((((25.1223 / 82993 - 9930)))))) --1.0 * (-1.0 * (-1.0 * ((((25.1223 / 82993.0 - 9930.0)))))) -9929.999697296158 - -18820.1882 - 188293.1 * 18829.22 + 93849.1882 - (-19292.11) + 26 * 37394 + (223) + (+1992) - 26.18820 * 7162 / 19928 -18820.1882 - 188293.1 * 18829.22 + 93849.1882 - (-19292.11) + 26.0 * 37394.0 + (223.0) + (1992.0) - 26.1882 * 7162.0 / 19928.0 --3544305793.3074775 - -((((((((((((((((((((((((((((((((((((((((((-2927.239 * 162373) + 17.1))))))))))))))))))))))))))))))))))))))))) -((((((((((((((((((((((((((((((((((((((((((-2927.239 * 162373.0) + 17.1))))))))))))))))))))))))))))))))))))))))) --475304561.047 - --98.12 - 3823 - (-1829) --98.12 - 3823.0 - (-1829.0) --2092.12 - -0/(-1) -0.0 / (-1.0) --0 - -982938 - 2990494 / ((1882 - 277494) + (177282 * 266373 / 277383 - 15) * 1667283) -982938.0 - 2990494.0 / ((1882.0 - 277494.0) + (177282.0 * 266373.0 / 277383.0 - 15.0) * 1667283.0) -982937.9999894635 - --0.111 * 1772 --0.111 * 1772.0 --196.692 - -10220 / 188293 / 2 / (-1) * (-1) * (0 - 1 + 1 - 1 + 1 - 1) -10220.0 / 188293.0 / 2.0 / (-1.0) * (-1.0) * (0.0 - 1.0 + 1.0 - 1.0 + 1.0 - 1.0) --0.0271385553366296144838098 - -87 - (0.11 * (2883 + ( 1829 - 1992/1882))) -87.0 - (0.11 * (2883.0 + (1829.0 - 1992.0 / 1882.0))) --431.20357066950055 - -+(-(+(-9921.11))) -(-1.0 * ((-9921.11))) -9921.11 - \ No newline at end of file