Initial commit

master
Oystein Kristoffer Tveit 2021-03-23 22:54:32 +01:00
commit b41d133be1
106 changed files with 5423 additions and 0 deletions

44
.classpath Normal file
View File

@ -0,0 +1,44 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" output="target/classes" path="src/main/java">
<attributes>
<attribute name="optional" value="true"/>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="src" output="target/test-classes" path="src/test/java">
<attributes>
<attribute name="optional" value="true"/>
<attribute name="maven.pomderived" value="true"/>
<attribute name="test" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-15">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="src" path="target/generated-sources/annotations">
<attributes>
<attribute name="optional" value="true"/>
<attribute name="maven.pomderived" value="true"/>
<attribute name="ignore_optional_problems" value="true"/>
<attribute name="m2e-apt" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="src" output="target/test-classes" path="target/generated-test-sources/test-annotations">
<attributes>
<attribute name="optional" value="true"/>
<attribute name="maven.pomderived" value="true"/>
<attribute name="ignore_optional_problems" value="true"/>
<attribute name="m2e-apt" value="true"/>
<attribute name="test" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="output" path="target/classes"/>
</classpath>

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
target

34
.project Normal file
View File

@ -0,0 +1,34 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>ovinger</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.m2e.core.maven2Builder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.jdt.core.javanature</nature>
<nature>org.eclipse.m2e.core.maven2Nature</nature>
</natures>
<filteredResources>
<filter>
<id>1615901682313</id>
<name></name>
<type>30</type>
<matcher>
<id>org.eclipse.core.resources.regexFilterMatcher</id>
<arguments>node_modules|.git|__CREATED_BY_JAVA_LANGUAGE_SERVER__</arguments>
</matcher>
</filter>
</filteredResources>
</projectDescription>

Binary file not shown.

After

Width:  |  Height:  |  Size: 338 KiB

View File

@ -0,0 +1,45 @@
@startuml LineEditor
skinparam DefaultFontName Fira Code
!definelong obj(id, txt, i)
object "~#1:LineEditor" as id
id : text = txt
id : insertionIndex = i
!enddefinelong
!define to(id1, id2, txt) id1 ..> id2 : txt
!define self(id, txt) id ..> id : "txt"
obj(A, '""', 0)
obj(B, "test", 0)
obj(C, "test", 1)
obj(D, "test", 0)
obj(E, "test", 2)
obj(F, "tet", 2)
obj(G, "tt", 1)
obj(H, "text", 3)
to(A, B, setText("test"))
self(B, 'getText() => "|test"')
to(B, C, "left()")
self(C, 'getText() => "t|est"')
to(C,D,"right()")
self(D, 'getText() => "|test"')
to(D,E,"setInsertionIndex(2)")
self(E, 'getText() => "te|st"')
to(E,F,"deleteRight()")
self(F, 'getText() => "te|t"')
to(F,G,"deleteLeft()")
self(G, 'getText() => "t|t"')
to(G,H,'insertString("ex")')
self(H, 'getText() => "tex|t"')
@enduml

View File

@ -0,0 +1 @@
<mxfile host="Electron" modified="2021-01-27T08:11:51.105Z" agent="5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/14.1.8 Chrome/87.0.4280.141 Electron/11.2.1 Safari/537.36" etag="YUmG1z7LbtLWfXLD1xFS" version="14.1.8" type="device"><diagram id="9T-pDAsRX8DZ6PBZXNOD" name="Page-1">7VxZd5s4FP41Pqd9SA4gNj/GTtJ2ptNp6+mZaV/mEJAxU4w8Qk7s/PpKRizCIsYL4CTkwUFXC+Le767CHoDxfPUOO4vZH8iD4UBTvNUAXA80bWir9JMR1gnBsIYJwceBl5DUnDAJHiEnKpy6DDwYCwMJQiEJFiLRRVEEXSLQHIzRgzhsikLxrgvHh1uEieuE29S/A4/M0t0pOf09DPwZyTp4z9xJB3NCPHM89FAggZsBGGOESHI1X41hyHiX8iWZd1vRm20Mw4jUmfDlx4MZ373/7Uf8zfq8wsvJp3/DC77KvRMu+QMPNDOk640WbMtkzflg/r9k+xzNHewH0QBc0V5lsaKflLh5Wka/IGiR9OmFPgJX5MIJA5/Pc+mGIRa7Pegi7JAA8THLyIM4DCKY35pe+fz/ZoN3OQGodNZHOvrGCwjC6QjKi7vyLEpblGkzXKYc9PAhnJKk02ad5X2z59xMuWafxe78KnmwbDtBFEPMmPKBsmOVTVbkD6MJm9bu2VQK5KuE89cb2Yy4HK6TzY4QHTUNN7CcBhQuYDRFEeFqqGq8fevMg5Ap8HsY3kO2KuMamYdsEBtDp45RSDnP7gym06nmupQeE4x+wkLP7a3CFCTtSTVKy/ZfhHOKTfoccFUgcXi/g2gOCV7TIbzX5prGTY3Omw8FvTU5bVbQWYvTHG4q/GzhXJvoBVeoPZTLkihXSUgw8q6YlaKtCFG0C2z1nHgGPd6gbMHrf2hDuTTS5vdi3zXjgpK11mlrFZDCNNr6XujJJ7FGOkeUG99YpYAIhT8ku40M9ARTuy3GgqAMiZxSGoYhtRT3ooGWCY/f4TMKIpKjJMMAh4laln+MltiFfFbRoJYW0soLDUsLJYzZWmiDpeyxD4dX6iJr4ssNnTgO3CcgVq2ulbJPeLVb9kWMfP30OLG98fzbV//35WTxZf7zz+G5Y0Q9ECM7wdY0RqodPDPsLCJyXNHL3QbYofQxjeS2nVgMyV/Uj73JewiMSd56W3BNyQ0qvNPGGwpQTD0TDw/AqOzA5oHnsekjDOPg0bnbLMXQuWDc2/DTGA2Ma7bWkqCYO7HT+Ba15FxUIPEumgSRWlPeRdXPS/2fUuud6g/OWv2zUOJY9c9ClLbU3ziV+iueQ5wLNukixq4wY0YIS8au2O4TrY8vfYT8EDqLIL500ZyS3ZgOuZ2mgWRyl5HcyLDY9M3LtSSWIWLC2LYksjD1FIbkKR3tc8DOckDRh/Z54GnzwCzmaiMRlGqYse2YqaOb8CbCZIZ8FDnhTU4dYcTwn/rmfMxHxOS3If4HCVlzGTE7JQpDnvilPfLEryrJzNNKeZLZeBwBasYRes04onaAcJTcQW9Zn6dlVXvLWseyZu3OLKss5+lN616mVa9pWtNc4kxsqyz77W3rM7CtfdRay7aCYde21XyOtQPMGPOCiwemWDwA5jZKrDaLBzXOuNwlvi8cY+1RkSxrpu3CHZpZyeIzqSxmlcQ0glKtS9Us/AFxxdpnUaV11aEqrjsU162oO1LROOvCMA7p6scpB4Safmk8vdFycm4b4gx6kezi0EKoFKZ2D9NjYAq0ZmAKFBGmlt4KTAHYF6basBWYDnuYHgNTHTQDU13tBKa6vi9MdaUVmErfPDjiVNnnp8pv0yTBdOYs3OcxW/FtKWssTzteWqCnDcsOnQd+u9KB5o6JTvwqQX2hEyr1Vyl0oHQudK3X9JaFrqudC7363OKMM/8Ykg9CxemN9mrKAHrdI87mMKNvMbUvxAvS3f2uFJfp7kq8WTMKb6cS/8RLRn0p/qxL8Zr8gfpSvOiRDaWmdW2sFA/M3roea13NmtZVO693SNTqY5jeurZlXXvj2phxNdWujasqO8M6+3THgyEk8OsLP+4se2LZC0dqq3lOf5JUwdZ6pXRDF0v0hrjgoRV6QytV6MVlGyrQG8a+BXpDbadA3x8kHYVS02gEpSboAqWmuS9KTa0VlKZGuoMTBTiwxq+xuGxoXReXte6OkZjQX4XMxeKwCSQylxnT5mQOtpjaly8E6e4sX6RVid3li2FNz9pO+SLdd1++6K580X8DZtBU9cLSJAlpu9+AkZ2+9KF+7VDfMhsJ9S29i1DfsvYN9S3QTqh/si8THPLy0CsM+iy986DvWZZVE3c4ITiI/MKv3MCCu3251VZLtA227NyzXQzJ6lh99Nju4dfqkPgR9PFjnfjRlv2MVLuvFmh9/HhM/GhbjcSPttFF/Gjb+8aPtt5K/AiqywiNVw1XryWCLCOw61IxeJYRZHIw//Fl/4ZZCSum7MvqymmwQpv5j2Qn9iT/pXFw8ws=</diagram></mxfile>

Binary file not shown.

After

Width:  |  Height:  |  Size: 131 KiB

View File

@ -0,0 +1,32 @@
@startuml Rectangle
skinparam DefaultFontName Fira Code
!definelong obj1(id, max, min)
object "~#1:Rectangle" as id
id : maxpoint = max
id : minpoint = min
!enddefinelong
!definelong obj2(id, max, min)
object "~#2:Rectangle" as id
id : maxpoint = max
id : minpoint = min
!enddefinelong
!define to(id1, id2, txt) id1 ..> id2 : txt
!define self(id, txt) id ..> id : "txt"
obj1(A, "[]", "[]")
obj1(B, "[1,2]", "[1,2]")
obj2(C, "[3,10]", "[-1,8]")
obj1(D, "[3,10]", "[-1,2]")
to(A,B,"add(1,2)")
self(B, "contains(1,2) => false")
to(B,D,"add(~#2)")
self(D, "contains(1,2) => true")
@enduml

View File

@ -0,0 +1 @@
<mxfile host="app.diagrams.net" modified="2021-01-26T13:33:14.506Z" agent="5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.141 Safari/537.36" etag="gUni9gv5qTJosgkG4kGU" version="14.2.7" type="device"><diagram id="9T-pDAsRX8DZ6PBZXNOD" name="Page-1">7Vptc+I2EP41zLQfYGwZG/MxwHGZzrRzbabT3n3pKLYAzckWlQUx9+u7QvKbwImT8nK5hMkQtNqVVtLus48MPW+a5B8FXq9+5TFhPeTEec+b9RByhwj11J8T77RkFBjBUtDYKFWCO/qNGKFjpBsak6yhKDlnkq6bwoinKYlkQ4aF4A9NtQVnzVnXeEkOBHcRZofSv2gsV1oaolElvyV0uTIzDx3jd4ILXSPIVjjmDzWR96HnTQXnUn9K8ilhau+KbdF285be0i9BUtnF4PcvD0F2f/vLl+zP0adcbO5++4f1zShbzDZmvT0UMBhvslYuy53ZhuDfjfJzkmCxpGnPu4FeZ53DOwj3q1XyvuRr3Tes9UmSyz5mdGnsInCYiGZ3TCIusKTc6GzSmAhGU1JNDZ+W5v/ewftK4Llg9QecPk6XjBQKsBX3thHI1rZsJWzJi9bOyELqzlB12m4nOP/EKRyVMpupd3/S82flakonEpo+pthcAWp4irZESAqxe6N3e7Y/j4nZ+5n2cMJBa8H2obigECLeZMFTaTLPRaY9xwllKmdvCdsSNaraKpkwpaR0wHTKGRf7mb3FYoGiCOSZFPwrqfXM5w68yp4iiVDpv3Ka5K1R7Za5AhhDeEKk2IGKMQhNdu2K9NPNhypV3cDIVrU0HRkZNuiwLAeuMgg+mCR6RkJ5RxLKOqRoI7YkNttI0vhGoRQ0I4azjEbNbY5xtiqV7T0PI/LEnuvJSXyAcdb+goN8IyLyNFRIiHYin9I7PK/aifhHDqSQCcIABrZNd4+dkpmhSJUiflAzHtyhddB6mcaqjpb2QKPmQGNrHL0NB+PAWeJdTW2tFLJ2f+34HY8e9cpW90MrYPX8VfiWB/DyiPZbS4SCCVVScdQEyjkVGORT4AJ1HHRiLHFfGfUzETUsVlKqcn6jloLmSiUbLDkHMMdrmg0inoA4ykBlvihwSc8ysWfRyAhsQGKaZj9BBMGyHfRzAacBThQmGvwE51lWrxh6TS3wqkpVMz0LaDU1zZvYCJzQOFbmE0Ey+g3f74dSmWkiA8bV8A5jbSTPDAqfBhxR6DfCJfAP0TE8kozoXOgYvNONK9KNIhc6044jBt8L/TgkE88nJKenH2XVuBr/GD3NP2qUI+UpeYRvwCaI3d8KsAZ+0fxc75vlBs10a1e0ciprZtD6XOupjFSjsGkek3HshPQleA30pbz1mnhCL6UvKLACsyN/ORVpCM9Jgy9Cd7vGS3DVeBk6g2DolC+3GT1oOBiH4+r1wlgaWrEU+gPHrQ0bXoQaq9X4h9N2pcrK63bz81Dn8Q9NnaXYvBnm7I6PUOfRJalzURk6Vvb/g6JtZb8q9MfL/gnRt+vDhquir2sXWfdU1dqmh2eu1m77Y+DvGKhwHO8xSgHUj4lC9kMo1zu8XlwWhFBroLxf4M9/gfd0RQaY6XqD779f4Z95hffca1/h3WPfIbzCJENvLcnC9yRrSzLfvkNe/UGZO7wYnz4hL+78FAtdlRjbvMUmJJ2JsdMcqOvXcCcjxq/yyy9FjBX+vhleXN6fTs+LoVn9REeHVfU7J+/Dfw==</diagram></mxfile>

Binary file not shown.

After

Width:  |  Height:  |  Size: 621 KiB

View File

@ -0,0 +1 @@
<mxfile host="Electron" modified="2021-01-29T09:28:07.693Z" agent="5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/14.1.8 Chrome/87.0.4280.141 Electron/11.2.1 Safari/537.36" etag="ipHiC1TG13N0dEeqCm6r" version="14.1.8" type="device"><diagram id="9T-pDAsRX8DZ6PBZXNOD" name="Page-1">7V1Zd6O4Ev41OWfmIX2Q2B/j5GYyW3fP5PaZ2/NyD7Flm2ls3IATJ79+hC3ZIIQDtpbYUT+kjdgKqviq6istF/b1bPVTFi2mv6cjlFxAa7S6sG8uIAwDgP+WDc+bBtcPNw2TLB5tmsCu4T5+QaTRIq3LeITy2oFFmiZFvKg3DtP5HA2LWluUZelT/bBxmtTvuogmqNFwP4ySZutf8aiYUumsXfsdiidTemeb7plF9GDSkE+jUfpUabL/c2FfZ2labH7NVtcoKd8dfS+b825b9m4Fy9C86HLCp+Tm6err05e7j/ffo4d5NrlbuJceka14pg+MRvj5yeY8neP/BmlWTNNJOo+S39J0gdsBbvwHFcUz0Ve0LFLcNC1mCdmLVnHxP/zb+uCSra+VPTeldVh045lsjKJ8ikbksLzI0m/oOk3SbC2XfXtr4X94T/O5qV1E2QQVex7W3RxXPmHlRPK2fkLpDBXZMz4gQ0lUxI91C4iIIU22x21P/ZzGWBRoEZsHHtE4MXmb2jK9RJ4usyEiZ+00hn9UxNg1rfXI1+kffz95+cPdL3/nX/zPq2x5//H/ySUR+DFKluQRLqCX4BczWNRU7X1flrY3mOH3Fs8v7Cu811qs8N/1W7Y27ZdFqfJyn1PZV6BVcRkl8YScN8TKQFl99wgN0wy/yJQcs5yPUJbE2KS2t8a/JuT/tYAPuwYbW8HVPb75X1ExnNID8At5YE/CbQu2bZqxLQc9e4LGxWZnUO5kxS7SIkr+Gw+/5esTb8qL7JNKogwzpE+EJFqUAnwaj3NU6BQjL37jilJ+EmplmUb5AKH5PcakAmMaFWQcJTnSJku6WHSThXEJjygrYuwSrzbf+80aEQbk67/Z3H2Q4qPGydrBjeMSsAbjdF4QBwEg2b6NZnFS4uIdSh5RedW63yhPrYD+eDyGw+Fed7DZQ30z3OcgyudAq73QT/YGDIKTzadKBEBBflr1/tBqdxY1mO+L6T4H01nHPR9dlQHPzm9X3mvNt+L3kj1XvHO5+bW6b+ef11vUQfOd+taPc516XXFEsFYNveqaKxpwOQqgbaI9OKvXFg/ePxTYxCzyQoH2WKD8GtcoMKxjzW2cRbj9GgfyTSjBspbw+sOPFEW8aFZiAcGNOtBu7tCCKWWEUDdRiicklLAHLOzM4tGoPH2QoTx+iR7WlyptbFG+vvULdQcX7k15LRyQ5gR6xCACDOuadMMmJECORYpAhD8/vtwHo+vZlz8nvy7vF3/Mvn0KuyDCcJk9Vj76LTwMsafM4+EehGBhOBiiV2D4rX/S0KnrLwAfvOo/+7APnL2s69YvG3idvnesmei5chix6PanYRxUEO6XkjncrWWK+Mfm/odCDzfZsoUjD4nt3iP4BG5Nf8DioA/v4xGBPlztOgZ9jkEfYMuBH2Ax+BMqgR9g98MfSp9JBSC3FYAMDSKdBlGecBsa5IRokCJbGhbk9FgQ6gyV0CBcTOclPUcElXlpl2U8eZ5hI/BbaIyKAn2VUWMgPCfYmxBY56pYloywfYVsBFezockHjskHHEtOPmD79XzAV5IOOFa/dMD2FaQDlHlVSEi8G/xxIAd/HJX4wyW6DQB1ByBXDgA5kAEgRw0Cuf0QyIEqEKhpkmI62xzbcWaj3D2SEyrl1Q42wOlo5ILrc47P4Iq4Hjb85+SR26xqTTlWVjnWCbqpu7/diCvH7v0+DCepg5N09HOStuEkDSdpOElxnKRL0zp9nCRoLzQdlFjiV//tB/vdkJJbZ66LlASeWP29xkra56rZRhcpWzcrCUwnqV6BdqM3k8ewAoeF3Y3L2jpYSdfr2UnKVsEJiC+JvMZKvh/8CbSzkqYschT+eEAO/gQ68McDPfEnUIA/28GJb42TfJVrpJ+Saq7Rsxm0kMw1wg6VDcM1SuMaPaebuvvbjWSukZqJ4RoN16iTa9QgRhvXqJz2NFTjGVGNHmWl9FGNUPCgGvzFnnHvR1Z/TlN/SsfMwOBUY20auqiOtf1QcazdIV83sba0WDuwBMXaDbuRHGvT78PE2jpibU9/rO2aWNvE2ibWFhZrB0pnXOFjuuCpM9ZlfXi+0bbf4st1lfXtdv5LSlnfPVfNsmW1QHtZ3+7Q/daU1SoaZOc+kVPWD7SU9YOeZf1ARVnfbu/oK6usD98N/vjhB7eJQEoL+zavz59BoM4IFFrwA5CAQL6377KSECi0qEF2xiDfqp8hCYVkze4rn3AMOpqvYAYqdBnEkEw42mZeR52EY+h1U3d/u5FNOLZ3GzSEoyEcJYjAJxw1iNFGOCrnPt8+4dguiuEbGfymOYZGvpFXezxqbqP0nIv7fn1GzK0nryiQpz95E2I6kmLtN7mShuPpic+B4vjc6ZDnm/hcWnzeVd2vX4jtxSs5PnfaxxSa+NxMPqosPtcgxpsf6G/WYDnJEB3oD9EdwdOPrrsEgDOO0RkNcmJ0pV0CHMXzj57xggRMTQ4C3X0CHDPUtleozZbOoFPvE+DCwyLvxnWBlgkAodOvVwBdb1TuigS8bsLGRrvbaMjYqCAT9fSYaNjTRD0VJmpGG+skPE5hoTm+3UCO3cjt8HTG0RWz3BPkjTxUOnTNtZsocBo9SVxNiz4DIG/VZ/6D8rocGuRWhdygQTsczFU3LEc2dpuVsjSS1YF+stp0JjGdSQxVLZCq3gZn+rhqV/Cspu9s+NrOnesiq13BxQYzfm2rWu2rZbm8QoQhAjsTgVhbUphAbBo6hrDhx+m5fi5QsWKWyyupyOV0rHcDQlD7klk0HjEgdCgIuXJACDJrZjHXlQZCPdfMAtQ0pYKQZwoSWmktKGqtJQAVL7bkyShJ5GZ69W2Xe06GBJSmSJ6ZB+IoDxY6UIYDC4GnwX/RIZhd3VdIlgwV5b3iUfC8+gUsPn+H2fzlZfLz40t0KXgOoVNeWqYBNJwvpTP2eCpTeK5mec5FO/QwONPpDeuBHl9OAu/V83c16bvfM3v3BCfvXPsUPFPwiefuQsHHB0onoOGqt0Ox38DPHvhx6vPPMEBxKPz4oD7/jJrIx3f6zj/jA9Hzz3CNVPDCiGeQgInFIc54CmkJGFfBvBqjQaHuKBRIyb98V0f+Refc7oxBroL8y9RQ5UBPGOrOv95kBfV0oAdYUEoCFoYaEjD8MP0ysDBUkIGZ6qk0+AGWrT8Fo710DQQdCkGelCQMG4eOLAw/Tt80DEuqJA8D4mnoLokYfD9wxFnDXm0mBqABo6PACMcQUjpzWL6OZGw7hqQ7FPlHpWN4M0tLkNgdnkWL6e8YP8oj/gU=</diagram></mxfile>

View File

@ -0,0 +1,13 @@
@startuml RNPCalc
scale 2
class encapsulation.RPNCalc {
-stack: Stack
+push(double num): void
+pop(): double
+getSize(): int
+peek(int i): double
+performOperation(char op): void
}
@enduml

23
diagrammer/ex4/Person.wsd Normal file
View File

@ -0,0 +1,23 @@
@startuml Person
class objectstructures.Person {
-name: String
-gender: char
-children: List<Person>
-mother: Optional<Person>
-father: Optional<Person>
+getGender(): char
+getChildren(): Collection
+getChildCount(): int
+getChild(int n): Person
+getMother(): Person
+getFather(): Person
+addChild(Person child): void
+setFather(Person father): void
+setMother(Person mother): void
+removeChild(Person child): void
+getName(): String
-setParent(Person newParent, Optional<Person> currentParent, char gender): void
}
@enduml

View File

@ -0,0 +1,31 @@
@startuml StringMergingIterator
actor Actor as a
participant StringMergingIterator as smi
entity Iterator1 as i1
entity Iterator2 as i2
a -> smi : next()
smi -> i1 : hasNext()
i1 -> smi : true
smi -> i2 : hasNext()
i2 -> smi : true
smi -> i1 : next()
i1 -> smi : next value
smi -> a : next value
a -> smi : next()
smi -> i1 : hasNext()
i1 -> smi : true
smi -> i2 : hasNext()
i2 -> smi : true
smi -> i2 : next()
i2 -> smi : next value
smi -> a : next value
a -> smi : next()
smi -> i1 : hasNext()
i1 -> smi : false
smi -> i2 : next()
i2 -> smi : next value
smi -> a : next value
@enduml

13
oppgavetekster/README.md Normal file
View File

@ -0,0 +1,13 @@
# Øvingstekster
Denne mappen inneholder øvingstekster for TDT4100 - Objektorientert programmering våren 2021. Tabellen under inneholder linker til hver enkelt oppgavetekst og tema for øvingen. Linker vil bli oppdatert underveis.
| Øving | Tema |
| ----------------- | ---------------------------------------- |
| [Øving 1](oving1/README.md) | Tilstand og oppførsel |
| Øving 2 | Innkapsling og validering |
| Øving 3 | Klasser og testing |
| Øving 4 | Objektstrukturer |
| Øving 5 | Grensesnitt |
| Øving 6 | Observatør-observert og delegering |
| Øving 7 | Arv og abstrakte klasser |

View File

@ -0,0 +1,27 @@
# Tilstand og oppførsel Account
Oppgaven handler om en `Account`-klasse, som håndterer data i en konto.
Tilstanden i `Account`-objekter er som følger:
- `balance` - et desimaltall som angir beløpet som er på kontoen
- `interestRate` - et desimaltall som angir rentefot som prosentpoeng.
`Account`-klassen har fem metoder, med følgende oppførsel:
- `deposit(double)` - øker konto-beløpet med den angitte argument-verdien (et desimaltall), men kun dersom det er positivt
- `addInterest()` - beregner renta og legger det til konto-beløpet
- `getBalance()` - returnerer beløpet som er på kontoen.
- `getInterestRate()` - returnerer rentefoten
- `setInterestRate(double)` - oppdaterer renten til å være den nye verdien
## Del 1 - Tilstandsdiagram
Tegn et objekttilstandsdiagram for en tenkt bruk av `Account`-klassen. Velg selv en passende start-tilstand for `Account`-objektet og sekvens av kall.
## Del 2 - Java-kode
Skriv Java-kode for `Account`-klassen med oppførsel som er beskrevet over.
Lag en passende `toString()`-metode og en `main`-metode, slik at du kan sjekke at oppførselen stemmer med tilstandsdiagrammet (bruk samme start-tilstand og sekvens av kall)
Testkode for denne oppgaven finner du her: [src/test/java/stateandbehavior/AccountTest.java](../../src/test/java/stateandbehavior/AccountTest.java).

View File

@ -0,0 +1,32 @@
# Tilstand og oppførsel Digit
Oppgaven handler om en `Digit`-klasse, som representerer ett siffer i et gitt tallsystem.
Tilstanden i `Digit`-objekter er todelt:
- en `int` som angir tallsystemet, f.eks. 10 for titallssystemet, som oppgis i konstruktøren
- en `int`-verdi, som representerer siffer-verdien i det angitt tallsystem og må være fra og med 0 til (men ikke med) tallsystem-verdien
Konstruktøren tar inn en `int`, som initialiserer tallsystem-verdien, mens siffer-verdien settes til 0.
`Digit`-tilstanden leses og oppdateres med følgende metoder:
- `int getValue()` - returnerer siffer-verdien
- `boolean increment()` - øker siffer-verdien med én. Dersom den da når tallsystem-verdien, så (re)settes den til 0 og metoden returnerer `true`, ellers returnerer den `false`.
- `int getBase()` - returnerer tallsystemet.
## Del 1 - Tilstandsdiagram
Tegn et objekttilstandsdiagram for en tenkt bruk av `Digit`-klassen. Sekvensen av kall må illustrere hele oppførselen til `increment()`-metoden.
## Del 2 - Java-kode
Skriv Java-kode for `Digit`-klassen med oppførsel som er beskrevet over. Lag også en `toString()`-metode som returnerer siffer-verdien som en `String`, hvor sifrene 0-9 etterfølges av A-Z. F.eks. skal siffer-verdien 10 i sekstentallssystemet gi "A" som returverdi.
Lag en `main`-metode, slik at du kan sjekke at oppførselen stemmer med tilstandsdiagrammet (bruk samme sekvens av kall).
Testkode for denne oppgaven finner du her: [src/test/java/stateandbehavior/DigitTest.java](../../src/test/java/stateandbehavior/DigitTest.java).
## Ekstraoppgave
La hovedprogrammet opprette tre `Digit`-objekter, som fungerer som sifre i et tre-sifret tall. Lag en løkke som teller fra 0 til høyest mulig verdi som kan representeres i tallsystemet. Legg så til 1 så verdien av det tre-sifrede tallet igjen blir 0.

View File

@ -0,0 +1,42 @@
# Tilstand og oppførsel LineEditor
Oppgaven omhandler en `LineEditor`-klasse, som håndterer data for redigering av en linje med tekst (altså tekst uten linjeskift).
`LineEditor`-klassen holder rede på en `String`-verdi og en tekstinnsettingsposisjon og har metoder for å redigere teksten. Tekstinnsettingsposisjonen er et heltall som angir hvor endringer skjer, tilsvarende en (blinkende) cursor som står mellom tegn. Verdien $`0`$ tilsvarer foran første tegn, og høyeste mulige verdi er lengden på teksten, som tilsvarer at cursoren står bak siste tegn. Tilstanden er altså som følger:
- `text` (en `String`-verdi) - teksten
- `insertionIndex` - heltall som angir hvor i teksten redigeringer vil skje
Klassen har metoder med følgende oppførsel:
- `void left()` - flytter tekstinnsettingsposisjonen ett tegn til venstre (tilsvarende bruk av venstre piltast)
- `void right()` - flytter tekstinnsettingsposisjonen ett tegn til høyre (tilsvarende bruk av høyre piltast)
- `void insertString(String s)` - skyter teksten angitt av argumentet `s` inn i teksten på tekstinnsettingsposisjonen og forskyver tekstinnsettingsposisjonen mot høyre tilsvarende
- `void deleteLeft()` - fjerner tegnet til venstre for tekstinnsettingsposisjonen
- `void deleteRight()` - fjerner tegnet til høyre for tekstinnsettingsposisjonen
- `String getText()` - Returnerer teksten
- `void setText(String)` - oppdaterer teksten til å være den nye teksten
- `int getInsertionIndex` - returnerer hvor i teksten redigeringer nå skjer
- `void setInsertionIndex(int)` - oppdaterer hvor i teksten redigeringer skal skje
## Del 1 - Objekttilstandsdiagram
Tegn et objekttilstandsdiagram for en tenkt bruk av `LineEditor`-klassen, som dekker bruk av alle metodene.
For alle metodene bør du tenke på hva som er fornuftig oppførsel for spesielle tilfeller, f.eks. når tekstinnsettingsposisjonen er helt til venstre eller høyre. Tenk også på hva som bør skje med tekstinnsettingsposisjonen når teksten endres.
## Del 2 - Java-kode
Skriv Java-kode for `LineEditor`-klassen med oppførsel som beskrevet over.
Lag en `toString()`-metode som viser teksten med tegnet `'|'` skutt inn på tekstinnsettingsposisjonen. Lag så en `main`-metode, slik at du kan sjekke at oppførselen stemmer med tilstandsdiagrammet (bruk samme start-tilstand og sekvens av kall).
Testkode for oppgaven finner du her: [src/test/java/stateandbehavior/LineEditorTest.java](../../src/test/java/stateandbehavior/LineEditorTest.java).
## Ekstraoppgave - frivillige utvidelser
- Legg til metoden `void insert(Object o)`, som skyter inn en tekstlig representasjon av objektet `o` som gis som argument.
- Legg til metoden `void left(int n)`, som flytter tekstinnsettingsposisjonen $`n`$ tegn til venstre, og skriv om `left()` til å bruke denne. Hva bør skje dersom tekstinnsettingsposisjonen er mindre enn $`n`$?
- Legg til metoden `right(int n)`, som flytter tekstinnsettingsposisjonen $`n`$ tegn til høyre, og skriv om `right()` til å bruke denne. Hva bør skje dersom det er færre enn $`n`$ tegn til høyre for tekstinnsettingsposisjonen?
- Utvid tilstanden og legg til metoder for å håndtere markering av region, tilsvarende bruk av shift og piltaster i en vanlig teksteditor. Husk at også de eksisterende metodene må endres til å håndtere dette riktig.

View File

@ -0,0 +1,27 @@
# Tilstand og oppførsel Location
Oppgaven handler om en `Location`-klasse, som holder rede på $`(x,y)`$-posisjonen til en figur som beveger seg i et rutenett.
`Location`-klassen har følgende metoder:
- `up()` - beveger figuren en rute opp
- `down()` - beveger figuren en rute ned
- `left()` - beveger figuren en rute til venstre
- `right()` - beveger figuren en rute til høyre
- `getX()` - returnerer x-posisjonen til figuren
- `getY()` - returnerer y-posisjonen til figuren
Merk at konvensjonen innen datagrafikk og rutenettbaserte spill er at $`x`$ øker mot høyre (som i matte) og $`y`$ nedover (altså motsatt av i matte).
## Del 1 - Tilstandsdiagram
Tegn et tilstandsdiagram for en tenkt bruk av Location-klassen. Velg selv passende tilstandsvariable for $`(x,y)`$-posisjonen.
## Del 2 - Java-kode
Skriv Java-kode for `Location`-klassen med oppførsel som beskrevet over.
Lag en passende `toString()`-metode og en `main`-metode, slik at du kan sjekke at oppførselen stemmer med tilstandsdiagrammet (bruk samme start-tilstand og sekvens av kall).
Testkode for oppgaven finner du her: [src/test/java/stateandbehavior/LocationTest.java](../../src/test/java/stateandbehavior/LocationTest.java).

View File

@ -0,0 +1,47 @@
# Øving 1: Objekter og klasser, tilstand og oppførsel
__Øvingsmål__:
- Bli kjent med Java-syntaks og bruk av Eclipse
- Lære (enkel) objektorientert tankegang
- Lære å lage enkle Java-klasser og -programmer
__Øvingskrav__:
- Kunne tegne enkle tilstandsdiagrammer
- Kunne deklarere klasser, med data og kode, iht. oppgavespesifikasjon
- Kunne skrive main-metoder for å teste objekter
- Kunne bruke standardtyper og -metoder (e.g. toString()-metoden)
## NB: Viktig beskjed!
For å få testene og eventuell kode til øvingene lokalt brukes systemet git. I Eclipse kan du klikke på Git --> Pull i menylinja for å hente den nye øvingen ved hjelp av dette. Om du ikke har denne i menylinjen, er det også mulig å høyreklikke på en av prosjektmappene og velge Team --> Pull.
## Dette må du gjøre
Oppgavene for denne øvingenskal du lagre i `ovinger/src/main/java/stateandbehavior`. Test-filene som kjøres for å versifisere ligger i `ovinger/src/test/java/stateandbehavior`.
Hvis du ikke allerede har gjort det, må du installere Eclipse med det ferdigkonfigurerte oppsettet for TDT4100. Se [denne](https://www.ntnu.no/wiki/display/tdt4100/Installasjon+av+Eclipse) wikisiden.
Du skal velge og gjennomføre minst tre av de følgende oppgavene angående [Tilstand og oppførsel](https://www.ntnu.no/wiki/pages/viewpage.action?pageId=65937373).
- [Account (Lett)](./Account.md)
- [Location (Lett)](./Location.md)
- [Digit (Lett)](./Digit.md)
- [UpOrDownCounter (Medium)](./UpOrDownCounter.md)
- [LineEditor (Vanskelig)](LineEditor.md)
- [Stopwatch (Vanskelig)](Stopwatch.md)
- [Rectangle (Vanskelig)](Rectangle.md)
Oppgavene er merket med en vanskelighetsgrad relativt til hverandre. Det er en god idé å begynne med de lettere oppgavene dersom du ikke er komfortabel med pensum så langt, men det er anbefalt å prøve seg på de vanskeligere oppgavene om du synes de første oppgavene er uproblematiske. Dersom du allerede føler deg trygg på punktene i øvingskravene kan du forsøke å gå rett på de vanskeligere oppgavene. Du er selvfølgelig velkommen til å løse flere oppgaver enn minstekravet, hvilket lurt gjøres med tanke på eksamen og et langt liv som programmerende.
Før du setter i gang kan det vært lurt å lese wiki-siden om [Tilstand og oppførsel](https://www.ntnu.no/wiki/pages/viewpage.action?pageId=65937373) nøye, samt ta en titt på det tilhørende `Counter`-eksempelet. Forelesningene og tilhørende øvingsforelesning er selvsagt også lure å få med seg
Det finnes også masse ressurser på [wikien](https://www.ntnu.no/wiki/display/tdt4100/Faginnhold) om hvordan ulike metoder skal fungere. F.eks `toString`-metoden og metoder for teksthåndtering. Naviger deg litt rundt om du lurer på noe.
### Hjelp/Mistanke om bugs
Ved spørsmål eller behov for hjelp konsulter studassen din i saltiden hans / hennes. Du kan også oppsøke andre studasser på sal eller legge ut et innlegg på Piazza.
### Godkjenning
Last opp kildekode på Blackboard innen den angitte innleveringsfristen. Innlevert kode skal demonstreres for stud.ass innen én uke etter innleveringsfrist. Se for øvrig Blackboard-sidene for informasjon rundt organisering av øvingsopplegget og det tilhørende øvingsreglementet.

View File

@ -0,0 +1,65 @@
# Tilstand og oppførsel Rectangle
Oppgaven handler om en `Rectangle`-klasse, som håndterer et firkantet område i et koordinatsystem, med sider parallelle med koordinataksene.
Tilstanden i `Rectangle`-objekter er ikke spesifisert eksplisitt, men må velges slik at metodene nedenfor kan implementerers. Merk at alle metodene jobber med heltallsstørrelser.
`Rectangle`-klassen har metoder for å spørre om tilstanden og endre tilstanden. Spørremetodene dekker høyde og bredde og koordinatene til øverste venstre og nederste høyre hjørne og om rektanglet inneholder spesifikke punkt(er). Endringsmetodene dekker utviding ved å legge til punkter.
**Merk**: `Rectangle`-klassen fungerer som om du krysser ut et rutenett, altså at et punkt ("kryss") har en lengde og bredde på 1, ikke som i matematikk der punkter ikke har størrelser. Bildeserien under viser et eksempel på hvordan bredde og høyde endres når man legger til punkter i rektangelet.
<img alt="Tomt rektangel" src="fig/rectangle/step1.png" width="500px" />
<br />
<img alt="Rektangel med et punkt" src="fig/rectangle/step2.png" width="500px" />
<br />
<img alt="Rektangel steg 3" src="fig/rectangle/step3.png" width="500px" />
<br />
<img alt="Rektangel steg 4" src="fig/rectangle/step4.png" width="500px" />
<br />
Logikken til metodene må tilfredsstille følgende krav:
- et `Rectangle`-objekt er tomt i starten
- et tomt `Rectangle`-objekt skal returnere $`0`$ for alle `get`-metodene (`minX`, `minY`, `maxX`, `maxY`), `true` for `isEmpty` og `false` for `contains`-metodene.
- et punkt har bredde og høyde lik $`1`$, så dersom en legger punktet $`(x,y)`$ til et tomt `Rectangle`-objekt, så skal `getMinX` og `getMaxX` returnere $`x`$, `getMinY` og `getMaxY` skal returnere $`y`$ og `getWidth` og `getHeight` skal returnere $`1`$.
- når en utvider et `Rectangle`-objekt med en av `add`-metodene, så skal ikke rektanglet bli større enn nødvendig.
Spørremetoder:
- `int getMinX()` og `int getMinY()` - returnerer henholdsvis $`x`$- og $`y`$-koordinatene til punktet med lavest $`(x,y)`$-verdier som er inneholdt i dette rektanglet. Dersom dette rektanglet er tomt, så skal $`0`$ returneres.
- `int getMaxX()` og `int getMaxY()` - returnerer henholdsvis $`x`$- og $`y`$-koordinatene til punktet med høyest $`(x,y)`$-verdier som er inneholdt i dette rektanglet. Dersom dette rektanglet er tomt, så skal $`0`$ returneres.
- `int getWidth()` og `int getHeight()` - returnerer henholdsvis bredden og høyden til rektanglet. Begge skal returnere $`0`$, dersom dette rektanglet er tomt.
- `boolean isEmpty()` - returnerer `true` om rektanglet er tomt, dvs. om bredden og/eller høyden er $`0`$.
- `boolean contains(int x, int y)` - returnerer `true` om punktet $`(x,y)`$ er inneholdt i dette rektanglet, og `false` ellers.
- `boolean contains(Rectangle rect)` - returnerer `true` om hele `rect`, dvs. alle punktene i `rect`, er inneholdt i dette rektanglet, og false ellers. Dersom `rect` er tomt, så skal `false` returneres.
Endringsmetoder:
- `boolean add(int x, int y)` - utvider (om nødvendig) dette rektanglet slik at det (akkurat) inneholder punktet $`(x,y)`$. Etter kallet skal altså `contains(x, y)` returnere `true`. Returnerer `true` om dette rektanglet faktisk ble endret, ellers `false`.
- `boolean add(Rectangle rect)` - utvider (om nødvendig) dette rektanglet slik at det (akkurat) inneholder hele `rect`-argumentet. Returnerer `true` om dette rektanglet faktisk ble endret, ellers `false`. Dersom `rect` er tomt, så skal dette rektanglet ikke endres.
Andre metoder:
- `Rectangle union(Rectangle rect)` - returnerer et nytt `Rectangle`-objekt som tilsvarer kombisjonen av dette rektanglet og `rect`-argumentet. Alle punktene som finnes i ett av rektanglene skal altså være inneholdt i rektanglet som returneres.
## Del 1 - Tilstandsdiagram
Tegn et objekttilstandsdiagram for en tenkt bruk av `Rectangle`-klassen. Velg en sekvens av kall som bruker alle fire endringsmetoder.
## Del 2 - Java-kode
Skriv Java-kode for `Rectangle`-klassen med oppførsel som er beskrevet over.
Lag en passende `toString()`-metode og et hovedprogram, slik at du kan sjekke at oppførselen stemmer med tilstandsdiagrammet (bruk samme sekvens av kall).
Testkode for denne oppgaven finner du her: [src/test/java/stateandbehavior/RectangleTest.java](../../src/test/java/stateandbehavior/RectangleTest.java).
## Ekstraoppgave
Implementer følgende metoder:
- `Rectangle intersection(Rectangle rect)` - returnerer et nytt `Rectangle`-objekt som tilsvarer overlappet mellom dette rektanglet og `rect`-argumentet. Alle punktene som finnes i begge rektanglene skal altså være inneholdt i rektanglet som returneres.
- `boolean intersects(Rectangle rect)` - returnerer `true` om dette rektanglet og `rect`-argumentet overlapper, dvs. om det finnes ett eller flere punkter som er inneholdt i begge disse rektanglene.

View File

@ -0,0 +1,48 @@
# Tilstand og oppførsel Stopwatch
Oppgaven handler om en `StopWatch`-klasse, som implementerer en stoppeklokke med funksjoner for å starte, ta rundetider og stoppe.
Klassen skal implementere logikken ofte funnet på stoppeklokker på smarttelefoner.
<img alt="Stopwatch" src="fig/stopwatch.gif" width="250px" style="float: right" />
For å holde styr på tiden som går vil `StopWatch`-klassen bli kalt utenfra (altså noe du slipper å implementere i denne oppgaven) på en `tick(int)`-metode. I dataverdenen er et tick (tikk på norsk) minste målbare diskret tidsenhet. I denne oppgaven er 1 tikk = 1 millisekund. F.eks. vil et kall `tick(3)` fortelle en `StopWatch`-instans at $`3`$ tikk har gått siden sist gang `tick()` ble kalt.
StopWatch skal fungere på følgende måte:
- Klokken startes når man ønsker å starte tidtakningen.
- Alle tikk etter at klokken har blitt startet skal medregnes i tidtakningen.
- Klokken stoppes når man ønsker å stoppe tidtakningen. Da er tidtakningen helt ferdig. Det er ikke en pause i tidtakningen - klokken skal ikke startes igjen.
- Det skal være mulig å ta rundetider.
- Første rundetid startes når klokken starter.
- Under tidtakningen kan man starte en ny rundetid, og dermed implisitt avslutte den nåværende.
- Siste rundetid avsluttes når klokken stoppes.
`StopWatch`-klassen må ha metoder for å spørre om tilstanden og metoder for å endre tilstanden.
Klassen skal ha følgende spørremetoder:
- `boolean isStarted()` - returnerer `true` om klokken har blitt startet eller `false` om den ikke har blitt startet
- `boolean isStopped()` - returnerer `true` om klokken har blitt stoppet eller `false` om den ikke har blitt stoppet. Merk at her snakker vi om at klokken har blitt stoppet, ikke om klokken går eller ikke.
- `int getTicks()` - returnerer det totale antall tikk (millisekunder) som har gått i levetiden til klokken uavhengig om klokken har vært startet eller stoppet.
- `int getTime()` - returnerer antall tikk som har gått under tidtakningen. Hvis tidtakningen ikke har startet returner $`-1`$. Merk at hvis klokken er startet, men ikke stoppet, skal metoden returnere antall tikk som har gått siden klokken ble startet til nå. Hvis klokken er stoppet skal metoden returnere antall tikk som har gått fra klokken ble startet til klokken ble stoppet.
- `int getLapTime()` - returnerer antall tikk som har gått under nåværende rundetid til nå. Hvis tidtakningen ikke har startet returner $`-1`$.
- `int getLastLapTime()` - returnerer lengden på forrige avsluttede rundetid. Hvis det ikke er noen tidligere rundetider returner $`-1`$.
Klassen skal ha følgende metoder for å endre tilstanden:
- `void tick(int ticks)` - forteller klokken at `ticks` antall tikk har gått.
- `void start()` - starter klokken.
- `void lap()` - avslutter nåværende rundetid og starter en ny.
- `void stop()` - stopper klokken.
## Del 1 - Tilstandsdiagram
Tegn et objekttilstandsdiagram for en tenkt bruk av `StopWatch`-klassen. Velg en sekvens av kall som både starter klokken, tar rundetider og stopper klokken.
## Del 2 - Java-kode
Skriv Java-kode for `StopWatch`-klassen med oppførsel som er beskrevet over.
Lag en passende `toString()`-metode og en `main`-metode, slik at du kan sjekke at oppførselen stemmer med tilstandsdiagrammet (bruk samme sekvens av kall).
Testkode for denne oppgaven finner du her: [src/test/java/stateandbehavior/StopWatchTest.java](../../src/test/java/stateandbehavior/StopWatchTest.java).

View File

@ -0,0 +1,14 @@
# Tilstand og oppførsel  UpOrDownCounter
I denne oppgaven skal du programmere en teller, ikke ulik den som er beskrevet i [Tilstand og oppførsel](https://www.ntnu.no/wiki/pages/viewpage.action?pageId=65937373) og kodet i [Tilstand og oppførsel - Java-kode for Counter-eksempel](https://www.ntnu.no/wiki/pages/viewpage.action?pageId=65937823), som også skal håndtere tilfellet hvor slutt-verdien er lavere enn start-verdien ved å telle nedover.
## Del 1 - Java-kode
`UpOrDownCounter`-klassen skal altså ha de samme konstruktørene/metodene:
- `UpOrDownCounter(int start, int end)` - initialiserer objektet med angitte start- og slutt-verdier, hvor altså slutt kan være større eller mindre enn start, slik at telleren teller henholdsvis opp eller ned. Lik start og slutt-verdi skal utløse unntak av typen `IllegalArgumentException` (se [Koding av valideringsmetoder](https://www.ntnu.no/wiki/display/tdt4100/Koding+av+valideringsmetoder)).
- `int getCounter()` - returnerer telleren
- `boolean count()` - beveger telleren i retning av slutt-verdien og returnerer true så lenge den ikke har nådd den, altså om telleren har mer igjen, og false ellers.
Testkode for oppgaven finner du her: [src/test/java/stateandbehavior/UpOrDownCounterTest.java](../../src/test/java/stateandbehavior/UpOrDownCounterTest.java)

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

View File

@ -0,0 +1,36 @@
# Innkapsling - Account-oppgave
Oppgaven er en innkapslet og litt utvidet variant av [Account-oppgaven](../oving1/Account.md) under temaet [Tilstand og oppførsel](https://www.ntnu.no/wiki/pages/viewpage.action?pageId=65937373), og stiller bl.a. større krav til validering.
Et `Account`-objekt inneholder data om beløpet som står på kontoen og rentefoten (prosentpoeng).
Begge verdiene skal oppgis og settes når objektet opprettes og ingen av verdiene kan være negative.
`Account`-klassen har metoder for å sette inn og ta ut beløp, og legge til påløpte renter, i tillegg til en konstruktør for å initialisere en ny konto. Alle disse skal utløse unntak av typen `IllegalArgumentException`, dersom et argument ikke tilfredstiller kravene som angis.
- `Account(double, double)` - Konstruktøren skal ta inn startbeløpet og rentefoten (prosentpoeng). Ingen av disse kan være negative.
- `double getBalance()` - Returnerer beløpet som står på kontoen.
- `double getInterestRate()` - Returnerer renten på kontoen.
- `void setInterestRate(double)` - Denne metoden tar inn en ikke-negativ verdi og setter renten til denne verdien.
- `void deposit(double)` - Denne metoden tar inn et ikke-negativt beløp og øker konto-beløpet tilsvarende.
- `void withdraw(double)` - Denne metoden tar inn et ikke-negativt beløp og minsker konto-beløpet tilsvarende. Dersom det nye konto-beløpet er negativt, så skal tilstanden ikke endre, og det skal utløses et unntak av typen `IllegalArgumentException`.
- `void addInterest()` - beregner renta og legger det til konto-beløpet.
__Leseliste__
- [Gyldig tilstand](https://www.ntnu.no/wiki/display/tdt4100/Gyldig+tilstand) - Tilstanden til et objekt er verdien av alle attributtene. En viktig del av [oppførselen til et objekt](https://www.ntnu.no/wiki/pages/viewpage.action?pageId=65937373) er å sikre at tilstanden til objektet alltid er *gyldig*, dvs. at alle attributtene har gyldige/konsistente verdier.
- [Innkapsling](https://www.ntnu.no/wiki/display/tdt4100/Innkapsling) - Innkapsling er en programmeringsteknikk som har som formål å hindre direkte tilgang til tilstanden til et objekt fra objekter av andre klasser.
- [Koding av valideringsmetoder](https://www.ntnu.no/wiki/display/tdt4100/Koding+av+valideringsmetoder) - En valideringsmetode har som formål å sjekke om en eller flere verdier er gyldige, slik at dette kan sjekkes av f.eks. setter-metoder før tilsvarende attributter evt. settes.
## Del 1: Innkapsling og validering - Teori
Ta utgangspunkt i koden fra [Account](../oving1/Account.md)-klassen og besvar følgende spørsmål:
- Forklar hvorfor metodene over kan sies å være en komplett innkapsling av tilstanden?
- Er denne klassen data-orientert eller tjeneste-orientert? Begrunn svaret!
## Del 2 - Java-kode
Implementer endringene fra [Account](../oving1/Account.md)-klassen i den nye `Account`-klassen med oppførsel som beskrevet over.
Testkode for denne oppgaven finner du i [encapsulation/AccountTest.java](../../src/test/java/encapsulation/AccountTest.java).

View File

@ -0,0 +1,39 @@
# Innkapsling og gyldig tilstand - Oppgave om innkapsling og validering av klasser
I denne oppgaven skal du velge tre oppgaver som du har gjort i øving 1 fra listen nedenfor, og innkapsle og validere disse klassene.
Skriv svar (stikkord/få, korte setninger) på spørsmål 1-4 (fra del 1 nedenfor) som kommentarer i koden din.
__Oppgaver__:
- [Digit-oppgave](../oving1/Digit.md) (Lett)
- [UpOrDownCounter-oppgave](../oving1/UpOrDownCounter.md) (Lett)
- [Location-oppgave](../oving1/Location.md) (Lett)
- [StopWatch-oppgave](../oving1/Stopwatch.md) (Medium)
- [Rectangle-oppgave](../oving1/Rectangle.md) (Vanskelig)
- [LineEditor-oppgave med fri peker](LineEditor.md) (Vanskelig)
Merk at spesifikasjonen for [LineEditor](../oving1/LineEditor.md) er litt utvidet for denne oppgaven. Se [LineEditor-oppgave med fri peker](LineEditor.md).
## Del 1: Innkapsling og validering - Teori
Ta utgangspunkt i (koden for) den originale klassen og besvar følgende spørsmål:
- Hvordan skal private og public brukes for at denne klassen skal være korrekt innkapslet?
- Hva slags validering bør legges til for å sikre gyldig tilstand?
- Hvilke metoder må evt. legges til?
- Vil du karakterisere denne klassen som data-orientert eller tjeneste-orientert. Begrunn svaret!
## Del 2: Java-kode
Implementer endringene foreslått i punktene 1-3 og prøv ut klassene.
Testkoder for denne oppgaven finner du her:
- [encapsulation/DigitTest.java](../../src/test/java/encapsulation/DigitTest.java)
- [encapsulation/LineEditorTest.java](../../src/test/java/encapsulation/LineEditorTest.java)
- [encapsulation/RectangleTest.java](../../src/test/java/encapsulation/RectangleTest.java)
- [encapsulation/UpOrDownCounterTest.java](../../src/test/java/encapsulation/UpOrDownCounterTest.java)
- [encapsulation/StopWatchTest.java](../../src/test/java/encapsulation/StopWatchTest.java)
Testkodene viser om du har innkapslet på samme måte som fagstaben har gjort. Din kode kan fungere selv om testene feiler, dersom du har valgt en løsere/strammere innkapsling iht. argumentasjonen i 1-3. Er du enig med hvordan fagstaben har gjort det?

View File

@ -0,0 +1,9 @@
# Gyldig tilstand - LineEditor-oppgave med fri peker
Oppgaven utvider [Tilstand og oppførsel - LineEditor-oppgave](../oving1/LineEditor.md) med validering.
Denne oppgaven tar utgangspunkt i [Tilstand og oppførsel - LineEditor-oppgave](../oving1/LineEditor.md) og utvider `LineEditor`-klassen med metoder for å endre teksten og tekstinnsettingsposisjonen direkte, så det blir enklere å gjøre om tilstanden til objektet. Når teksten endres skal tekstinnsettingsposisjonen settes til å være bak teksten.
Hvordan vil du implementere dette med én eller flere metoder, inkludert valideringsmetode(r), slik at en er sikret at `LineEditor`-objekter aldri blir ugyldige?
JExercise-testkode for denne oppgaven finner du her: [tests/encapsulation/LineEditorTest.java](../../tests/encapsulation/LineEditorTest.java).

View File

@ -0,0 +1,59 @@
# Innkapsling - Person-oppgave
Oppgaven handler om en `Person`-klasse, som håndterer informasjon om en person (navn, e-post, fødselsdato og kjønn) og implementerer innkapslingsmetoder med validering.
Et `Person`-objekt inneholder *navn* (både fornavn og etternavn), *e-post*, *fødselsdag* og *kjønn*:
- Navnet inneholder både fornavn og etternavn (og ingen mellomnavn), som begge må være på minst to bokstaver langt, navnene må være skilt med ett mellomrom og kun inneholde bokstaver.
- E-post-adressen (hvis den ikke er `null`) må være på formen `fornavn.etternavn@domene.landskode`, f.eks. `hallvard.trætteberg@ntnu.no` (en liste over landskoder finner du [her](http://pastebin.com/chG6WLWF)).
- Fødselsdagen skal være et dato-objekt (java.util.Date) og kan ikke være frem i tid.
- En persons kjønn skal kunne returneres som `'M'`, `'F'` eller `'\0'` (null-tegnet).
`Person`-klassen har tilgangsmetoder for å hente og sette tilstandene. Dersom et argument er ugyldig i seg selv, så skal unntaket `IllegalArgumentException` utløses.
- `setName(String)` - oppdaterer navnet (fornavn og etternavn med mellomrom mellom), dersom det er gyldig i henhold til kravene over. Det er greit om navnet som settes, ikke stemmer med e-post-adressen.
- `setEmail(String)` - oppdaterer e-post-adressen, etter å ha sjekket at den stemmer med navnet.
- `setBirthday(Date)` - oppdaterer fødselsdatoen
- `setGender(char)` - oppdaterer kjønnet
I tillegg til disse såkalte *setter*-metodene, så må `Person`-klassen ha tilsvarende *getter*-metoder.
__Leseliste__
- [Gyldig tilstand](https://www.ntnu.no/wiki/display/tdt4100/Gyldig+tilstand) - Tilstanden til et objekt er verdien av alle attributtene. En viktig del av [oppførselen til et objekt](https://www.ntnu.no/wiki/pages/viewpage.action?pageId=65937373) er å sikre at tilstanden til objektet alltid er *gyldig*, dvs. at alle attributtene har gyldige/konsistente verdier.
- [Innkapsling](https://www.ntnu.no/wiki/display/tdt4100/Innkapsling) - Innkapsling er en programmeringsteknikk som har som formål å hindre direkte tilgang til tilstanden til et objekt fra objekter av andre klasser.
- [Koding av valideringsmetoder](https://www.ntnu.no/wiki/display/tdt4100/Koding+av+valideringsmetoder) - En valideringsmetode har som formål å sjekke om en eller flere verdier er gyldige, slik at dette kan sjekkes av f.eks. setter-metoder før tilsvarende attributter evt. settes.
- [String-klassen](https://www.ntnu.no/wiki/display/tdt4100/java.lang.String) - Siden gir en innføring i String-klassen og en oversikt over nyttig String-metoder.
## Del 1 Java-kode
Implementer `Person`-klassen med stram innkapsling. Eventuelle hjelpemetoder for validering bør også ha stram innkapsling. Det kan være lurt å lese om [String-klassen](https://www.ntnu.no/wiki/display/tdt4100/java.lang.String) og dens metoder før du setter i gang.
Testkode for denne oppgaven finner du i [encapsulation/PersonTest.java](../../src/test/java/encapsulation/PersonTest.java).
Merk at din implementasjon må ligge i en pakke med samme navn som testkodens pakke. Pass derfor på at Person-klassen ligger i pakken `encapsulation`.
## Del 2 - Spørsmål om innkapsling
- Foreslå en alternativ innkapsling av navnet. Hint: del opp.
- Foreslå *to* alternative strategier for å kapsle inn tilstand som er koblet slik navn og e-post er. Hint: 1) samtidig og 2) dekoble.
## Ekstraoppgave - Personnummer
Utvid klassen med en persons personnummer. Personnummeret kan ikke settes før kjønn og fødselsdag er satt.
Et personnummer består grovt sett av fødselsdatoen, et (vilkårlig) løpenummer og to kontrollsifre. Kontrollsifrene gjør det enklere å sjekke om et personnummer er ekte. Mer spesifikt er reglene for personnummer som følger:
- Et personnummer består av 11 siffer, med følgende struktur: **D1D2**M1M2**Y1Y2**N1N2N3**K1K2** (fet skrift for lesbarhet).
- De seks første sifrene, **D1D2**M1M2**Y1Y2**, tilsvarer fødselsdatoens dag (1-31), måned (1-12) og år (0-99).
- De tre neste sifrene, N1N2N3, kan antas å være vilkårlige, men N3 må være partall for kvinner og oddetall for menn.
- De to siste sifrene, K1K2, er kontrollsifre, som hver for seg beregnes ut fra de foregående sifrene. Formelen for dem begge er `11 (VS % 11)`, hvor VS (veid sum) for `K1` er `D1*F1 + D2*F2 + … + N2*F8 + N3*F9` og `VS` for `K2` er `D1*G1 + D2*G2 + … + N3*G9 + K1*G10`. Fene og Gene er oppgitt i tabellen under. Dersom formelen gir tallet 11 så skal verdien 0 brukes isteden. Om både K1 og K2 stemmer med kontrollsifferne generert basert på formlene over, så er kontrollsifferne i personnummeret gyldig.
| | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
| F | 3 | 7 | 6 | 1 | 8 | 9 | 4 | 5 | 2 |
| G | 5 | 4 | 3 | 2 | 7 | 6 | 5 | 4 | 3 | 2 |
Implementer kode for å sette (med metoden `setSSN(String)` og validere et gyldig personnummer.
Testkode for denne oppgaven finner du i [encapsulation/PersonTest2.java](../../src/test/java/encapsulation/PersonTest2.java).

View File

@ -0,0 +1,49 @@
# Øving 2: Innkapsling og validering
__Øvingsmål__:
- Lære å innkapsle klasser og metoder etter god programmeringsskikk
- Lære å validere argumenter for å sikre gyldig tilstand
__Øvingskrav__:
- Kunne forstå og implementere hvordan en klasse best bør innkapsles
- Kunne skrive kode for å validere argumenter for å sikre gyldig tilstand
- Kunne utløse exceptions ved ugyldige argumenter i en metode
## NB: Viktig beskjed!
Etter at du har hentet ned/pullet denne øvingen, **må** du høyreklikke på prosjektet og kjøre *Maven -> Update Project*. Vi har fikset problemet med *.classpath*-filene, og det er veldig viktig at dette blir gjort for at prosjektet fortsatt skal funke! Hvis du har gjort endringer i en *.classpath*-fil vil du få en merge-conflict når du puller øvingen. Løsning på dette problemet finner du [her.](../classpath_fix/README.md)
## Dette må du gjøre
__Del 1: Teori__
Les [wikisiden om innkapsling](https://www.ntnu.no/wiki/display/tdt4100/Innkapsling) og svar på følgende:
- Hva er en **synlighetsmodifikator**?
- Hva er forskjellen på **private** og **public** og når brukes de?
Teori-oppgavene besvares i en tekstfil eller på papir, og gjennomgås med studass ved godkjenning.
__Del 2: Programmering__
Velg minst 2 av følgende oppgaver:
- [Innkapsling og validering av 3 eksisterende klasser](./Encapsulation.md) (Varierende)
- [Account](./Account.md) (Lett)
- [Person](./Person.md) (Medium)
- [Vehicle](./Vehicle.md) (Medium)
Oppgavene for denne delen skal du lagre i `ovinger/src/main/java/encapsulation`. Test-filene for å verifisere korrekt implementasjon ligger i `ovinger/src/test/java/encapsulation`.
Oppgavene er markert med en vanskeliggrad relativt til hverandre. Det er en god idé å begynne med de lettere oppgavene dersom du ikke er komfortabel med pensum så langt, men det er anbefalt å prøve seg på de vanskeligere oppgavene om du synes de første oppgavene er uproblematiske. Dersom du allerede føler deg trygg på punktene i øvingskravene kan du forsøke å gå rett på de vanskeligere oppgavene. Du er selvfølgelig velkommen til å løse flere oppgaver enn minstekravet, hvilket lurt gjøres med tanke på eksamen og et langt liv som programmerende.
Før du setter i gang kan det vært lurt å lese nevnte [wikiside om innkapsling](https://www.ntnu.no/wiki/display/tdt4100/Innkapsling) nøye. Forelesningene og tilhørende øvingsforelesning er selvsagt også lure å få med seg.
### Hjelp / mistanke om bugs
Ved spørsmål eller behov for hjelp konsulter studassen din i saltiden hans / hennes. Du kan også oppsøke andre studasser på sal eller legge ut et innlegg på Piazza.
### Godkjenning
Last opp kildekode på Blackboard innen den angitte innleveringsfristen. Innlevert kode skal demonstreres for stud.ass innen én uke etter innleveringsfrist. Se for øvrig Blackboard-sidene for informasjon rundt organisering av øvingsopplegget og det tilhørende øvingsreglementet.

View File

@ -0,0 +1,30 @@
# Innkapsling - Vehicle-oppgave
Oppgaven handler om en `Vehicle`-klasse, som håndterer informasjon om et kjøretøy og implementerer innkapslingsmetoder med validering.
Et `Vehicle`-objekt inneholder type *kjøretøy*, *drivstoffet* det bruker og *registreringsnummer*:
- Typen kan være enten motorsykkel eller bil.
- Drivstoffet kan være enten hydrogen, elektrisitet, diesel eller bensin. Kun biler kan gå på hydrogen.
- Gyldige registreringsnummeret avhenger av typen kjøretøy og drivstoff etter følgende regler:
- Kjøretøy som går på elektrisitet skal ha registreringsnummer som starter med bokstavene `"EL"` eller `"EK"`
- Hydrogenbilene har registreringsnummer som begynner med bokstavene `"HY"`
- Dieselkjøretøy og bensinkjøretøy har registreringsnummer som begynner på to bokstaver. De kan ikke være `"EK"`, `"EL"` eller `"HY"`. Bokstavene Æ, Ø og Å skal ikke brukes.
- For alle drivstoff gjelder det at det skal være brukt store bokstaver.
- Ellers så gjelder det at motorsykler har 4 sifre etter bokstavene, mens biler har 5.
Følgende metoder må implementeres:
- `Vehicle(char, char, String)` - Konstruktør der argument-rekkefølgen må være kjøretøystype, drivstofftype og registreringsnummer. Ved ugyldige argumenter utløses unntak av typen `IllegalArgumentException`.
- `getFuelType()` - returnerer type drivstoff som følgende: `'H'` for hydrogen, `'E'` for elektrisitet, `'D'` for diesel eller `'G'` for bensin.
- `getRegistrationNumber()` - returnerer registreringsnummeret
- `setRegistrationNumber(String)` - endrer registreringsnummeret dersom det er gyldig i henhold til kravene over, og utløser unntak av typen `IllegalArgumentException` dersom det ikke er gyldig.
- `getVehicleType()` - returnerer kjøretøystype: `'M'` for motosykkel, `'C'` for bil.
## Del 1 Java-kode
Implementer `Vehicle`-klassen som beskrevet over med stram innkapsling. Eventuelle hjelpemetoder for validering bør også ha stram innkapsling. Det kan være lurt å lese om [String-klassen](https://www.ntnu.no/wiki/display/tdt4100/java.lang.String) og dens metoder før du setter i gang.
Testkode for denne oppgaven finner du i [encapsulation/VehicleTest.java](../../src/test/java/encapsulation/VehicleTest.java).
Merk at din implementasjon må ligge i en pakke med samme navn som testkodens pakke. Pass derfor på at Vehicle-klassen ligger i pakken `encapsulation`.

View File

@ -0,0 +1,33 @@
# Innkapsling - Card-oppgave
Denne oppgaven handler om to klasser for kortspill: `Card` (kort) og `CardDeck` (kortstokk), der den siste inneholder ett eller flere `Card`-objekter.
I mange sammenhenger vil objekter av en klasse inneholde eller "eie" objekter av andre klasser. Når en klasse er assosiert med én instans av en (annen) klasse er dette en [1-1-assosiasjon](https://www.ntnu.no/wiki/display/tdt4100/Koding+av+1-1-assosiasjoner) og når en klasse er assosiert med flere instanser av en annen klasse er dette en [1-n-assosiasjon](https://www.ntnu.no/wiki/display/tdt4100/Koding+av+1-n-assosiasjoner). I denne oppgaven skal du implementere logikk for en kortstokk som inneholder kort. Nedenfor beskrives begge klassene og metodene disse skal inneholde.
`Card`-klassen er en såkalt *verdiklasse*, som kodes slik at objektene ikke kan endres etter at de er opprettet. Et `Card`-objekt har en *kortfarge*, som er en av bokstavene `'S'` (for spades), `'H'` (for hearts), `'D'` (for diamonds) og `'C'` (for clubs), og *tallverdi*, som er et heltall mellom 1 (ess) og 13 (konge). Følgende metoder må implementeres:
- `Card(char, int)` - konstruktøren initialiserer kortfarge og tallverdi med henholdsvis første og andre argument. Konstruktøren må utløse unntak av typen `IllegalArgumentException` hvis en (eller begge) av disse verdiene er ugyldige.
- `getSuit()` - returnerer kortfargen som en char, en av `'S'`, `'H'`, `'D'` eller `'C'`.
- `getFace()` - returnerer tallverdien som en int mellom 1 og 13 (inklusive).
- `toString()` - returnerer en streng som består av `<suit><face>` e.g. for spar ess skal `"S1"` returneres.
`CardDeck`-objekter inneholder initielt et visst antall kort av de fire kortfargene `'S'`, `'H'`, `'D'` og `'C'`. Klassen inneholder standardmetoder for å lese hvor mange og hvilke kort, og en metode for å endre tilstand.
Konstruktør:
- `CardDeck(int n)` - fyller kortstokken med de `n` første kortene av hver kortfarge, totalt `n*4` kort, med spar 1 som første kort (indeks nr. 0), spar 2 som andre (nr. 1), spar 3 som tredje (nr. 2), spar 4 som fjerde (nr. 3), ..., hjerter 1 som fjortende (nr. 13), hjerter 2 som femtende (nr. 4) osv. i.e. først alle spar, så hjerter, så ruter og så kløver, alle i stigende rekkefølge.
Lesemetoder:
- `getCardCount()` - returnerer hvor mange Card-objekter som `CardDeck`-objektet inneholder.
- `getCard(int n)` - returnerer kort nr. `n` eller utløser et `IllegalArgumentException` hvis `n` ikke er gyldig. Kort nr. 0 er det første kortet i kortstokken.
Endringsmetode:
- `shufflePerfectly()` - stokker kortstokken ved å dele den i to like store deler og flette de to delene perfekt, slik at kortet på toppen forblir på toppen og kortet på bunnen forblir på bunnen (se [http://en.wikipedia.org/wiki/Out_shuffle](http://en.wikipedia.org/wiki/Out_shuffle)).
## Oppgave: Java-kode
Skriv `Card`- og `CardDeck`-klassene, slik at de har ønsket oppførsel og er skikkelig innkapslet.
Testkode for denne oppgaven finner du i [encapsulation/CardDeckTest.java](../../src/test/java/encapsulation/CardDeckTest.java) og [encapsulation/CardTest.java](../../src/test/java/encapsulation/CardTest.java).

View File

@ -0,0 +1,37 @@
# Debugging - CoffeeCup-oppgave
Oppgaven handler om feilsøking i en **CoffeeCup**- og en **CoffeeCupProgram**-klasse ved bruk av [debuggeren](https://www.ntnu.no/wiki/display/tdt4100/Bruk+av+debuggeren+i+Eclipse) i Eclipse.
Et **CoffeeCup**-objekt inneholder kapasitet og nåværende volum av kaffe.
- Kapasitet må være et ikke-negativt flyttall, som til enhver tid må være større enn nåværende volum av kaffe.
- Nåværende volum av kaffe er et ikke-negativt flyttall som til enhver tid må være mindre enn kapasiteten til koppen.
**CoffeeCup**-klassen har følgende metoder og konstruktører:
- `void drinkCoffee(double)` og `void fillCoffee(double)`, hvor man henholdsvis drikker og fyller koppen med kaffe. Om man prøver å drikke mer enn det finnes i koppen, eller fyller for mye i koppen blir en `IllegalArgumentException` utløst.
- `void increaseCupSize(double)`, som øker størrelsen på koppen. Om man prøver å gjøre koppen mindre skjer ingenting.
- `CoffeeCup()`, en konstruktør som setter standard initialverdier til 0.0 kapasitet og 0.0 nåværende kaffe i koppen.
- `CoffeeCup(double, double)`, en konstruktør som setter initialverdier til de oppgitte verdiene.
- I tillegg har klassen noen private hjelpefunksjoner som man kan identifisere selv.
**CoffeeCupProgram**-klassen er en hovedprogramklasse som oppretter en instans av **CoffeeCup**, og utfører en sekvens av kall til koppens ulike metoder.
I denne oppgaven blir en implementasjon av **CoffeeCup** sammen med et hovedprogram utdelt, men i implementasjonen av hovedprogrammet har vi plantet noen feil. Det er to oppgaver som må løses.
CoffeeCup-koden finner du i [debugging/CoffeeCup.Java](../../src/main/java/debugging/CoffeeCup.java). CoffeeCupProgram finner du i [debugging/CoffeeCupProgram.java](../../src/main/java/debugging/CoffeeCupProgram.java).
### Leseliste
- [Debugging i Eclipse](https://www.ntnu.no/wiki/display/tdt4100/Bruk+av+debuggeren+i+Eclipse) - Debuggeren er et verktøy som brukes til å analysere kjørende kode, noe som kan være svært nyttig når man vil forstå og evt. rette feil i et program.
- [Hovedprogramklasser](https://www.ntnu.no/wiki/display/tdt4100/Hovedprogramklasser) - Hovedprogramklasser er klasser som aktiveres når programmer starter opp og som initialiserer og kontrollerer/koordinerer de andre objekter i applikasjonen.
- [java.util.Random](https://docs.oracle.com/javase/8/docs/api/java/util/Random.html) - En pseudotilfeldig nummergenerator i Java.
### Oppgave 1
Målet for oppgaven er å finne en feil i funksjonen `part1()` ved hjelp av debuggeren i Eclipse. Kjør hovedprogrammet i debug-modus, og bruk dette til å finne hva kapasiteten og nåværende volum av kaffe er like før programmet utløser et unntak.
Finn også ut hvilken metode i **CoffeeCup** som utløser unntaket.
### Oppgave 2
Du fant feilen i oppgave 1, bra! Kommentér ut kallet til `part1()` i hovedprogrammet, så slipper vi å ha mer med det å gjøre.
Du skal nå finne ut hvordan nåverende volum av kaffe endrer seg i `part2()`, før programmet utløser et unntak. Lag en liste over hvilke verdier nivået har. Hvilken metode i **CoffeeCup** utløser et unntak denne gangen? Hvilken type unntak blir utløst?

View File

@ -0,0 +1,25 @@
# Innkapsling - Nim-oppgave
Denne oppgaven handler om en `Nim`-klasse, som implementerer en variant av spillet [Nim](https://en.wikipedia.org/wiki/Nim).
Spillet Nim består av **tre** hauger med brikker. To spillere velger på tur én av haugene, og fjerner så mange brikker som ønskelig, men minimum én brikke. Spillet er over når en av haugene er helt tom for brikker.
`Nim`-klassen har enkel tilstand: En spillomgang består av tre hauger med et antall brikker. `Nim`-klassen må dermed kunne representere hvor mange brikker som er i hver haug, og oppdatere dette ettersom det fjernes brikker. Hvordan dette gjøres er åpent, så lenge det er tilstrekkelig for å oppfylle oppførselen definert for klassen.
`Nim`-klassen har følgende metoder:
- `void removePieces(int number, int targetPile)` - fjerner `number` (>= 1) antall brikker fra haugen `targetPile`. `IllegalArgumentException` utløses hvis targetPile eller number er ugyldig. Siden man ikke kan gjøre trekk etter at spillet er over, må `IllegalStateException` utløses dersom metoden kalles etter det.
- `boolean isValidMove(int number, int targetPile)` - returnerer `true` dersom argumentene representerer et lovlig trekk, og `false` dersom argumentene ikke ville ført til et lovlig trekk. Her skal man kun sjekke om et trekk er lovlig å utføre, ikke faktisk endre på tilstanden. **PS**: Husk at å flytte når et spill er over ikke er et lovlig trekk.
- `boolean isGameOver()` - returnerer `true` dersom en av haugene har 0 brikker.
- `int getPile(int targetPile)` - returnerer antall brikker i haugen targetPile.
- `String toString()` - returnerer en tekststreng som oppsummerer spillets tilstand
Haugene skal identifiseres ved tall, altså at gyldige verdier for `targetPile` er 0, 1 og 2.
`Nim`-klassen skal ha en [konstruktør](https://www.ntnu.no/wiki/display/tdt4100/Klasser+i+java#Klasserijava-constructor) `Nim(int pileSize)` som lar en bestemme hvor mange brikker som skal være i hver haug, og i tillegg en tom konstruktør som initialiserer `Nim`-objektet til å ha 10 brikker i hver haug.
## Oppgave: Java-kode
Skriv `Nim`-klassen, slik at den har ønsket oppførsel og er skikkelig innkapslet.
Testkode for denne oppgaven finner du i [encapsulation/NimTest.java](../../src/test/java/encapsulation/NimTest.java).

View File

@ -0,0 +1,43 @@
# Øving 3: Klasser og testing
__Øvingsmål__:
- Lære å lage enkle Java-klasser og -programmer
- Lære å bruke debuggeren i Eclipse
__Øvingskrav__:
- Kunne tegne enkle klassediagrammer
- Kunne deklarere klasser og metoder ihht. oppgavespesifikasjon
## Dette må du gjøre
__Del 1: Programmering__
Velg minst én av oppgavene under. Merk at *Card*-oppgaven bygges videre på i både øving 5 og 6, og kan derfor være lur å gjøre, slik at man har flere oppgaver å velge mellom senere.
- [Nim](./Nim.md) (Lett)
- [Card-oppgave](./Card.md) (Medium)
- [RPN-kalkulator](./RPN.md) (Vanskelig)
Oppgavene for denne øvingen skal du lagre i `ovinger/src/main/java/encapsulation`. Test-filene ligger i `ovinger/src/test/java/encapsulation`.
__Del 2: Klassediagram__
Tegn et [klassediagram](https://www.ntnu.no/wiki/display/tdt4100/Klassediagrammer) for oppgaven du velger i del 1. Klassediagrammet kan for eksempel skrives på papir eller tegnes i et valgfritt program. Diagrammet skal demonstrerer på sal, men det er ikke nødvendig å levere det på Blackboard.
__Del 3: Debugger__
I tillegg skal en kort oppgave som omhandler bruk av [debuggeren i Eclipse](https://www.ntnu.no/wiki/display/tdt4100/Bruk+av+debuggeren+i+Eclipse) løses. Oppgaven skal demonstreres for en læringsassistent på sal:
- [CoffeeCup](./CoffeeCup.md) (Lett/Medium)
Oppgavene er merket med en vanskelighetsgrad relativt til hverandre. Det er en god idè å begynne med de lettere oppgavene dersom du ikke er komfortabel med pensum så langt, men det er anbefalt å prøve seg på de vanskeligere oppgavene om du synes de første oppgavene er uproblematiske. Dersom du allerede føler deg trygg på punktene i øvingskravene kan du forsøke å gå rett på de vanskeligere oppgavene. Du er selvfølgelig velkommen til å løse flere oppgaver enn minstekravet, noe som er lurt med tanke på eksamen og et langt liv som programmerer.
### Hjelp / mistanke om bugs
Ved spørsmål eller behov for hjelp konsulter studassen din i saltiden hans / hennes. Du kan også oppsøke andre studasser på sal eller legge ut et innlegg på Piazza.
### Godkjenning
Last opp kildekode på Blackboard innen den angitte innleveringsfristen. Innlevert kode skal demonstreres for en læringsassistent innen én uke etter innleveringsfrist. Se for øvrig Blackboard-sidene for informasjon rundt organisering av øvingsopplegget og det tilhørende øvingsreglementet.

View File

@ -0,0 +1,70 @@
# Innkapsling - RPN-kalkulator-oppgave
Oppgaven handler om å lage en RPN-kalkulator (RPN = Reverse Polish Notation). I denne oppgaven kommer du til å ta i bruk en *stack*, og utføre matematiske operasjoner.
Her er en forklaring av hvordan en RPN-kalkulator fungerer:
> The RPN calculator differs from a normal calculator in that its syntax is completely different: It uses postfix notation instead of infix notation.
>
> Reverse polish notation, or postfix notation, is a notation where the operators (+, -, \*, /, ...) appear after the operands (1.0, 3.14, 2.187, 42, …). As an example, consider the expressions `2 1` and `9 + 3*4`. In RPN, this would be `2 1 -` and `9 3 4 * +`, as the operator shall appear after the operands. If the last example is confusing, think of it as `9 (3 4 *) +` instead.
>
> The main advantage of this is notation is that we can avoid parentheses and avoid determining which calculation to perform first. In infix, operator precedence is a problem: In the expression `9 + 3*4`, we first have to multiply, even though the plus operator appears first. In RPN, we simply take the first operator we see and apply it on the last two numbers we've seen, and replace the operator and operands with the result. For the multiplication and addition example:
>
> `9 3 4 * +` - the first operator we see is *, so we apply that to the two last seen values, 3 and 4, and replace those elements with the result, 12
>
> `9 12 +` - the next operator we see is +, so we apply that to 9 and 12
>
> `21` - We are finished, as there are no more operators in the expression
>
>For the more complex expression `(9 + 7) / (5 3)`, this will be written as `9 7 + 5 3 - /` in RPN. Notice how we can avoid parentheses and still calculate what we want without any issues:
>
> `9 7 + 5 3 - /` - the first operator is +, so we perform `9 + 7` and replace those elements with the result
>
> `16 5 3 - /` - the next operator is -, so we perform the operation `5 3`, as those are the last two elements
>
> `16 2 /` - we perform the last operation, division:
>
> `8` - We are done, as there are no more operators left.
>
> Most RPN calculators have a stack of numbers, giving them control over which numbers to do operations on when they see the next operand. Whenever they see an operand, they push it on the stack. If they see an operator, they pop off the numbers required, perform the operation, and push the result on top of the stack again.
>
> As an example of how this will work, consider the example `9 3 4 * +` once more. Here, an RPN calculator will first push 9, 3 and 4 on the stack. The stack will now look like this: [9, 3, 4], where 4 is the top of the stack. Then, when the calculator see the operator *, it then pops 4 off the stack, pops 3 off the stack, and pushes 3 * 4 on top of the stack. The stack which now contains [9, 12]. Then, when the RPN calculator sees the operator *, it pops off 12 and 9 off the stack, and performs the operation 9 + 12, and pushes it back on top of the (now empty) stack. The stack will now contain [21].
Tilstanden i `RPNCalc`-objekter velger du selv, men det er naturlig å bruke en `Stack` eller `ArrayList` med `Double`-objekter.
`RPNCalc`-klassen skal ha følgende metoder:
- `void push(double)` - legg argumentet på toppen av stacken.
- `double pop()` - returner verdien på toppen av stacken. Verdien skal også fjernes fra stacken. Dersom stacken er tom, så skal `Double.NaN` returneres.
- `double peek(int)` - returner verdien i stacken som står på plassen gitt i argumentet, telt fra toppen. Det vil si, `peek(0)` skal returnere verdien på toppen av stacken, `peek(1)` skal returnere verdien nest øverst i stacken osv. Verdien skal ikke fjernes av stacken. Dersom det er for få elementer på stacken, så skal `Double.NaN` returneres.
- `int getSize()` - returner antallet elementer i stacken.
- `void performOperation(char)` - utfør den angitte operasjonen på de to øverste verdiene i stacken. De to verdiene skal fjernes fra stacken og resultatet skal legges øverst. Bruk eksisterende metoder for å utføre dette der det er mulig. Metoden må støtte '+' (pluss), '-' (minus), '*' (multiplikasjon) og '/' (divisjon), men kan også støtte andre operatorer, f.eks. '~' (swap) for å bytte de to øverste operandene, 'p' eller 'π' (pi) for å legge pi på stacken (bruker ingen operander), '|' (absolutt-verdi, bruker én operand). Prøv å håndtere manglende operander på en måte som gir mening for operasjonen.
__Del 1 - Tilstandsdiagram__
Tegn [objekttilstandsdiagram](https://www.ntnu.no/wiki/display/tdt4100/Objekttilstandsdiagrammer) for en tenkt bruk av `RPNCalc`-klassen. Velg selv passende start-tilstand og sekvens av kall.
__Del 2 - Java-kode__
Skriv Java-kode for `RPNCalc`-klassen med oppførsel som er beskrevet over. Du skal bruke synlighetsmodifikatorer på metoder og felt for å gjøre innkapslingen "vanntett".
Lag en [main-metode](https://www.ntnu.no/wiki/display/tdt4100/Main-metoden), hvor du tester sekvenser av operander og operatorer, og kaller henholdsvis `push` og `performOperation`-metodene på et `RPNCalc`-objekt og skriver ut stacken. Test `RPNCalc`-klassen og sjekk at oppførselen stemmer med tilstandsdiagrammet.
__Del 3 - Teori__
Til nå har det blitt spesifisert at `peek()` og `pop()`-metodene skal returnere `Double.NaN` hvis stacken er tom. Alternativet er å utløse et unntak.
Svar på følgende:
- Hvilken type unntak vil det være naturlig å bruke?
- Hvilke fordeler og ulemper ser du for dette alternativet?
Det er også spesifisert at en skal "håndtere manglende operander på en måte som gir mening for operasjonen". Hvis `+`-operasjonen ble utført på kun én operand, så kan en f.eks. velge å la den manglende operanden være 0.
Svar på følgende:
- Hva vil tilsvarende verdi for manglende operand for `*`-operasjonen (multiplikasjon) være? Hva med for `/` (divisjon)?
- Hvordan kan du endre (evt. har du endret) grensesnittet for stack-operasjonene for å gjøre implementasjonen av disse enklere?
- Også her er et alternativ å utløse unntak. Hva tror du om det?
Testkode for denne oppgaven finner du i [encapsulation/RPNCalcTest.java](../../src/test/java/encapsulation/RPNCalcTest.java).

View File

@ -0,0 +1,24 @@
# Objektstrukturer - Card-oppgave del 2
Denne oppgaven handler om klasser for kortspill: `Card` (kort), `CardDeck` (kortstokk) og `CardHand` (korthånd), hvorav de to siste inneholder én eller flere `Card`-objekter. Oppgaven bygger på `Card` og `CardDeck` i [Innkapsling - Card-oppgave](../oving3/Card.md).
**Merk**: Om du ikke har gjort `Card`-oppgaven allerede, kan du bruke løsningsforslaget som er lagt ut under `lf/src/main/java/encapsulation`.
I mange sammenhenger vil objekter av en klasse inneholde eller "eie" objekter av andre klasser, og de underordnede objektene vil kunne flyttes/overføres mellom de overordnede. Når en klasse er assosiert med én instans av en (annen) klasse er dette en [1-1-assosiasjon](https://www.ntnu.no/wiki/display/tdt4100/Koding+av+1-1-assosiasjoner) og når en klasse er assosiert med flere instanser av en annen klasse er dette en [1-n-assosiasjon](https://www.ntnu.no/wiki/display/tdt4100/Koding+av+1-n-assosiasjoner). Et eksempel er kortspill, hvor kortene starter i kortstokken, fordeles på korthender og til slutt ender i en kortbunke. Et kort kan bare være ett sted om gangen, og må overføres fra ett sted til et annet, f.eks. fra kortstokk til korthender i utdelingsfasen. I [Innkapsling - Card-oppgave](../oving3/Card.md) ble det lagd logikk for kortstokk og enkeltkort. I denne oppgaven skal du implementere logikk for korthender, og utvide kortstokkens logikk litt.
`Card`-klassen har du allerede implementert, men du må sannsynligvis kopiere koden over fra `encapsulation` til `objectstructures`. Her er det enklest å lage en ny `Card`-klasse i `objectstructures` og så lime inn innholdet fra den gamle.
`CardDeck`-klassen har du også implementert, og denne må også flyttes på samme måte som Card. Denne klassen skal utvides:
- `deal(CardHand, int n)` - flytter n kort fra kortstokken (`CardDeck`-objektet) til korthånda (`CardHand`-objektet, som er første argument), ved å ta ett og ett kort med høyeste gyldige indeks, fjerne det fra `CardDeck`-objektet og legge det til `CardHand`-objektet.
`CardHand` er en ny klasse som skal implementeres. `CardHand`-objekter inneholder initielt ingen kort, og klassen inneholder de samme standardmetodene som `CardDeck`, altså `getCardCount()` og `getCard(int)`, for å lese hvor mange og hvilke kort den inneholder. I tillegg har den to metoder for å endre tilstand:
- `addCard(Card)` - legger argumentet til dette `CardHand`-objektet
- `play(int n)` - returnerer og fjerner kort nr. n (første kort har nr. 0) fra dette `CardHand`-objektet (som om det ble spilt ut)
**Java-kode**
Utvid `CardDeck` og lag `CardHand` som beskrevet over. Test klassene med selvlagde main-metoder og ved å kjøre JUnit-testene.
Testkode for denne oppgaven finner du her: [CardTest.java](../../src/test/java/objectstructures/CardTest.java), [CardDeckTest.java](../../src/test/java/objectstructures/CardDeckTest.java), [CardHandTest.java](../../src/test/java/objectstructures/CardHandTest.java).

View File

@ -0,0 +1,70 @@
# Objektstrukturer - Partner-oppgave
Denne oppgaven handler om en `Partner`-klasse med en [1-1-assosiasjon](https://www.ntnu.no/wiki/display/tdt4100/Koding+av+1-1-assosiasjoner)
kalt partner tilbake til samme klasse (altså kjønnsnøytralt partnerskap)
og det å sikre konsistens, slik at Partner-objekter er parvis knyttet sammen.
En viktig del av det å implementere assosiasjoner er å sikre konsistens, dvs. at objekter i hver ende av en kobling refererer korrekt til
hverandre. Et eksempel på dette for [1-1-assosiasjoner](https://www.ntnu.no/wiki/display/tdt4100/Koding+av+1-1-assosiasjoner) er
(kjønnsnøytralt) partnerskap, hvor to partnere er koblet til hverandre når partnerskap
inngås og kobles fra hverandre ved en evt. skillsmisse. I denne oppgaven skal en `Partner`-klasse implementeres og ulike situasjoner håndteres
korrekt, som illustrert nedenfor.
`Partner`-klassen skal inneholde informasjon om *navn* (en String), som bare skal kunne settes i konstruktøren, og *partneren*, som er et annet
Partner-objekt. Navnet er ikke viktig for oppførselen, men er grei å ha med i en `toString()`-metode, for å skille Partner-objektene fra
hverandre. `Partner`-klassen skal ha følgende metoder for å lese tilstanden:
* `getName()` - returnerer navnet knyttet til dette Partner-objektet
* `getPartner()` - returnerer Partner-objektet som er knyttet til dette Partner-objektet, evt. null, hvis partnerskap ikke er inngått
`Partner`-klassen har kun én endringsmetode, `setPartner(Partner)`, som brukes både for å inngå partnerskap, når argumentet er et Partner-objekt,
og oppløse det, når argumentet er null. List og figurene under illustrerer de tre tilfellene som må kunne håndteres, og som JUnit-testene sjekker.
### 1. Inngåelse av partnerskap:
**Kall**: p1.setPartner(p2)
**Beskrivlse**: Partner-objektene p1 og p2 kobles sammen med ett kall til setPartner. Før kallet er p1 og p2 ikke koblet sammen,
og etter kallet er det koblet sammen.
**Før kall**:
![](img/partner1.png)
**Etter kall**:
![](img/partner2.png)
### 2. Oppløsning av partnerskap:
**Kall**: p1.setPartner(null)
**Beskrivlse**: Partner-objektene p1 og p2 kobles fra hverandre med ett kall til setPartner med null som argument.
Før kallet er p1 og p2 koblet sammen, og etter kallet er det ikke lenger koblet sammen.
**Før kall**:
![](img/partner2.png)
**Etter kall**:
![](img/partner1.png)
### 3. Oppløsning og inngåelse av partnerskap i ett:
**Kall**: p1.setPartner(p3)
**Beskrivlse**: Partner-objektene p1, p2, p3 og p4 er parvis koblet sammen, før ett kall til setPartner kobler
sammen p1 og p3, mens p2 og p4 kobles fra deres tidligere partnere.
**Før kall**:
![](img/partner3.png)
**Etter kall**:
![](img/partner4.png)
## Gjøremål
Oppgaven er (enkelt og greit) å implementere `Partner`-klassen og sjekke (f.eks. med en `main`-metode) at Partner-objektene oppfører seg som de skal.
Testkode for denne oppgaven finner du her: [objectstructures/PartnerTest.java](../../src/test/java/objectstructures/PartnerTest.java).

View File

@ -0,0 +1,149 @@
# Objektstrukturer - Person-oppgave
Denne oppgaven handler om en `Person`-klasse med en [1-n-assosiasjon](https://www.ntnu.no/wiki/display/tdt4100/Koding+av+1-n-assosiasjoner)
med rollene *children* og *mother*/*father* til samme klasse (altså barn-mor/far-forhold) og det å sikre konsistens, slik at foreldre og barn
er korrekt knyttet sammen.
En viktig del av det å implementere assosiasjoner er å sikre konsistens, dvs. at objekter i hver ende av en kobling refererer korrekt
til hverandre. Et eksempel på dette for [1-n-assosiasjoner](https://www.ntnu.no/wiki/display/tdt4100/Koding+av+1-n-assosiasjoner) er
foreldreskap, hvor foreldre og barn er koblet til samme i et slektstre. I denne oppgaven skal en `Person`-klasse implementeres og det å
legge til (og fjerne) barn håndteres korrekt, som illustrert nedenfor.
`Person`-klassen skal inneholde informasjon om *navn* (en String) og *kjønn* (en char, 'F' eller 'M'), som bare skal kunne settes i
konstruktøren, og *mor*, *far* og *barn*, som er andre Person-objekter. Navnet er ikke viktig for oppførselen, men er grei å ha med
i en `toString()`-metode, for å skille Person-objektene fra hverandre. `Person`-klassen skal ha følgende metoder for å lese tilstanden:
* `getName()` - returnerer navnet knyttet til dette Person-objektet
* `getGender()` - returnerer tegnet som representerer kjønnet, enten 'F' eller 'M'
* `getMother()` - returnerer Person-objektet som er moren, evt. `null`
* `getFather()` - returnerer Person-objektet som er faren, evt. `null`
* `getChildCount()` - returnerer antall barn dette Person-objektet har
* `getChild(int n)` - returnerer barn nr. n (altså et Person-objekt), evt. utløser (et passende) unntak om `n` er for stor (eller liten)
`Person`-klassen har to sett med endringsmetoder, knyttet til de to rollene i hver ende av *children*-*mother*/*father*-assosiasjonen.
Fra *children*-perspektivet har vi følgende to metoder:
* `addChild(Person)` - oppretter en kobling til et barn (et annet Person-objekt). Dersom Person-objektet som metoden kalles på, er
en *kvinne*, så skal denne bli barnets *mor*, og motsatt, dersom Person-objektet som metoden kalles på, er en *mann*, så skal denne
bli barnets *far*.
* `removeChild(Person)` - fjerner en kobling til et barn (et annet Person-objekt). Dersom Person-objektet som metoden kalles på, er *moren*
til argumentet, så skal *mother*-koblingen fjernes, og motsatt, dersom Person-objektet som metoden kalles på, er argumentets *far*, så skal
*father*-koblingen fjernes.
Fra *mother*/*father*-perspektivet har vi følgende to metoder:
* `setMother(Person)` - setter argumentet (en kvinne) som *moren* til Person-objektet som metoden kalles på. Argumentet får samtidig
registrert Person-objektet som metoden kalles på, som sitt *barn*.
* `setFather(Person)` - setter argumentet (en mann) som *faren* til Person-objektet som metoden kalles på. Argumentet får samtidig
registrert Person-objektet som metoden kalles på, som sitt *barn*.
Det som er verd å merke seg er at begge sett med metoder, `addChild`/`removeChild` og `setMother`/`setFather`, må ha logikk
som håndterer koblingen den andre veien, så `addChild`/`removeChild` må kalle `setMother`/`setFather` og omvendt, eller ha kode
med tilsvarende effekt. Dette kan være nokså fiklete, fordi en både må sikre konsistens og unngå uendelig nøstede kall
(inntil du får StackOverflowException).
Listen og figurene under illustrerer de fem tilfellene som må kunne håndteres, og som testes av testene det er lenket til.
### 1. Opprettelse av koblinger med `addChild`
**Kall**:
marit.addChild(jens)
hallvard.addChild(jens)
(Dette har samme effekt som kallene under punkt 2.)
**Før kall**:
![](img/person1.png)
**Etter kall**:
![](img/person2.png)
### 2. Opprettelse av koblinger med `setMother` og `setFather`
**Kall**:
jens.setMother(marit)
jens.setFather(hallvard)
(Dette har samme effekt som kallene under punkt 1.)
**Før kall**:
![](img/person1.png)
**Etter kall**:
![](img/person2.png)
### 3. Fjerning av koblinger med `removeChild`
**Kall**:
marit.removeChild(jens)
hallvard.removeChild(jens)
(Dette har samme effekt som kallene under punkt 4.)
**Før kall**:
![](img/person2.png)
**Etter kall**:
![](img/person1.png)
### 4. Fjerning av koblinger med `setMother` og `setFather`
**Kall**:
jens.setMother(null)
jens.setFather(null)
(Dette har samme effekt som kallene under punkt 3.)
**Før kall**:
![](img/person2.png)
**Etter kall**:
![](img/person1.png)
### 5. Fjerning og oppretting av kobling med `setMother` og `setFather`, en slags "adoption"
**Kall**:
jens.setFather(torkel)
jens.setMother(jorunn)
**Før kall**:
![](img/person3.png)
**Etter kall**:
![](img/person4.png)
## Oppgaven
Oppgaven er delt i to trinn, den første håndterer *children*- og *mother*/*father*-rollen isolert og uten krav om konsistens,
mens det andre skal sikre konsistens.
### Del 1
* Implementer `addChild`- og `removeChild`-metodene slik at `getChildCount`- og `getChild`-metodene virker som forventet.
Disse metodene håndterer altså kun *children*-rollen.
* Implementer `setMother`- og `setFather`-metodene slik at `getMother`- og `getFather`-metodene virker som forventet.
Disse metodene håndteres altså kun *mother*/*father*-rollen.
Test metodene ved å koble opp Person-objekter tilsvarende din egen familie. Du blir nødt til å bruke de tre metodene `addChild`, `setMother`
og `setFather`. Prøv å få med minst tre generasjoner.
### Del 2
Utvid metodene til å sikre konsistens. Test at det fortsatt virker å koble opp din egen familie, denne gangen ved å bare bruke
`addChild` og ved å bare bruke `setMother` og `setFather`. Kjør JUnit-testene som hører til oppgaven.
Testkode for denne oppgaven finner du her: [objectstructures/PersonTest.java](../../src/test/java/objectstructures/PersonTest.java).

View File

@ -0,0 +1,62 @@
# Øving 4: Objektstrukturer
**Øvingsmål**
- Lære hva assosiasjoner er og hvordan dette brukes i OO
- Lære hvordan man sikrer konsistent oppførsel mellom assosierte objekter
**Øvingskrav**
- Kunne implementere klasser som har assosiasjoner til én eller flere andre klasser
- Kunne sikre at disse assosiasjon er konsistente i enkle objektstrukturer
- Kunne implementere metoder som oppretter, oppdaterer og fjerner slike assosiasjoner
## Dette må du gjøre
### Del 1: Programmering
I denne øvingen skal du velge og gjennomføre ENTEN både Partner- og Card del 2-oppgavene ELLER minst én av Twitter-, Stopwatch- og Person-oppgavene.
Merk at **noen av oppgavene i neste øving (øving 6), bygger videre på noen av oppgavene under**, disse er uthevet med **fet skrift**.
Det er ikke et krav at man gjør de uthevede oppgavene, men de gir flere oppgaver å velge mellom i øving 6.
**Gjør enten _begge_ disse:**
- [Partner](./Partner.md) (lett)
- **[Card del 2](./Card.md)** (lett)
**Eller _minst én_ av følgende oppgaver:**
- **[Twitter](./Twitter.md)** (medium, men lang)
- [Stopwatch](./Stopwatch.md) (medium)
- [Person](./Person.md) (medium/vanskelig)
Oppgavene for denne øvingen skal du lagre i `ovinger/src/main/java/objectstructures`. Test-filene ligger i `ovinger/src/test/java/objectstructures`.
Alle oppgavene ovenfor er høyst eksamensrelevante og det anbefales følgelig å ta en titt på alle sammen.
### Del 2: Klassediagram
- Lag et [klassediagram](https://www.ntnu.no/wiki/display/tdt4100/Klassediagrammer) for oppgaven(e) du velger.
Diagrammet kan for eksempel skrives på papir eller tegnes/lages i et valgfritt program. Du trenger ikke levere inn diagrammene, men de skal vises til studass under godkjenning av øvingen.
### Del 3: Testing
Skriv kode som tester oppførselen til `CoffeeCup`-klassen, dvs. at du skal teste om metodene i listen under har rett oppførsel og returnerer det de skal, i tillegg til at du skal teste konstruktørene. Det er ikke nødvendig å teste absolutt alle mulige tilfeller, men det kreves at du tester den grunnleggende funksjonaliteten.
* `getCapacity`
* `getCurrentVolume`
* `increaseCupSize`
* `drinkCoffee`
* `fillCoffee`
Du finner `CoffeeCup`-klassen under `ovinger/src/main/java/objectstructures`.
Her er det anbefalt å bruke [JUnit](https://www.ntnu.no/wiki/display/tdt4100/Enhetstesting+med+JUnit),
men det er lov å teste vha. main-metoden også.
### Hjelp / mistanke om bugs
Ved spørsmål eller behov for hjelp konsulter studassen din i saltiden hans / hennes. Du kan også oppsøke andre studasser på sal eller legge ut et innlegg på [Piazza](https://piazza.com/).
### Godkjenning
Last opp kildekode på Blackboard innen den angitte innleveringsfristen. Innlevert kode skal demonstreres for en læringsassistent innen én uke etter innleveringsfrist. Se for øvrig Blackboard-sidene for informasjon rundt organisering av øvingsopplegget og det tilhørende øvingsreglementet.

View File

@ -0,0 +1,31 @@
# Objektstrukturer - StopWatchManager-oppgave
Denne oppgaven handler om en `StopWatchManager`-klasse som inneholder flere `StopWatch`-objekter. Oppgaven bygger på klassen lagd i [StopWatch-oppgaven](../oving1/Stopwatch.md) fra "tilstand og oppførsel".
I mange sammenhenger vil objekter av en klasse inneholde eller "eie" objekter av andre klasser. Når en klasse er assosiert med én instans av en (annen) klasse er dette en [1-1-assosiasjon](https://www.ntnu.no/wiki/display/tdt4100/Koding+av+1-1-assosiasjoner) og når en klasse er assosiert med flere instanser av en annen klasse er dette en [1-n-assosiasjon](https://www.ntnu.no/wiki/display/tdt4100/Koding+av+1-n-assosiasjoner).
I denne oppgaven skal du implementere en `StopWatchManager`-klasse som kan holde styr på flere stoppeklokker. Ved hjelp av `StopWatchManager` skal man enkelt kunne holde styr på flere stoppeklokker og sørge for at alle stoppeklokker får beskjed om tiden som går. Dette kan være nyttig hvis man f.eks. ønsker å holde styr på flere løpere i et skirenn der ikke alle starter og fullfører samtidig, men hvor allikevel klokken må gå for alle.
Det skal være mulig å opprette nye stoppeklokker med et tilhørende navn (streng). Navnet skal man senere kunne bruke til å hente stoppeklokken igjen eller fjerne stoppeklokken fra `StopWatchManager`. For å få til dette kan det være lurt å se litt på `Map` fra [Collection-rammeverket](https://www.ntnu.no/wiki/display/tdt4100/Collection-rammeverket).
`StopWatchManager` skal ha følgende endringsmetoder:
- `StopWatch newStopWatch(String name)` - Oppretter en ny stoppeklokke knyttet til navnet `name`. Returnerer den nye stoppeklokken.
- `void removeStopWatch(String name)` - Fjerner stoppeklokken tilknyttet navnet `name`.
- `void tick(int ticks)` - Informerer alle stoppeklokkene om at ticks tikk har gått.
`StopWatchManager` skal ha følgende lesemetoder:
- `StopWatch getStopWatch(String name)` - returnerer stoppeklokken tilknyttet navnet `name`.
- `Collection<StopWatch> getAllWatches()` - returnerer alle stoppeklokkene.
- `Collection<StopWatch> getStartedWatches()` - returnerer alle stoppeklokkene som er startet.
- `Collection<StopWatch> getStoppedWatches()` - returnerer alle stoppeklokkene som er stoppet.
**Merk**: Det er viktig at de metodene som returnerer en samling av stoppeklokker returnerer nye samlinger. De som får en samling må kunne endre på den (f.eks. fjerne elementer) uten at dette forstyrrer `StopWatchManager` eller andre som har fått samlinger tidligere.
**Java-kode**
Kopier `StopWatch` fra `encapsulation`-pakken og lag `StopWatchManager` som beskrevet over. Test klassen med selvlagde main-metoder og ved å kjøre JUnit-testene.
Testkode for denne oppgaven finner du her: [objectstructures/StopWatchTest.java](../../src/test/java/objectstructures/StopWatchTest.java) og [objectstructures/StopWatchManagerTest.java](../../src/test/java/objectstructures/StopWatchManagerTest.java).

View File

@ -0,0 +1,73 @@
# Objektstrukturer - Twitter-oppgave
Denne oppgaven handler om en begrenset klone av `Twitter`, med to klasser, `TwitterAccount` og `Tweet`.
En Twitter-konto kan følge andre Twitter-kontoer og motsatt: en Twitter-konto kan bli fulgt av andre Twitter-kontoer.
Dette er altså en gjensidig kobling: Hvis konto A følger konto B, så er konto B fulgt av konto A. En kan selvsagt ikke følge seg selv.
I tillegg har hver Twitter-konto en mengde *tweets*, som er små, korte tekster. En tweet hører til den kontoen den ble sendt fra.
Hvis noen finner en annen sin tweet interessant har man muligheten til å retweete denne. Da lager man en tweet som refererer til originalen,
og (implisitt) få original-tweeten sin tekst. Merk at i en kjede av retweets, så vil alle referere til samme original-tweet. Mao.,
hvis tweet B er en retweet av A og tweet C er en retweet av B, vil både tweet B og C ha A som original-tweet, slik det er vist under.
**Riktig objektstrutur**, når B er en retweet av A og C er en retweet av B:
![](img/twitter1.png)
**Feil objektstrutur**, når B er en retweet av A og C er en retweet av B:
![](img/twitter2.png)
## Tweet-klassen
`Tweet` skal ha to konstruktører, en for hver måte å tweete på:
* `Tweet(TwitterAccount, String)` - En ny original-tweet
* `Tweet(TwitterAccount, Tweet)` - En retweet av Tweet-argumentet. Utløser et passende unntak, hvis original-tweeten er fra samme konto.
`Tweet` skal ha metodene:
* `String getText()` - returnerer teksten til en tweet
* `TwitterAccount getOwner()` - returnerer kontoen som tweeten kom fra
* `Tweet getOriginalTweet()` - returnerer original-tweeten, hvis den er en retweet, ellers null
* `int getRetweetCount()` - returnerer antall ganger denne tweeten har blitt retweetet
## TwitterAccount-klassen
`TwitterAccount` skal ha konstruktøren:
* `TwitterAccount(String)` - som tar inn brukernavnet
`TwitterAccount` skal ha metodene:
* `String getUserName()` - returnerer brukernavnet
* `void follow(TwitterAccount account)` - denne (this) kontoen starter å følge account
* `void unfollow(TwitterAccount account)` - slutt å følge account
* `boolean isFollowing(TwitterAccount account)` - returnerer om denne kontoen følger account
* `boolean isFollowedBy(TwitterAccount account)` - returnerer om account følger denne kontoen
* `void tweet(String)` - lager en ny tweet for denne kontoen
* `void retweet(Tweet tweet)` - retweeter tweet fra denne kontoen
* `Tweet getTweet(int i)` - returner tweet nummer i, der 1 er den nyeste, 2 den nest nyeste, … (merk rekkefølgen!)
* `int getTweetCount()` - returner antall tweets til kontoen
* `int getRetweetCount()` - returner antall retweets av tweets fra denne kontoen
## Del 1
* Implementer `Tweet`-klassen.
* For å teste klassen må du sende inn TwitterAccount-objekter i konstruktøren. Lag en forenklet versjon av `TwitterAccount`-klassen
for dette formålet, der du kun implementerer konstruktøren og evt. en passende toString(). Dette gjør det mulig å teste `Tweet`-klassen
din uten at du må implementere hele `TwitterAccount`-klassen først.
## Del 2
* Implementer `TwitterAccount`-klassen.
* Test klassen og dens samspill med `Tweet`-klassen ved å lage Twitter-konto for deg selv og noen av vennene dine.
La noen av kontoene følge hverandre, tweete og retweete.
Testkode for denne oppgaven finner du her: [objectstructures/TweetTest.java](../../src/test/java/objectstructures/TweetTest.java) og [TwitterAccountTest.java](../../src/test/java/objectstructures/TwitterAccountTest.java).
## Frivillig utvidelse
På Twitter kan man markere en annen sin tweet som en favoritt. Implementer passende metoder for å kunne gjøre dette.
En konto må ha oversikt over hvilke tweets den har markert som favoritter, og en tweet må vite hvem og hvor mange som har markert den
som favoritt. Hva synes du burde skje hvis man markerer en retweet som en favoritt?

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 KiB

View File

@ -0,0 +1,52 @@
# Interface - BinaryComputingIterator
Denne oppgaven handler om en meta-iterator som kombinerer par av verdier fra to iteratorer til en ny iterator-sekvens ved hjelp av en gitt binær operasjon.
Meta-iteratorer er iteratorer som bruker andre iteratorer som datakilder. Se lysark om teknikken: [Forelesningslysark om meta-iteratorer](https://docs.google.com/file/d/0B9IltfWcNirndERTb1RnTGM3YWs/preview)
Du skal lage en ny klasse `BinaryComputingIterator`, som er en *implementasjon* av det innebygde `Iterator<Double>`-grensesnittet. Konstruktøren til `BinaryComputingIterator` skal ta inn to iteratorer og en binær operator, samt to valgfrie standardverdier. Når `next()`-metoden til `BinaryComputingIterator` blir kalt, skal en verdi hentes fra hver av de to iteratorene, den binære operasjonen utføres på disse to verdiene og resultatet returneres. Den binære operasjonen skal spesifiseres ved hjelp av det innebygde `BinaryOperator<Double>`-grensesnittet, som har en `apply`-metode som lar en beregne resultatet av den binære operasjonen. Hvordan en kan opprette funksjonelle grensesnitt kan du lese mer om på [wikisiden om dette](https://www.ntnu.no/wiki/display/tdt4100/Lambda-uttrykk+og+funksjonelle+grensesnitt+i+Java+8).
## BinaryComputingIterator
Klassen skal ha to konstruktører, en med og en uten standardverdier.
- `BinaryComputingIterator(Iterator<Double> iterator1, Iterator<Double> iterator2, BinaryOperator<Double> operator)`
- `BinaryComputingIterator(Iterator<Double> iterator1, Iterator<Double> iterator2, Double default1, Double default2, BinaryOperator<Double> operator)`
hvor `iterator1` og `iterator2` er iteratorene som blir kombinert av `BinaryComputingIterator`, og `default1` og `default2` er standardverdier for de respektive iteratorene. Klassen må ha følgende metoder, begge spesifisert av `Iterator<Double>`-grensesnittet: Hvordan en lager en `BinaryOperator` klasse kan du se mer om lenger ned i oppgaveteksten.
- `boolean hasNext()` - returnerer true dersom det kan beregnes flere verdier, altså hvis begge iteratorene enten har flere verdier eller har en tilhørende standardverdi. Men merk at hasNext() returnerer uansett false, hvis begge iteratorene er tomme.
- `Double next()` - returnerer resultatet av å bruke binæroperasjonen operator på de neste verdiene fra sub-iteratorene, og bruker standardverdier dersom en av iteratorene ikke har flere verdier.
Husk at ved hjelp av Java 8-syntaks kan en implementasjon av BinaryOperator skrives som `(num1, num2) -> <uttrykk>`, der `<uttrykk>` er et Java-uttrykk som brukes `num1` og `num2`. Hvis BinaryComputerIterator henter verdier fra to iteratorer med hhv. verdiene 1, 2, 3 og 3, 4, 5 og den binære operatoren er (num1, num2) -> num1 + num2, så skal sekvensen en får ut være 4, 6, 8.
For å håndtere tilfellet hvor den ene iteratoren gir ut flere verdier enn den andre, så skal det være mulig å gi standardverdier hhv. `default1` og `default2` for `iterator1` og `iterator2`, som vil bli brukt for å fylle inn manglende verdier. Hvis `BinaryComputerIterator` henter verdier fra to iteratorer med hhv. verdiene 6, 3, 0 og 3, 4 og den binære operatoren er `(num1, num2) -> num1 - num2` og `default2` er 2, så skal sekvensen en får ut være 3, -1, -2.
### Eksempel
`BinaryOperator`-implementasjoner kan lett skrive ved hjelp av Java 8 sin funksjonsnotasjon. Dersom man for eksempel vil bruke en addisjonsoperator kan det se slik ut:
```java
Iterator<Double> iterator1 = Arrays.asList(2.0, 3.0).iterator();
Iterator<Double> iterator2 = Arrays.asList(5.0).iterator();
BinaryOperator<Double> addition = (a, b) -> a + b;
// Opprett en ny BinaryComputingIterator som tar inn iterator1 og iterator2 og utfører addisjon på verdiene.
BinaryComputingIterator binaryIterator = new BinaryComputingIterator(iterator1, iterator2, null, 10.0, addition);
binaryIterator.next(); // 7.0
binaryIterator.hasNext() // true
binaryIterator.next() // 13.0
binaryIterator.hasNext() // false
```
Testkode for BinaryComputingIterator er her: [interfaces/BinaryComputingIteratorTest.java](../../src/test/java/interfaces/BinaryComputingIteratorTest.java).
**Hjelp / mistanke om bugs**
Ved spørsmål eller behov for hjelp konsulter studassen din i saltiden hans / hennes. Du kan også oppsøke andre studasser på sal eller legge ut et innlegg på Piazza.
**Godkjenning**
Last opp **kildekode** på Blackboard innen den angitte innleveringsfristen. Innlevert kode skal demonstreres for stud.ass innen én uke etter innleveringsfrist. Se for øvrig Blackboard-sidene for informasjon rundt organisering av øvingsopplegget og det tilhørende øvingsreglementet.

View File

@ -0,0 +1,39 @@
# Interface - CardComparison-oppgave
Denne oppgaven hander om to måter å håndtere sortering av `Card`-objekter, med grensesnittene `Comparable` og `Comparator`, som er innebygd i Java
(java.util.Comparable, java.util.Comparator).
Vi tar i denne oppgaven utgangspunkt i en `Card`-klassen fra [Innkapsling - Card-oppgave](../oving3/Card.md).
Et `Card`-objekt har en kortfarge (av typen *char*) og verdi (*int*), og sortering gjøres på disse verdiene, ved at en først sorterer på
kortfarge og så på verdi. Siden Java har sortering innebygget, så trenger vi ikke bry oss om selve sorteringsalgoritmen,
men fokuserer her på logikken for *sammenligning* av `Card`-objekter.
#### Del 1
La `Card`-klassen implementere `Comparable` med følgende sammenligningslogikk
* `compareTo`-metoden skal sammenligne et kort med et annet, slik at spar vurderes som høyere enn hjerter, hjerter høyere enn ruter
og ruter høyere enn kløver. Ved lik kortfarge skal verdien brukes, altså 1 (ess) er mindre enn 2 er mindre enn 3 osv. til og med 11 (knekt),
12 (dame) og 13 (konge).
Skriv testkode som sorterer kort i en liste vha. `Collections.sort` og `Comparable-logikken`, så dere ser at det virker som forventet.
#### Del 2
For å kunne sortere `Card`-objekter med annen logikk, så kan en bruke grensesnittet `Comparator`, som er et objekt som kan sammenligne
objekter parvise. Implementer en `Comparator` (dvs. lag en klasse som *implements* `Comparator`) kalt `CardComparator`, som kan
konfigureres (stilles inn) til å sortere med ess som høyeste kort og med en bestemt kortfarge som trumf, altså en kortfarge som regnes
som høyere enn de andre.
* `CardComparator` må ha en konstruktør som tar inn en *boolean* og en *char*. *boolean*-argumentet sier om ess skal regnes som størst (`true`)
eller minst (`false`) og *char*-argumentet angir hvilke kortfarge som er trumf. F.eks. skal et `CardComparator`-objekt laget med
`new CardComparator(true, ' ')` rangere ess høyere enn konge og bruke standard rangering av kortfarger
(siden trumf-argumentet ikke er en av kortfargene), og et `CardComparator`-objekt laget med `new CardComparator(false, 'C')`
rangerer ess lavest og kløver ('C' = clubs) høyest av kortfargene (de andre kortfargene har standard rangering med spar over
hjerter over ruter).
Skriv testkode som sorterer kort i en liste vha. `Collections.sort` og `Comparator-logikken`, så dere ser at det virker som forventet.
Testkode for del 1 og del 2 finner du her: [interfaces/CardComparatorTest.java](../../src/test/java/interfaces/CardComparatorTest.java).
#### Ekstraoppgave
Utvid `CardComparator` slik at den kan konfigureres med en annen rangering av kortfargene, f.eks. slik at kløver er høyere enn ruter.
Merk at denne fortsatt skal overstyres av evt. trumf. Nytten er altså at en kan endre rangeringsrekkefølgen på alle på én gang.

View File

@ -0,0 +1,33 @@
# Interface - CardContainer-oppgave
Denne oppgaven handler om å lage et felles grensesnitt for `CardDeck`- og `CardHand`-klassene, laget i oppgavene
[Innkapsling - Card-oppgave](../oving3/Card.md) og
[Objektstrukturer - Card-oppgave del 2](../oving5/Card.md).
Her skal du lage og implementere et grensenitt kalt `CardContainer`, som spesifiserer metoder for lesing av samlinger av Card-objekter.
#### Del 1 - CardContainer interface
Definer et `CardContainer`-grensesnitt, med metodene som er felles for `CardHand` og `CardDeck`:
* `getCardCount()` - returnerer antall kort som dette objektet inneholder
* `getCard(int n)` - returnerer kort nr. `n` i dette objektet
Gjør nødvendig endringer i `CardHand`- og `CardDeck`-klassene for å implementere `CardContainer`-grensesnittet.
#### Del 2 - Iterator for CardContainer
Lag en klasse kalt `CardContainerIterator`, som er en [Iterator](https://www.ntnu.no/wiki/display/tdt4100/Iterasjon+med+Iterator+og+Iterable)
for alle klasser som implementerer `CardContainer`-grensesnittet. `CardContainerIterator` må ha en konstruktør som tar inn en instans av
(en klasse som implementerer) `CardContainer`.
*Hint*: Merk at `CardContainerIterator` ikke vet om `CardContainer`-objektet er et `CardDeck`-objekt, et `CardHand`-objekt eller et annet
objekt som implementerer `CardContainer`. Den har derfor ikke tilgang til de interne listene i `CardHand` og `CardDeck`.
Hvilke metoder må alle klasser som implementerer `CardContainer` ha, og hvordan kan disse metodene brukes for å lage en
[Iterator](https://www.ntnu.no/wiki/display/tdt4100/Iterasjon+med+Iterator+og+Iterable)?
Testkode for oppgaven finner du her: [interfaces/CardDeckTest.java](../../src/test/java/interfaces/CardDeckTest.java), [interfaces/CardHandTest.java](../../src/test/java/interfaces/CardHandTest.java) og [interfaces/CardContainerIteratorTest.java](../../src/test/java/interfaces/CardContainerIteratorTest.java).
#### Ekstraoppgave
La `CardContainer`-grensesnittet utvide (`extends`) `Iterable<Card>` og la `iterator()`-metoden som dermed kreves, returnere en korrekt
konfigurert instans av `CardContainerIterator`.

View File

@ -0,0 +1,19 @@
# Interface - CardPredicate-oppgave
Denne oppgaven hander om hvordan en kan bruke det funksjonelle `Predicate<T>`-grensesnittet, sammen med `CardDeck`-klassen.
Vi tar i denne oppgaven utgangspunkt i `CardDeck`-klassen fra [Innkapsling - Card-oppgave](../oving3/Card.md).
Et `CardDeck`-objekt har en liste med `Card`-objekter. `Card` har en kortfarge (av typen *char*) og verdi (*int*), og vi ønsker å lage metoder
i `CardDeck` som søker opp `Card`-objekter som tilfredsstiller visse kriterier, f.eks. sjekker om spar dame finnes, teller antall hjerter eller
henter ut alle ess. For å representere selve kriteriet brukes `Predicate<T>`-grensesnittet, som handler om å teste/sjekke om et objekt
tilfredsstiller visse kriterium.
**Implementer** følgende metoder i `CardDeck`-klassen:
* `boolean hasCard(Predicate<Card> predicate)` - Skal returnere `true` dersom det finnes et kort som tilfredsstiller `predicate`, `false` ellers.
* `int getCardCount(Predicate<Card> predicate)` - Skal returnere hvor mange kort som tilfredsstiller `predicate`.
* `List<Card> getCards(Predicate<Card> predicate)` - Skal returnere en liste med de kortene som tilfredsstiller `predicate`.
Lag også din egen main-metode hvor du prøver hver av de tre metodene over. Du skal altså sjekke om spar dame finnes, telle antall hjerter og
hente ut alle ess.
Hvordan en kan opprette funksjonelle grensesnitt kan du se på [wikisiden](https://www.ntnu.no/wiki/display/tdt4100/Lambda-uttrykk+og+funksjonelle+grensesnitt+i+Java+8) om dette.

View File

@ -0,0 +1,35 @@
# Interface - Named-oppgave
Denne oppgaven handler om et grensnitt (interface) for person-navn og hvordan slike implementeres og sorteres med grensesnittet `Comparator`.
I denne oppgaven tar vi utgangspunkt i at en person har et navn (`String`) bestående av fornavn ("given name") og etternavn ("family name") som sammen gir personens fulle navn ("full name").
## Del 1
Definer et grensesnitt `Named` med følgende metoder:
- `setGivenName(String)` og `String getGivenName()` for å sette og hente fornavn
- `setFamilyName(String)` og `String getFamilyName()` for å sette og hente etternavn
- `setFullName(String)` og `String getFullName()` for å sette og hente personens hele navn. Argumentet til set-metoden skal være fornavn og etternavn skilt med mellomrom. Tilsvarende skal get-metoden returnere fornavn og etternavn skilt med mellomrom.
## Del 2
Lag klassene `Person1` og `Person2` som begge implementerer grensesnittet `Named`. `Person1`-klassen skal ha felter for for- og etternavn (altså `givenName` og `familyName`) og en konstruktør som tar inn to tilsvarende argumenter. `Person2` skal ha ett felt for fullt navn (`fullName`) og en konstruktør som tar inn det fulle navnet. Begge skal imidlertid implementere samme logikk, dvs. ha get- og set-metoder for fornavn, etternavn og fullt navn. Man kan anta at brukeren oppretter `Person1` og `Person2`-objekter med gyldige navn, altså trenger man ikke å implementere valideringsmetoder.
## Del 3
For å kunne sammenligne `Named`-objekter, f. eks. for å sortere en kontaktliste, kan du lage en klasse kalt `NamedComparator`, som implementerer grensesnittet [Comparator](https://www.ntnu.no/wiki/display/tdt4100/Sortering+med+Comparable+og+Comparator). `NamedComparator`-objektet skal brukes for å sammenligne navn parvis: Først på etternavn og deretter på fornavn om etternavnene er like. Dette kan gjøres ved å la `NamedComparator`-klassen implementere metoden `int compare(Named named1, Named named2)` med følgende logikk:
- Dersom etternavnene er ulike skal metoden
- returnere et negativt heltall om det første etternavnet er alfabetisk ordnet før det andre,
- eller et positivt heltall i motsatt tilfelle.
- Dersom etternavnene er like skal metoden gjøre det samme på fornavnene. Dersom også fornavnene er like skal metoden returnere 0.
Skriv testkode som bruker `Collections.sort`-metoden på en `ArrayList<Named>`, for å teste om `NamedComparator`-klassen har implementert [Comparator](https://www.ntnu.no/wiki/display/tdt4100/Sortering+med+Comparable+og+Comparator) riktig.
Hint: For å sammenlikne to `String`-objekter kan man bruke `String`-klassens innebygde `Comparable`-metode `int compareTo(String streng2)`. Denne metoden sammenlikner en `String` med en annen alfabetisk.
Testkode for oppgavene finner du her:
- [interfaces/NamedComparatorTest.java](../../src/test/java/interfaces/NamedComparatorTest.java),
- [interfaces/Person1Test](../../src/test/java/interfaces/Person1Test.java),
- [interfaces/Person2Test](../../src/test/java/interfaces/Person2Test.java).

View File

@ -0,0 +1,47 @@
# Øving 5: Grensesnitt
**Øvingsmål**
* Lære hva grensesnitt er og hvordan disse defineres
* Lære hvordan man implementerer et grensesnitt
* Kjenne til grunnleggende funksjonelle grensesnitt
**Øvingskrav**
* Kunne lage grensesnitt og implementere disse med andre klasser
* Kunne bruke grensesnittene Comparable<T> og Comparator<T>
* Kunne bruke grensesnittene Iterable<T> og Iterator<T>
## Dette må du gjøre
### Del 1: Programmering
Gjør **minst to** av oppgavene under. Oppgavene (unntatt Twitter) skal lagres i `ovinger/src/main/java/interfaces`.
Filer til Twitter-oppgaven skal lagres i `ovinger/src/main/java/interfaces.twitter`.
* [CardContainer](./CardContainer.md)
* [CardComparison](./CardComparison.md)
* [CardPredicate](./CardPredicate.md)
* [Twitter](./Twitter.md)
* [Named](./Named.md)
* [BinaryComputingIterator](./BinaryComputingIterator.md)
* [StringGrid](./StringGrid.md)
* [RPNKalkulator med funksjonelle grensesnitt](./RPNCalc.md)
Disse oppgavene har noe ulik vanskelighetsgrad og omfang, og trolig stigende (i.e. CardContainer den enkleste og StringGrid den vanskeligste).
Alle er høyst eksamensrelevante og det anbefales følgelig å ta en titt på alle sammen.
### Del 2: Debugging
Gjør følgende oppgave om debugging og vis frem løsningen til studass på sal:
* [StringMergingIterator](./StringMergingIterator.md)
### Del 3: Sekvensdiagram
Lag et [sekvensdiagram](https://www.ntnu.no/wiki/display/tdt4100/Sekvensdiagrammer) som viser samhandlingen mellom et
`StringMergingIterator`-objekt og dens argumenter. Dvs. lag et [sekvensdiagram](https://www.ntnu.no/wiki/display/tdt4100/Sekvensdiagrammer)
som viser hvordan `StringMergingIterator` gjennom metodekall fletter verdiene fra de to gitte iteratorene (som blir tatt inn som argumentene til
`StringMergingIterator`-objektet).
### Hjelp / mistanke om bugs
Ved spørsmål eller behov for hjelp konsulter studassen din i saltiden hans / hennes. Du kan også oppsøke andre studasser på sal eller legge ut et innlegg på [Piazza](https://piazza.com/).
### Godkjenning
Last opp kildekode på Blackboard innen den angitte innleveringsfristen. Innlevert kode skal demonstreres for en læringsassistent innen én uke etter innleveringsfrist. Se for øvrig Blackboard-sidene for informasjon rundt organisering av øvingsopplegget og det tilhørende øvingsreglementet.

View File

@ -0,0 +1,31 @@
# Interface - RPNKalkulator med funksjonelle grensesnitt
Denne oppgaven handler om å gjøre det enklere å utvide en kalkulator ved å bruke det innebygde funksjonelle grensesnittet `BinaryOperator<T>`, sammen med **RPNCalc**-klassen.
Vi tar i denne oppgaven utgangspunkt i **RPNCalc**-klassen fra [Innkapsling - RPN-kalkulator](../oving3/RPN.md)-oppgave. Et **RPNCalc**-objekt består av en stack med tall, metoder for å håndtere stacken, og `performOperation(char)` metoden, som utfører en operasjon gitt av parameteret. For eksempel vil kallet `performOperation('*')` fjerne de to øverste tallene fra stacken, multiplisere dem, og pushe resultatet på toppen av stacken. Om operasjonene hardkodes i metoden, vil det være vanskelig å endre hva kalkulatoren kan gjøre under kjøring. Denne oppgaven går derfor ut på å gjøre det mulig å legge til og fjerne operasjoner fra kalkulatoren. Operasjoner på en og to flyttall kan representeres ved bruk av henholdsvis `UnaryOperator<Double>`- og `BinaryOperator<Double>`-grensesnittene, og operasjoner uten operander (f.eks. pi) kan representeres ved bruk av `Supplier<Double>`.
Implementer følgende metoder i **RPNCalc**-klassen:
- `boolean addOperator(char, BinaryOperator<Double>)` - Legg til en operator som virker på to tall (f.eks +) hvis operatoren ikke allerede er lagt til. Returner true hvis den nye operatoren blir lagt til
- `void removeOperator(char)` - Fjern operatoren med tegn operatorSign
Du må også oppdatere `performOperation(char)` til å bruke operatorene som legges til via metodene over. Om man prøver å kalle `performOperation` med en operator som ikke er lagt til skal det utløses et `UnsupportedOperationException`-unntak.
## Ekstraoppgave
Utvid programmet til å kunne ta inn operatorer som tar inn et parameter (`UnaryOperator<Double>`, f.eks. `|` (absoluttverdi)) og ingen parametre (`Supplier<Double>`, f.eks. `p` (pi)). Husk at du må håndtere forsøk på å legge til samme operator i flere kategorier (f.eks. om man prøver å legge til `+` som både **UnaryOperator** og **BinaryOperator** må det håndteres på en god måte).
Dette vil innebære å legge til metodene
- `boolean addOperator(char, UnaryOperator<Double>)`
- `boolean addOperator(char, Supplier<Double>)`
som fungerer på samme måte som `addOperator(char, BinaryOperator<Double>)`, samt å oppdatere resten av koden til å fungere med de forskjellige operatortypene.
**Hjelp / mistanke om bugs**
Ved spørsmål eller behov for hjelp konsulter studassen din i saltiden hans / hennes. Du kan også oppsøke andre studasser på sal eller legge ut et innlegg på Piazza.
**Godkjenning**
Last opp **kildekode** på Blackboard innen den angitte innleveringsfristen. Innlevert kode skal demonstreres for stud.ass innen én uke etter innleveringsfrist. Se for øvrig Blackboard-sidene for informasjon rundt organisering av øvingsopplegget og det tilhørende øvingsreglementet.

View File

@ -0,0 +1,64 @@
# Interface - StringGrid-oppgave
Denne oppgaven handler om et grensnitt (interface) for rutenett som holder strenger (StringGrid), hvordan slike implementeres og hvordan en kan iterere gjennom et slikt rutenett ved hjelp av en [Iterator](https://www.ntnu.no/wiki/display/tdt4100/Iterasjon+med+Iterator+og+Iterable).
I denne oppgaven tar vi utgangspunkt i et **StringGrid**-grensesnitt som definerer metodene til et rutenett som holder strenger. Et rutenett er firkantet og består av et antall rader og kolonner. Det skal være mulig å spørre rutenettet hvilken streng som er på angitt rad og kolonne i tillegg til å endre strengen på angitt rad og kolonne. Denne oppførselen er oppsummert i det definerte **StringGrid**-grensesnittet under:
```java
package interfaces;
/*
An interface with methods for managing the content of a String grid.
The grid has a number of rows (the grid's height) and columns (the grid's width).
In each cell in the grid there is a String that can be set with the setElement method and read with the getElement method.
*/
public interface StringGrid {
// Returns the number of rows in this StringGrid
public int getRowCount();
// Returns the number of columns in this StringGrid
public int getColumnCount();
// Returns the String at the given row and column. Throws an IllegalArgumentException if the row or column is out of range
public String getElement(int row, int column);
// Sets the String at the given row and column. Throws an IllegalArgumentException if the row or column is out of range
public void setElement(int row, int column, String element);
}
```
Alle klasser som implementerer **StringGrid**-grensesnittet må støtte de fire definerte metodene.
## Del 1 - StringGrid-grensesnitt og implementerende StringGridImpl-klasse
Lag en **StringGridImpl**-klasse som implementerer **StringGrid**-grensesnittet definert over. Merk at grensesnitt ikke kan brukes til å spesifisere konstruktører, så du må selv definere en eller flere egnede konstruktører. Det er imidlertid nødvendig å implementere en konstruktør som tilsvarer den JUnit-testen forventer:
- `StringGridImpl(int rows, int columnCount)` - konstruktør som tar inn antall rader som `rows` og antall kolonner som `columnCount`.
Du står fritt til å velge hvordan metodene definert av grensesnittet skal implementeres så lenge de tilfredsstiller den definerte oppførselen. Hint: Bruk en enkel `ArrayList<String>` eller en dobbel `ArrayList<ArrayList<String>>` (se wiki-siden om [todimensjonale matriser](https://www.ntnu.no/wiki/display/tdt4100/Todimensjonale+matriser)).
## Del 2 - StringGridIterator-klasse
Det er hensiktmessig å kunne iterere over alle elementene i et rutenett som implementerer grensesnittet **StringGrid**, f.eks. når en bygger en streng i en **toString()**-metode eller skal sjekke om et spill har blitt avsluttet / vunnet. I denne deloppgaven skal du lage en slik [Iterator](https://www.ntnu.no/wiki/display/tdt4100/Iterasjon+med+Iterator+og+Iterable)-implementasjon, kalt **StringGridIterator**. Denne klassen må implementere grensesnittet `Iterator<String>`, siden **StringGrid** inneholder **String**-objekter. I tillegg til metodene som er definert i **Iterator**-grensesnittet, må **StringGridIterator** ha en konstruktør som tar imot hvilken **StringGrid** det skal itereres over og i hvilken rekkefølge elementene skal returneres i. Disse verdiene må huskes, så koden i **Iterator**-metodene kan brukes dem til å styre iterasjonen. **StringGridIterator**-klassen må altså støtte følgende konstruktør / metoder:
- `StringGridIterator(StringGrid, boolean)` - konstruktør som tar inn **StringGrid**-objektet som **StringGridIterator**-klassen skal iterere over i tillegg til en logisk verdi som angir om iterasjonen skal være bortover først (`rowMajor=true`) eller nedover først (`rowMajor=false`).
- `boolean hasNext()` - returnerer true så lenge det er flere **String**-objekter igjen i **StringGrid**-objektet som ikke ennå er blitt iterert over (mao. sjekk om du har kommet til siste rute i rutenettet).
- `String next()` - returnerer det neste **String**-objektet i rutenettet. Hvilken **String** som er den neste, avhenger av hvordan rutenettet skal itereres (mao. om `rowMajor=true` eller `rowMajor=false`).
- `void remove()` - denne metoden skal bare utløse et unntak av typen `UnsupportedOperationException` siden det ikke skal være mulig å fjerne `String`-objekter fra rutenettet.
## Del 3 - Iterable-grensesnittet
Endre **StringGrid**-grensesnittet slik at det utvider (med extends) [**Iterable<String>**](https://www.ntnu.no/wiki/display/tdt4100/Iterasjon+med+Iterator+og+Iterable). Dette skal gjøre det mulig å skrive for-setningen under, for å gå gjennom alle elementene i rutenettet.
```java
StringGrid stringGrid = ... // her initialiseres stringGrid
// gå gjennom alle elementene i stringGrid
for (String s: stringGrid) {
// gjør noe med s her
}
```
Rekkefølgen som en slik for-setningen går gjennom elementene på, skal være hele første rad, så hele andre rad osv. til og med siste rad.
Hva slags følger får det for **StringGridImpl**? Gjør nødvendige endringer i den også, og test at det virker!
Testkode for del 1 og del 2 finner du her: [interfaces/StringGridTest.java](../../src/test/java/interfaces/StringGridTest.java).

View File

@ -0,0 +1,20 @@
# Debugging - StringMergingIterator-oppgave
Oppgaven handler om feilsøking ("debugging") av en Iterator-implementasjon ved bruk av [**debuggeren**](https://www.ntnu.no/wiki/display/tdt4100/Bruk+av+debuggeren+i+Eclipse) i Eclipse.
Les først denne artikkelen om bruk av debugger i Eclipse om du ikke er kjent med dette: [Bruk av debuggeren i Eclipse](https://www.ntnu.no/wiki/display/tdt4100/Bruk+av+debuggeren+i+Eclipse)
Klassen **StringMergingIterator** implementerer grensesnittet [**Iterator<String>**](https://www.ntnu.no/wiki/display/tdt4100/Iterasjon+med+Iterator+og+Iterable), og leverer annenhver verdi fra to andre iteratorer av typen **Iterator<String>**. Denne iteratoren fletter altså verdiene fra to andre gitte iteratorer, og er altså en meta-iterator. Meta-iteratorer er iteratorer som bruker andre iteratorer som datakilder. Se lysark om teknikken: [Forelesningslysark om meta-iteratorer](https://docs.google.com/file/d/0B9IltfWcNirndERTb1RnTGM3YWs/preview)
**StringMergingIterator** har følgende konstruktør:
- `StringMergingIterator(Iterator<String> first, Iterator<String> second)`
Siden klassen implementerer **Iterator<String>** har den også følgende metoder:
- `boolean hasNext()` - returnerer `true` dersom iteratoren har flere verdier, `false` dersom det ikke er flere verdier.
- `String next()` - returnerer den neste verdien fra iteratoren, eller utløser et `NoSuchElementException` dersom iteratoren er tom.
I denne oppgaven blir en implementasjon av **StringMergingIterator** sammen med et testprogram utdelt, men i implementasjonen av klassen har vi plantet en eller flere feil. Målet for oppgaven er å finne feilene i implementasjonen ved hjelp av [debuggeren](https://www.ntnu.no/wiki/display/tdt4100/Bruk+av+debuggeren+i+Eclipse) i Eclipse. Kjør programklassen **StringMergingIteratorProgram** i debug-modus, og bruk dette til å finne ut hvor **StringMergeIterator** gjør feil. Dersom programklassen lykkes med å få en flettet strøm med verdier har du funnet alle feilene.
Merk at du *ikke* skal gjøre noen endringer **StringMergingIteratorProgram**, men bruke dette programmet til å teste logikken i **StringMergingIterator**.

View File

@ -0,0 +1,53 @@
# Interface - Sortering av TwitterAccount-objekter ved bruk av Comparator
Denne oppgaven handler om sortering av `TwitterAccount`-objekter, ved bruk av grensesnittet `Comparator`. Oppgaven illustrerer
hvordan man kan sortere objekter av samme klasse på ulike måter, ved hjelp av ulike implementasjoner av `Comparator`.
Vi tar i denne oppgaven utgangspunkt i `TwitterAccount`- og `Tweet`-klassen fra [Objektstrukturer - Twitter-oppgave](../oving5/Twitter.md).
Et `TwitterAccount`-objekt har et brukernavn, en liste over andre brukere som følges, en liste over brukere som følger denne brukeren
(dette `TwitterAccount`-objektet), og en liste med tweets. Vi ønsker å kunne sortere `TwitterAccount`-objekter på tre ulike parametre:
1. Brukernavn
1. Antall følgere
1. Antall tweets
#### Del 1
I denne delen av oppgaven skal du lage tre ulike implementasjoner av `Comparator`-grensesnittet. `Comparator`-grensesnittet inneholder
én metode `compare(Object o1, Object o2)`. Implementasjonen av denne metoden skal returnere:
* et negativt tall dersom objektet o1 skal komme før objektet o2 i en sortert rekkefølge
* et positivt tall dersom objektet o1 skal komme etter objektet o2 i en sortert rekkefølge
* 0 om det er likegyldig hvilken rekkefølge objektene har (dvs. de er like hverandre for den parameteren/de paremetrene de sorteres på)
De tre klassene du skal lage er som følger:
* `UserNameComparator`: sammenligner `TwitterAccount`-objektene på brukernavn, slik at brukeren “Apekatten” vil komme før
“Bjørnen” som igjen vil komme før “Cameleonen” (dvs. leksikalsk rekkefølge - tenk rekkefølgene brukernavnene ville stått i et
leksikon eller en ordbok)
* `FollowersCountComparator`: sammenligner `TwitterAccount`-objektene på antall følgere, slik at brukeren med flest følgere havner først.
* `TweetsCountComparator`: sammenligner `TwitterAccount`-objektene på antall tweets, slik at brukeren med flest tweets havner først.
Alle klassene skal implementere `Comparator<TwitterAccount>`.
#### Del 2
I denne delen av oppgaven skal du legge til en funksjon i `TwitterAccount`-klassen som lar deg hente ut en sortert versjon av følgerene
til dette (this) `TwitterAccount`-objektet. Funksjonen du skal implementere er som følger:
* `getFollowers(Comparator<TwitterAccount>)` - skal returnere en sortert kopi av følgere-listen til dette `TwitterAccount`-objektet.
Objektene skal sorteres ved å bruke det `Comparator`-objektet som tas inn som parameter. Dersom parameteren er `null` skal du returnere
den orginale (usorterte) versjonen av følgere-listen. Du skal ikke skrive din egen sorteringsalgoritme, men bruke
`Collections.sort`-funksjonen fra `java.utils`-biblioteket. Merk at den opprinnelige følgere-listen skal være uforandret etter at
denne funksjonen har blitt kjørt.
Testkode for oppgaven finner du her:
- [interfaces/twitter/TwitterAccountTest.java](../../src/test/java/interfaces/twitter/TwitterAccountTest.java)
- [interfaces/twitter/FollowersCountComparatorTest.java](../../src/test/java/interfaces/twitter/FollowersCountComparatorTest.java)
- [interfaces/twitter/TweetsCountComparatorTest.java](../../src/test/java/interfaces/twitter/TweetsCountComparatorTest.java)
- [interfaces/twitter/UserNameComparatorTest.java](../../src/test/java/interfaces/twitter/UserNameComparatorTest.java)
#### Ekstraoppgave
Lag en klasse `TwitterAccountComparator` som implementerer `Comparator<TwitterAccount>` og sammenligner `TwitterAccount`-objekter på
følgende måte:
* `TwitterAccount`-objektet med flest følgere skal komme først.
* Dersom to `TwitterAccount`-objekter har like mange følgere skal det `TwitterAccount`-objektet med flest tweets komme først.
* Dersom to `TwitterAccount`-objekter har like mange følgere og tweets skal `TwitterAccount`-objektene sammenlignes på brukernavn.

View File

@ -0,0 +1,40 @@
# Observatør-observert-teknikken - HighscoreList-oppgave
Denne oppgaven handler om å bruke observatør-observert-teknikken for å bli informert om endringer i en highscore-liste.
Observatør-observert-teknikken går ut på at det observerte objektet sier ifra til en eller flere observatører om at tilstanden er endret. I denne oppgaven skal vi lage en **HighscoreList** som kan si fra til lyttere av typen **HighscoreListListener** når nye resultater blir registrert. En hovedprogramklasse kalt **HighscoreProgram** vil bli brukt til å sjekke at det virker. Denne klassen oppretter en **HighscoreList**-instans, legger inn resultater (tall) fra konsollen som legges til lista og skriver ut lista hver gang et nytt resultat faktisk blir lagt til.
## Del 1: Implementasjon av HighscoreList
En **HighscoreList** skal holde styr på heltallsresultater (av typen int/Integer). Lista skal være observerbar ved at den kan registrere lyttere (**HighscoreListListener**-instanser) og si fra til dem når lista blir endret. Lista skal ha en maksimal lengde, som settes i konstruktøren, f.eks. skal en topp 10-liste kunne opprettes med `new HighscoreList(10)`. Nye resultater registreres med metoden `addResult(int)`, som skal finne riktig posisjon og legge resultatet inn (dersom det er godt nok). Dersom lista er for lang, så skal det dårligste resultatet fjernes. NB: *Lavest verdi* er best, f.eks. antall sekunder på en oppgave eller antall flytt i Sokoban.
**HighscoreListListener**-grensesnittet er vist i klassediagrammet til venstre og må implementeres av alle klasser som ønsker å fungere som lyttere for **HighscoreList**-instanser. Lyttere registrerer seg med **HighscoreList** sin **addHighscoreListListener**-metode og vil siden få beskjed om nye resultater ved at **listChanged**-metoden kalles. Argumentene som tas inn er **HighscoreList**-objektet som ble endret og posisjonen i lista der endringen skjedde.
Her er en oversikt over metoden som må implementeres:
- `HighscoreList(int maxSize)` - konstruktøren tar inn maks antall resultater som lista skal kunne holde. Denne verdien må brukes av **addResult**, slik at resultater som er for dårlige kastes.
- `size()` - returnerer antall elementer i lista, som altså aldri skal overstige maks-antallet
- `int getElement(int)` - returnerer resultatet i posisjonen angitt av argumentet
- `void addResult(int)` - registrere et nytt resultat, og dersom resultatet er godt nok til å komme med på lista, så legges det inn på riktig plass. Dersom lista blir for lang, så må dårligste resultat kastes. Alle registrerte lyttere må få beskjed om en evt. endring av lista, inkludert på hvilken posisjon som ble endret.
- `addHighscoreListListener(HighscoreListListener)` - registrerer en ny lytter
- `removeHighscoreListListener(HighscoreListListener)` - fjerner en tidligere registrert lytter
Klassediagram for **HighscoreList** og **HighscoreListListener**:
![](images/highscore-list.png)
Testkode for denne oppgaven finner du her: [patterns/observable/HighscoreListTest.java](../../src/test/java/patterns/observable/HighscoreListTest.java).
## Del 2: Hovedprogramklasse
Lag en hovedprogramklasse kalt **HighscoreListProgram**, som tester at **HighscoreList**-klassen din virker som den skal. La den opprette en **HighscoreList**-instans, lese inn tall fra konsollet (f.eks. med en **Scanner** og **nextInt**-metoden) og legge disse inn i lista. Sørg for at **HighscoreListProgram** implementerer **HighscoreListListener**-grensesnittet og registrerer seg som lytter på **HighscoreList**-instansen. La lyttermetoden **listChanged** skrive ut informasjon og resultatene i **HighscoreList**-instansen og posisjonsargumentet, slik at du ser at alt virker som det skal.
Vi foreslår følgende metoder og oppførsel:
- `void init()` - oppretter en ny **HighscoreList** og registrerer seg selv (altså **HighscoreListProgram**-instansen) som lytter
- `void run()` - leser inn tall (resultater) fra konsollet og legger dem til i listen
- `void listChanged(HighscoreList, int)` - observerer endringer i **HighscoreList**-instansen og skriver ut posisjonsargumentet, samt selve listen, til konsollen.
Klassediagrammet viser hvordan klassene henger sammen, og vårt forslag til metoder:
![](images/hl-program.png)

View File

@ -0,0 +1,126 @@
# Delegering - Logger-oppgave
Denne oppgaven bruker delegeringsteknikken for å implementere en fleksibel måte å håndtere logging (av feil i programmer) på.
## Logging
Ved kjøring av programmer er det ofte behov for å logge hva som skjer underveis, slik at det blir lettere å drive feilsøking i etterkant. F.eks. kan en lagre feilmeldinger til fil, med tidspunkt og litt om programtilstanden og hvis programmet kræsjer ordentlig, så kan brukeren sende logg-fila som e-post til utviklerne. En enkel måte å støtte logging på er å lage en hjelpeklasse med én metode, f.eks. `log(String melding)`, og så er det hjelpeklassen som bestemmer om meldingen skal vises i statuslinja, skrives til fil, sendes som melding til en alarmsentral osv. Hjelpeklassen kan kanskje brukes av mange programmer, og siden behovene vil variere er det viktig å gjøre dette fleksibelt. Denne oppgaven bruker [grensesnitt](https://www.ntnu.no/wiki/pages/viewpage.action?pageId=65936813) og [delegeringsteknikken](https://www.ntnu.no/wiki/display/tdt4100/Delegeringsteknikken) for å implementere fleksibel logging, litt på samme måte som eksisterende loggingsrammeverk (se f.eks. [Java sin egen loggingsfunksjonalitet](http://docs.oracle.com/javase/6/docs/technotes/guides/logging/overview.html), Apache sitt [log4j-rammeverk](http://logging.apache.org/log4j/), Eclipse sin innebygde [loggingsmekanisme](http://www.vogella.com/tutorials/EclipseLogging/article.html#logging) eller Google ["Java logging framework"](https://www.google.no/search?q=java+logging+frameworks)).
### ILogger-grensesnittet
Logging gjøres ved å bruke ulike implementasjoner av **ILogger**, som er definert som følger:
```java
package delegation;
public interface ILogger {
public String ERROR = "error", WARNING = "warning", INFO = "info";
public void log(String severity, String message, Exception exception);
}
```
ILogger-grensesnittet definerer én log-metode som brukes til all logging:
- `severity`-argumentet angir alvorlighetsgraden, og må være en av **String**-verdiene **ERROR**, **WARNING** eller **INFO**, som er definert som konstanter i grensesnittet.
- `message`-argumentet er en melding om hva som var feil.
- `exception`-argumentet er et unntaksobjekt, som kan gi mer informasjon av hva som var feil, men kan være **null**.
En typisk bruk vil være i **catch**-delen av en **try/catch**:
```java
ILogger logger = ...
...
try {
...
} catch (IOException ioe) {
logger.log(ILogger.ERROR, "Feil ved lesing fra fil", ioe);
}
```
Akkurat hvordan logging utføres bestemmes av hvilken implementasjon av ILogger-grensesnittet en bruker, og i denne oppgaven skal du implementere følgende tre klasser:
- **DistributingLogger** - delegerer til andre loggere basert på *alvorlighetsgraden*
- **FilteringLogger** - delegerer til en annen logger, men kun for spesifikke alvorlighetsgrader
- **StreamLogger** - skriver logg-meldingen til en angitt strøm
Hver av disse utgjør én av deloppgavene beskrevet under.
## Del 1 - StreamLogger
En **StreamLogger** sørger for å skrive alle logg-meldinger til en angitt **OutputStream**, med én melding pr. linje (altså linjeskift mellom hver melding). **OutputStream**-objektet må gis inn i konstruktøren:
- `StreamLogger(OutputStream stream)` - initialiserer **StreamLogger**-objektet slik at logg-meldinger skrives til **stream**.
Eksempel på bruk:
```java
ILogger logger = new StreamLogger(System.out);
logger.log(ILogger.INFO, "Denne meldingen er til informasjon og skrives til System.out", null);
```
Husk å kalle **flush**-metoden til OutputStream etter at logg-meldingen er skrevet.
Det skal også være mulig å angi en såkalt *format*-string, dvs. en **String** som fungerer som en slags mal for hva som skrives, f.eks. `"%s: %s (%s)"`:
- `setFormatString(String formatString)` - setter format-string-en som brukes for å lage logg-meldingen som skrives
Effekten av skriving skal være som om man ga format-string-en som første argument til **String.format**-metoden etterfulgt av severity-, message- og exception-argumentene, og så skrev ut det denne metoden returnerer:
```java
String logMessage = String.format(formatString, severity, message, exception);
// skriv logMessage til OutputStream-en her
```
Merk at dersom format-string-en ikke er satt, så skal den ha en fornuftig start-verdi.
Testkode for oppgaven: [patterns/delegation/StreamLoggerTest.java](../../src/test/java/patterns/delegation/StreamLoggerTest.java).
## Del 2 - FilteringLogger
**FilteringLogger**-klassen implementerer **ILogger**-grensesnittet og delegerer til en annen **ILogger**-implementasjon, men bare hvis *alvorlighetsgraden* er en av et sett angitte verdier. Både loggeren det delegeres til og alvorlighetsgradene angis når **FilteringLogger**-objektet opprettes:
- `FilteringLogger(ILogger logger, String... severities)` - initialiserer **FilteringLogger**-objektet så det delegerer logging til **logger**-argumentet, men bare hvis *alvorlighetsgraden* som gis til **log**-metoden er en av verdiene angitt i **severities**-argumentet. **severities**-argumentet er et såkalt varargs-argument, som du kan lese mer om her: [Varargs - variabelt antall argumenter](https://www.ntnu.no/wiki/display/tdt4100/Varargs+-+variabelt+antall+argumenter).
Det skal også være mulig å sjekke om logging er på og slå logging av og på i etterkant:
- `boolean isLogging(String severity)` - returnerer **true** hvis logging er slått på for den angitte alvorlighetsgraden og **false** ellers.
- `void setIsLogging(String severity, boolean value)` - slår logging på (**value = true**) eller av (**value = false**) for den angitte *alvorlighetsgraden*
Eksempel på bruk:
```java
ILogger syserrLogger = new StreamLogger(System.err);
FilteringLogger logger = new FilteringLogger(syserrLogger, ILogger.ERROR);
logger.log(ILogger.ERROR, "Denne meldingen er alvorlig og skrives til System.err", null);
logger.log(ILogger.WARNING, "Denne meldingen er en advarsel og blir filtrert bort", null);
logger.log(ILogger.INFO, "Denne meldingen er til informasjon og blir filtrert bort", null);
logger.setIsLogging(ILogger.WARNING, true);
logger.log(ILogger.WARNING, "Denne meldingen er en advarsel og blir nå skrevet til System.err", null);
```
Testkode for oppgaven: [patterns/delegation/FilteringLoggerTest.java](../../src/test/java/patterns/delegation/FilteringLoggerTest.java).
## Del 3 - DistributingLogger
**DistributingLogger**-klassen brukes for å fordele logg-meldinger til en av tre andre loggere, avhengig av *alvorlighetsgraden* til en logg-melding. Den har én hjelpe-logger for meldinger med alvorlighetsgrad **ERROR**, én for meldinger av alvorlighetsgrad **WARNING** og en for meldinger av alvorlighetsgrad **INFO**. Alle disse angis til konstruktøren:
- `DistributingLogger(ILogger errorLogger, ILogger warningLogger, ILogger infoLogger)` - initialiserer objektet slik at den første loggeren brukes til alvorlighetsgraden **ERROR**, den andre til alvorlighetsgraden **WARNING** og den tredje til alvorlighetsgraden **INFO**.
I tillegg skal klassen ha en metode for å sette hver av dem individuelt:
- `void setLogger(String severity, ILogger logger)` - setter/endrer loggeren som brukes for den angitte alvorlighetsgraden.
Eksempel på bruk:
```java
ILogger syserrLogger = new StreamLogger(System.err);
ILogger sysoutLogger = new StreamLogger(System.out);
DistributingLogger logger = new DistributingLogger(syserrLogger, syserrLogger, sysoutLogger);
logger.log(ILogger.ERROR, "Denne meldingen er alvorlig og skrives til System.err", null);
logger.log(ILogger.WARNING, "Denne meldingen er en advarsel og skrives til System.err", null);
logger.log(ILogger.INFO, "Denne meldingen er til informasjon og skrives til System.out", null);
logger.setLogger(ILogger.WARNING, sysoutLogger);
logger.log(ILogger.WARNING, "Denne meldingen er en advarsel, men nå skrives den til System.out", null);
```
Testkode for oppgaven: [patterns/delegation/DistributingLoggerTest.java](../../src/test/java/patterns/delegation/DistributingLoggerTest.java).

View File

@ -0,0 +1,52 @@
# Delegering - The Office-oppgave
Denne oppgaven bruker delegeringsteknikken for å modellere arbeidsfordeling på en “vanlig” arbeidsplass. Denne oppgaven kan muligens oppleves som mindre meningsfull. Dette er kanskje omtrent tilsvarende hvor meningsløst noen typer kontorarbeid kan virke.
Vi skal i dette scenarioet ha en sjef, eller **Manager**, som har én eller flere arbeidere, eller **Clerk**s, altså i en såkalt én-til-mange relasjon. Et **Employee**-grensesnitt definerer en oppførsel som er felles for de ansatte, og implementeres av både **Manager** og **Clerk**.
**Employee**-objekter på denne simulerte arbeidsplassen har to oppgaver:
- utskrift av dokumenter
- utførelse av matematiske beregninger
## Del 1: Employee, Clerk og Printer
**Employee**-grensesnittet har følgende metoder:
- `double doCalculations(BinaryOperator<Double> operation, double value1, double value2)` - regner ut resultatet av å utføre **operation** med argumentene **value1** og **value2**.
- `void printDocument(String document)` - Printer **document**. Hvordan dette gjøres avhenger av den spesifikke implementasjonen.
- `int getTaskCount()` - returnerer hvor mange oppgaver (beregninger og printinger) som har blitt utført av eller på vegne av dette **Employee**-objektet.
- `int getResourceCount()` - antallet employees til rådighet, inkludert **Employee**-objektet metoden blir kalt på. En **Employee** skal altså medregne seg selv i antall ressurser den ansatte har til rådighet. Dette tallet skal inkludere alle **Employee**-objekter nedover i hierarkiet.
Lag dette grensesnittet, og lag så en **Clerk**-klasse som implementerer det. **Clerk** må ha følgende konstruktør:
- `Clerk(Printer printer)`
**Clerk**-klassen må inneholde egen logikk for å løse **doCalculations**, men skal altså delegere **printDocuments** til **Printer**-objektet gitt i konstruktøren.
Definer en **Printer**-klasse med følgende metoder:
- `void printDocument(String document, Employee employee)` - skriver documentet til konsollen og tar vare på dokumentet i **employee** sin historikk.
- `List<String> getPrintHistory(Employee employee)` - returnerer en **List<String>** med alle dokumentene som har blitt printet av **employee** av denne printeren i rekkefølgen de har blitt printet. Om **employee** ikke har printet noen dokumenter ved denne printeren skal en tom liste returneres.
La så **Clerk** delegere **printDocument** til **Printer**. Siden **Clerk** ikke har noen andre ansatte å delegere til, vil **getResourceCount()** alltid være 1.
Testkode for Clerk er her: [delegation/office/ClerkTest.java](../../src/test/java/patterns/delegation/office/ClerkTest.java)
Testkode for Printer er her: [delegation/office/PrinterTest.java](../../src/test/java/patterns/delegation/office/PrinterTest.java)
## Del 2: Manager
Vi definerer så sjefen til de hardt-arbeidende **Clerk**-objektene. **Manager**-klassen har følgende konstruktør:
- `Manager (Collection<Employee> employees)` - utløser et **IllegalArgumentException** dersom employees er tom.
La så **Manager** implementere **Employee**-grensesnittet. Implementer **Manager**s oppgaver ved å delegere alle videre til en av arbeiderne i listen med **Employee**-objekter gitt i konstruktøren. Regelen for hvilken **Employee** som får hvilken oppgave delegert til seg kan du bestemme selv, men prøv å gjøre det slik at arbeidet fordeles jevnt på alle. Mens **Clerk** altså har kun én tilgjengelig ressurs vil **Manager**-objekter vil ha flere.
Testkode for Manager er her: [delegation/office/ManagerTest.java](../../src/test/java/patterns/delegation/office/ManagerTest.java)
## Del 3: Main-metode
Lag en main-metode som illustrerer hva som skjer med effektiviteten når vi legger til flere nivåer med mellomledere.
Lag først et **Manager**-objekt som blir tildelt noen **Clerk**-objekter under seg. Presentér deretter effektiviteten av hierarkiet ved å skrive ut `getTaskCount() / getResourceCount()` for **Manager**-objektet. Vis deretter hvordan effektiviteten faller når vi legger til nivåer med mellomledere ved å lage to eller flere nivåer med **Manager**, hvor lederne på bunnen tildeles **Clerk**-objekter, og skriv ut den nye effektiviteten for topplederne.

View File

@ -0,0 +1,34 @@
# Øving 06: Observatør-Observert og Delegering
**Øvingsmål**
* Lære hva observatør-observert-teknikken er, dens bruksområder og fordeler
* Lære bruk av delegering for å utføre oppgaver i en klasse
**Øvingskrav**
* Kunne definere og implementere et observatørgrensesnitt
* Kunne la en observert klasse fortelle dens observatører om endringer
* Kunne la en klasse delegere utførelsen av oppgaver til interne objekter
## Dette må du gjøre
Denne øvingen omfatter både [delegeringsteknikken](https://www.ntnu.no/wiki/display/tdt4100/Delegeringsteknikken) og
[observatør-observert-teknikken](https://www.ntnu.no/wiki/pages/viewpage.action?pageId=66879660). Minstekravet for å få øvingen
godkjent er å gjennomføre **mint én** av de fire oppgavene under. Det anbefales derimot på det *sterkeste* å gjøre minst én oppgave
fra hvert av de to temaene, siden dette må til for å dekke hele pensum.
Gjennomfør enten *minst én* av oppgavene om delegering:
* [The Office (ovinger/src/patterns.delegation.office)](./Office.md) (anbefalt)
* [Logger (ovinger/src/patterns.delegation)](./Logger.md)
ELLER *minst én* av oppgavene om observatør-observert-teknikken:
* [StockListener (ovinger/src/patterns.observable)](./StockListener.md)
* [Highscore (ovinger/src/patterns.observable)](./HighscoreList.md)
Oppgavene skal lagres i mappene som er spesifisert i parentes etter oppgavene.
**I tillegg til oppgaven(e) ovenfor skal du levere en tekstfil hvor du gjør kort rede for delegeringsteknikken og observatør-observert-teknikken.**
### Hjelp / mistanke om bugs
Ved spørsmål eller behov for hjelp konsulter studassen din i saltiden hans / hennes. Du kan også oppsøke andre studasser på sal eller legge ut et innlegg på [Piazza](https://piazza.com/).
### Godkjenning
Last opp kildekode på Blackboard innen den angitte innleveringsfristen. Innlevert kode skal demonstreres for en læringsassistent innen én uke etter innleveringsfrist. Se for øvrig Blackboard-sidene for informasjon rundt organisering av øvingsopplegget og det tilhørende øvingsreglementet.

View File

@ -0,0 +1,62 @@
# Observatør-observert-teknikken - StockListener-oppgave
Denne oppgaven handler om å bruke observatør-observert-teknikken for å holde en aksjeindeks (**StockIndex**) informert om endringer i én eller flere aksjer (**Stock**).
Observatør-observert-teknikken går ut på at det observerte objektet sier ifra til én eller flere observatører om at tilstanden er endret. I vårt tilfelle skal vi ta utgangspunkt i at aksjer (**Stock**) har en pris, og at personer eller institusjoner (**StockListener**) ønsker å holde seg oppdatert på aksjepriser.
## Del 1: Stock-klassen og StockListener-grensesnittet
Du skal implementere en klasse **Stock** med følgende funksjonalitet:
- `Stock(String, double)` - en konstruktør som tar inn en aksjekode (ticker) og en aksjepris.
- `void setPrice(double)` - endringsmetode for aksjeprisen. Dersom aksjepris er negativ eller lik null, skal metoden utløse en **IllegalArgumentException**.
- `String getTicker()` - metode for å hente aksjekoden.
- `double getPrice()` - metode for å hente aksjeprisen.
Du skal videre definere et lyttergrensesnitt kalt **StockListener**, som observatørene må implementere. Grensesnittet skal inneholde én metode:
- `void stockPriceChanged(Stock stock, double oldValue, double newValue)` - lyttermetode for å holde lytteren oppdatert på aksjeprisen. Metoden skal ta inn et **Stock**-objekt, samt gammel og ny pris. Alle lyttere må implementere denne metoden.
Foreløpig er **Stock** ikke observerbar. For at observatører skal kunne holdes oppdatert, må **Stock**-objekter administrere en liste med lyttere. Derfor må **Stock**-klassen i tillegg ha følgende metoder:
- `void addStockListener(StockListener)` - metode for å legge til nye observatører.
- `void removeStockListener(StockListener)` - metode for å fjerne observatører.
Observatørene skal holdes oppdatert på prisendringer. Derfor må lyttermetoden kalles hos alle registrerte observatører når aksjeprisen endres med **setPrice**-metoden.
Testkode for denne oppgaven finner du her: [patterns/observable/StockTest.java](../../src/test/java/patterns/observable/StockTest.java).
## Del 2: StockIndex implements StockListener
Vi skal nå lage en veldig forenklet versjon av en aksjeindeks. I korte trekk bruker man en aksjeindeks til å måle utviklingen av et utvalg aksjer. Vår enkle, fiktive aksjeindeks **StockIndex** har et navn (**String**), indeks (**double**) og en liste med **Stock**-objektene som er inkludert i indeksen. Indeksen beregnes ut i fra aksjeprisene den "observerer", og vil være lik summen av disse. Når en av aksjeprisene øker eller synker, må tilstanden til **StockIndex**-objektet holdes konsistent med denne utviklingen. Dette lar seg gjøre ved at **StockIndex** observerer én eller flere **Stock**-objekter. Klassen skal ha følgende metoder:
- `StockIndex(String, Stock... )` - konstruktør som tar inn ingen, én eller flere aksjer (**Stock**-objekter). **Stock**-parameteret defineres som et såkalt [varargs-parameter](https://www.ntnu.no/wiki/display/tdt4100/Varargs+-+variabelt+antall+argumenter). NB: **StockIndex**-objektet skal holdes oppdatert på aksjeprisene allerede fra det er instansiert. Dersom en indeks instansieres uten **Stock**-objekter, skal aksjeindeksen være 0.
- `void addStock(Stock)` - metode for å legge til en aksjepris i indeksen.
- `void removeStock(Stock)` - metode for å fjerne en aksjepris fra indeksen.
- `double getIndex()` - hentemetode for indeksen.
I tillegg må **StockIndex**-klassen selvsagt implementere **StockListener** og dermed også lyttermetoden **stockPriceChanged**, slik at indeksen kan holdes oppdatert.
Testkode for denne oppgaven finner du her: [patterns/observable/StockIndexTest.java](../../src/test/java/patterns/observable/StockIndexTest.java).
## Ekstraoppgaver
I en del sammenhenger vil du ikke være interessert i alle småendringer i en aksjepris, men interessert i endringer utenfor et visst område eller av en viss størrelse. Kanskje vil du kjøpe aksjer hvis det er billig nok, ønsker å selge dersom prisen blir høy nok eller ønsker å vite om større endringer som kan være signal om viktige prisendringer. Så for å unngå å sende ut mange uinteressante prisoppdateringer, er det aktuelt med to typer utvidelser av **Stock**-klassen. I begge tilfellene bruker men en egen **addStockListener**-metode for å registrere lytteren og hva slags endring man er interessert i. Implementér utvidelsen(e) i en subklasse som du kaller **SmartStock**. Merk at denne utvidelsen av **Stock** ikke er så relevant å bruke sammen med **StockIndex**, siden den da vil miste noen oppdateringer og dermed kunne risikere å være inkonsistent innimellom.
### Pris*intervall*
I denne utvidelsen skal du støtte lyttere som ønsker å få beskjed kun dersom **Stock**-objektets pris settes utenfor et gitt intervall. Følgende metode må implementeres:
- `void addStockListener(StockListener, double min, double max)` - metode som legger til lyttere med krav til prisintervall.
Lyttere som er registrert med denne metoden skal bare varsles dersom **Stock**-objektets pris endres til en verdi utenfor det angitte intervallet. Hint: Bruk en eller flere **Map<StockListener, Double>**-felt til å holde oversikt over intervallene, evt. definér en hjelpeklasse som har felt for **StockListener** og minimum- og maksimumsverdiene.
### Pris*differanse*
I denne utvidelsen skal du støtte lyttere som ønsker å få beskjed kun når akkumulerte endringer av **Stock**-objektets pris er større enn en gitt differanse. Følgende metode må implementeres:
- `void addStockListener(StockListener, double difference)` - metode som legger til lyttere med krav til prisdifferanse.
Et viktig poeng med denne er varianter er hvilke tidligere verdien som skal gis til lyttermetoden **stockPriceChanged** sitt andre argument. Denne verdien skal være den forrige verdien som ble rapportert, som kan være en annen enn den forrige prisverdien. Anta f.eks. at en lytter registreres med **10** som prisdifferanse og at aksjeprisen starter som **110** og så endres til **118** og videre til **121**. Da skal lyttermetoden **stockPriceChanged** kalles med **110** som gammel verdi og **121** som ny verdi, fordi dette sett fra lytterens perspektiv er forrige verdi den fikk vite om. En annen lytter som var registrert med prisdifferansen **5**, ville fått beskjed allerede ved første endring og da med **110** som gammel verdi og **118** som ny, men den ville ikke få beskjed om endringen fra **118** til **121**, fordi differansen da er for liten. Dersom prisen endrer seg videre til **124**, vil lytteren få beskjed og da med **118** som gammel verdi.
Testkode for denne oppgaven finner du her: [patterns/observable/SmartStockTest.java](../../src/test/java/patterns/observable/SmartStockTest.java).

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

92
pom.xml Normal file
View File

@ -0,0 +1,92 @@
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>tdt4100-v2021</groupId>
<artifactId>ovinger</artifactId>
<version>0.0.1-SNAPSHOT</version>
<properties>
<xtextVersion>2.9.1</xtextVersion>
</properties>
<!-- Commented out because it's never used anywhere -->
<!-- <repositories>
<repository>
<id>no.hal.jextest.lib</id>
<url>https://raw.githubusercontent.com/hallvard/jexercise/master/no.hal.jex.jextest.lib/m2/repository</url>
<releases><enabled>false</enabled></releases>
<snapshots><enabled>true</enabled></snapshots>
</repository>
</repositories> -->
<dependencies>
<!-- JavaFX FXML -->
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-fxml</artifactId>
<version>16-ea+5</version>
</dependency>
<!-- JUnit 5 -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>5.7.0</version>
</dependency>
<!-- JUnit 3/4 -->
<dependency>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
<version>5.7.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.eclipse.xtext/org.eclipse.xtext.xbase.lib -->
<dependency>
<groupId>org.eclipse.xtext</groupId>
<artifactId>org.eclipse.xtext.xbase.lib</artifactId>
<version>2.9.1</version>
</dependency>
<!-- <dependency>
<groupId>no.hal.jex</groupId>
<artifactId>no.hal.jex.jextest.lib</artifactId>
<version>0.9.0-SNAPSHOT</version>
</dependency> -->
</dependencies>
<build>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
<configuration>
<release>15</release>
<compilerArgs>
<arg>--enable-preview</arg>
<!-- Needed in order for the class diagram generation script
to output diagrams with correct parameter names -->
<arg>-parameters</arg>
</compilerArgs>
</configuration>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.0.0-M5</version>
<configuration>
<argLine>--enable-preview</argLine>
</configuration>
</plugin>
<plugin>
<artifactId>maven-failsafe-plugin</artifactId>
<version>3.0.0-M5</version>
<configuration>
<argLine>--enable-preview</argLine>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@ -0,0 +1,74 @@
package debugging;
public class CoffeeCup {
private double capacity;
private double currentVolume;
public CoffeeCup() {
this.capacity = 0.0;
this.currentVolume = 0.0;
}
public CoffeeCup(double capacity, double currentVolume){
if(isValidCapacity(capacity)){
this.capacity = capacity;
}
else{
throw new IllegalArgumentException("Illegal capacity given.");
}
if(isValidVolume(currentVolume)){
this.currentVolume = currentVolume;
}
else{
throw new IllegalArgumentException("Illegal volume given.");
}
}
private boolean isValidCapacity(double capacity){
if(capacity >= 0.0){
return true;
}
return false;
}
public void increaseCupSize(double biggerCapacity){
if(isValidCapacity(biggerCapacity)){
this.capacity += biggerCapacity;
}
}
private boolean isValidVolume(double volume){
if(volume > this.capacity || volume < 0.0){
return false;
}
return true;
}
private boolean canDrink(double volume){
if(this.currentVolume >= volume){
return true;
}
return false;
}
public void drinkCoffee(double volume){
if(isValidVolume(volume) && canDrink(volume)){
this.currentVolume -= volume;
}
else{
throw new IllegalArgumentException("You can't drink that much coffee!");
}
}
public void fillCoffee(double volume){
if(isValidVolume(this.currentVolume + volume)){
this.currentVolume += volume;
}
else{
throw new IllegalArgumentException("You just poured coffee all over the table. Good job.");
}
}
}

View File

@ -0,0 +1,59 @@
package debugging;
import java.util.Random;
public class CoffeeCupProgram {
private CoffeeCup cup;
private Random r;
public void init(){
cup = new CoffeeCup();
r = new Random(123456789L);
}
public void run(){
// part1();
part2();
}
private void part1(){
cup.increaseCupSize(40.0);
cup.fillCoffee(20.5);
cup.drinkCoffee(Math.floor(r.nextDouble()*20.5));
cup.fillCoffee(32.5);
cup.drinkCoffee(Math.ceil(r.nextDouble()*38.9));
cup.drinkCoffee(Math.ceil(r.nextDouble()*42));
cup.increaseCupSize(17);
cup.drinkCoffee(40);
cup.drinkCoffee(Math.ceil(r.nextDouble()*42));
cup.drinkCoffee(Math.floor(r.nextDouble()*20.5));
cup.fillCoffee(32.5);
cup.drinkCoffee(Math.ceil(r.nextDouble()*38.9));
cup.drinkCoffee(Math.ceil(r.nextDouble()*42));
cup.increaseCupSize(17);
}
private void part2(){
cup = new CoffeeCup(40.0, 20.5);
r = new Random(987654321L);
cup.drinkCoffee(Math.floor(r.nextDouble()*20.5));
cup.fillCoffee(Math.floor(r.nextDouble()*30));
cup.drinkCoffee(Math.ceil(r.nextDouble()*38.9));
cup.drinkCoffee(Math.ceil(r.nextDouble()*42));
cup.increaseCupSize(Math.floor(r.nextDouble()*26));
cup.fillCoffee(Math.ceil(r.nextDouble()*59));
cup.drinkCoffee(Math.ceil(r.nextDouble()*42));
cup.increaseCupSize(Math.floor(r.nextDouble()*35));
cup.fillCoffee(Math.floor(r.nextDouble()*30));
cup.increaseCupSize(Math.floor(r.nextDouble()*26));
}
public static void main(String[] args) {
CoffeeCupProgram program = new CoffeeCupProgram();
program.init();
program.run();
}
}

View File

@ -0,0 +1,48 @@
package debugging;
import java.util.Iterator;
import java.util.NoSuchElementException;
public class StringMergingIterator implements Iterator<String> {
private Iterator<String> first;
private Iterator<String> second;
private boolean turnSwitch;
public StringMergingIterator(Iterator<String> first, Iterator<String> second){
this.first = first;
this.second = second;
this.turnSwitch = true;
}
@Override
public boolean hasNext() {
return first.hasNext() || second.hasNext();
}
@Override
public String next() {
if(! hasNext()){ throw new NoSuchElementException(); }
String result = null;
if(! first.hasNext()){
result = second.next(); // Switch which iterator to return from
} else if(! second.hasNext()){
result = first.next();
} else {
if(turnSwitch){
result = first.next();
turnSwitch = false;
} else { // This block would execute after the previous with two ifs
result = second.next();
turnSwitch = true;
}
}
return result;
}
}

View File

@ -0,0 +1,40 @@
package debugging;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.eclipse.xtext.xbase.lib.CollectionLiterals;
public class StringMergingIteratorProgram {
public static void main(String[] args) throws Exception {
Iterator<String> one = CollectionLiterals.<String>newArrayList("a", "b").iterator();
Iterator<String> two = CollectionLiterals.<String>newArrayList("c", "d", "e").iterator();
StringMergingIterator mergeIterator = new StringMergingIterator(one, two);
List<String> values = new ArrayList<String>();
while(mergeIterator.hasNext()){
values.add(mergeIterator.next());
}
List<String> expectedOutput = CollectionLiterals.<String>newArrayList("a", "c", "b", "d", "e");
if(values.size() != expectedOutput.size()){
throw new Exception("The merged output did not contain the expected number of values. Try using the Eclipse debugger to see the difference between the lists.");
}
for(int i = 0; i < expectedOutput.size(); i++){
if(! values.get(i).equals(expectedOutput.get(i))){
throw new Exception("The iterator did not correctly merge the output. Try using the Eclipse debugger to see the difference between the lists.");
}
}
System.out.println("Success! StringMergingIterator correctly merged the output of the two lists.");
}
}

View File

@ -0,0 +1,99 @@
package encapsulation;
public class LineEditor {
private String text = "";
private int insertionIndex = 0;
/**
* Moves the insertion index to the left (if available)
*/
public void left() {
this.insertionIndex -= (this.insertionIndex != 0) ? 1 : 0;
}
/**
* Moves the insertion index to the right (if available)
*/
public void right() {
this.insertionIndex += (this.insertionIndex != this.text.length()) ? 1 : 0;
}
/**
* Inserts a string at the insertion index and shoves the insertionIndex behind the new text.
* @param s the string to insert
*/
public void insertString(String s) {
this.text = this.text.substring(0, this.insertionIndex)
+ s
+ this.text.substring(this.insertionIndex);
this.insertionIndex += s.length();
}
/**
* Removes the character to the left of the insertionIndex (if available)
*/
public void deleteLeft() {
if (this.insertionIndex != 0) {
this.text = this.text.substring(0, this.insertionIndex - 1)
+ this.text.substring(this.insertionIndex);
this.insertionIndex--;
}
}
/**
* Removes the character to the right of the insertionIndex (if available)
*/
public void deleteRight() {
if (this.insertionIndex != this.text.length())
this.text = this.text.substring(0, this.insertionIndex)
+ this.text.substring(this.insertionIndex + 1);
}
public String getText() {
return this.text;
}
public void setText(String s) {
if (this.insertionIndex > s.length())
this.insertionIndex = s.length();
this.text = s;
this.insertionIndex = s.length();
}
public int getInsertionIndex() {
return this.insertionIndex;
}
public void setInsertionIndex(int i) throws IllegalArgumentException {
if ( i < 0 || this.text.length() < i)
throw new IllegalArgumentException("The insertion index has to be inside the scope of the text");
this.insertionIndex = i;
}
@Override
public String toString() {
return this.text.substring(0, this.insertionIndex)
+ "|"
+ this.text.substring(this.insertionIndex);
}
public static void main(String[] args) {
LineEditor lineeditor = new LineEditor();
lineeditor.setText("test");
System.out.println(lineeditor);
lineeditor.left();
System.out.println(lineeditor);
lineeditor.right();
System.out.println(lineeditor);
lineeditor.setInsertionIndex(2);
System.out.println(lineeditor);
lineeditor.deleteRight();
System.out.println(lineeditor);
lineeditor.deleteLeft();
System.out.println(lineeditor);
lineeditor.insertString("ex");
System.out.println(lineeditor);
}
}

View File

@ -0,0 +1,230 @@
package encapsulation;
import java.text.DateFormat;
import java.text.ParseException;
import java.util.Date;
import java.util.List;
import java.util.Set;
import java.util.function.BiFunction;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream;
class Person {
private String[] name = {""};
private String email = "";
private Date birthday;
private char gender = '\0';
private String ssn;
public Date getBirthday() {
return this.birthday;
}
public String getEmail() {
return this.email;
}
public char getGender() {
return this.gender;
}
public String getName() {
return String.join(" ", this.name);
}
public String getSSN() {
return this.ssn;
}
public void setName(String name) throws IllegalArgumentException {
final String nameFormat = "[A-ZÆØÅa-zæøå]{2,}";
final String fullNameFormat = String.format("%s %s", nameFormat, nameFormat);
if (!name.matches(fullNameFormat))
throw new IllegalArgumentException("Couldn't parse name");
this.name = name.split(" ");
}
private boolean checkEmail(String email) {
final Set<String> cTLDs = Set.of("ad", "ae", "af", "ag", "ai", "al", "am", "ao", "aq", "ar", "as", "at", "au", "aw",
"ax", "az", "ba", "bb", "bd", "be", "bf", "bg", "bh", "bi", "bj", "bl", "bm", "bn", "bo", "bq", "br", "bs",
"bt", "bv", "bw", "by", "bz", "ca", "cc", "cd", "cf", "cg", "ch", "ci", "ck", "cl", "cm", "cn", "co", "cr",
"cu", "cv", "cw", "cx", "cy", "cz", "de", "dj", "dk", "dm", "do", "dz", "ec", "ee", "eg", "eh", "er", "es",
"et", "fi", "fj", "fk", "fm", "fo", "fr", "ga", "gb", "gd", "ge", "gf", "gg", "gh", "gi", "gl", "gm", "gn",
"gp", "gq", "gr", "gs", "gt", "gu", "gw", "gy", "hk", "hm", "hn", "hr", "ht", "hu", "id", "ie", "il", "im",
"in", "io", "iq", "ir", "is", "it", "je", "jm", "jo", "jp", "ke", "kg", "kh", "ki", "km", "kn", "kp", "kr",
"kw", "ky", "kz", "la", "lb", "lc", "li", "lk", "lr", "ls", "lt", "lu", "lv", "ly", "ma", "mc", "md", "me",
"mf", "mg", "mh", "mk", "ml", "mm", "mn", "mo", "mp", "mq", "mr", "ms", "mt", "mu", "mv", "mw", "mx", "my",
"mz", "na", "nc", "ne", "nf", "ng", "ni", "nl", "no", "np", "nr", "nu", "nz", "om", "pa", "pe", "pf", "pg",
"ph", "pk", "pl", "pm", "pn", "pr", "ps", "pt", "pw", "py", "qa", "re", "ro", "rs", "ru", "rw", "sa", "sb",
"sc", "sd", "se", "sg", "sh", "si", "sj", "sk", "sl", "sm", "sn", "so", "sr", "ss", "st", "sv", "sx", "sy",
"sz", "tc", "td", "tf", "tg", "th", "tj", "tk", "tl", "tm", "tn", "to", "tr", "tt", "tv", "tw", "tz", "ua",
"ug", "um", "us", "uy", "uz", "va", "vc", "ve", "vg", "vi", "vn", "vu", "wf", "ws", "ye", "yt", "za", "zm",
"zw");
String[] emailParts = email.split("[.@]");
if (emailParts.length != 4) return false;
return Stream.of(
emailParts[0].equalsIgnoreCase(this.name[0]),
emailParts[1].equalsIgnoreCase(this.name[1]),
emailParts[2].matches("[A-Za-z0-9]+"), // "\w" includes '_', so it's no good here.
cTLDs.contains(emailParts[3])
).allMatch(b -> b);
}
public void setEmail(String email) throws IllegalArgumentException {
if (!checkEmail(email))
throw new IllegalArgumentException("Couldn't parse email. Is the name correct?");
this.email = email;
}
public void setBirthday(Date birthday) throws IllegalArgumentException {
Date now = new Date(System.currentTimeMillis());
if (!birthday.before(now))
throw new IllegalArgumentException("This date is invalid. Please set a date in the past.");
this.birthday = birthday;
}
public void setGender(char gender) throws IllegalArgumentException {
final Set<Character> legalGenders = Set.of('M', 'F', '\0');
if (!legalGenders.contains(gender))
throw new IllegalArgumentException("Couldn't parse gender.");
this.gender = gender;
}
/**
* Emphasize a range of characters within a String
* @param s The string to modify
* @param start The start index of the range
* @param end The end index of the range
* @return The modified string with an emphazised range
*/
private static String emphasizeStringRange(String s, int start, int end)
throws IndexOutOfBoundsException {
return s.substring(0, start)
+ " <"
+ s.substring(start, end)
+ "> "
+ s.substring(end);
}
/**
* Make sure that the birthdate of the SSN is equal to the
* birthday of the person
* @param ssn The SSN to validate
*/
private void checkSSNBirthday(String ssn) throws IllegalArgumentException {
// Calendar birthdate = Calendar.getInstance();
try {
Date birthday =
DateFormat
.getDateInstance(DateFormat.SHORT)
.parse(
ssn.substring(0, 2) + "/" +
ssn.substring(2, 4) + "/" +
ssn.substring(4, 6)
);
if (!birthday.equals(this.birthday))
throw new IllegalArgumentException(
"The SSN birthday does not match this persons birthday"
);
} catch (ParseException e) {
throw new IllegalArgumentException(
"Could not parse SSN date: " + emphasizeStringRange(ssn, 0, 6)
);
}
}
/**
* Make sure that the SSN is valid based on the persons gender
* @param ssn The ssn to validate
*/
private void checkSSNMaleFemaleDigit(String ssn) throws IllegalArgumentException {
int maleOrFemaleDigit = Integer.parseInt(ssn.substring(8, 9));
boolean isMale = maleOrFemaleDigit % 2 == 1;
boolean isGenderCorrect =
(this.gender == 'M' && isMale) || (this.gender == 'F' && !isMale);
if (!isGenderCorrect)
throw new IllegalArgumentException(
"The digit "
+ emphasizeStringRange(ssn, 8, 9)
+ " has to be different because the person is a "
+ (isMale ? "male" : "female")
);
}
/**
* Check that the control ciphers are valid.
* @param ssn The SSN to validate
*/
private void checkSSNControlCiphers(String ssn) throws IllegalArgumentException {
final List<Integer> f = List.of(3, 7, 6, 1, 8, 9, 4, 5, 2);
final List<Integer> g = List.of(5, 4, 3, 2, 7, 6, 5, 4, 3, 2);
final BiFunction<List<Integer>, List<Integer>, Integer> processDigits =
(ds, cs) ->
IntStream
.range(0, ds.size())
.map(i -> ds.get(i) * cs.get(i))
.sum();
List<Integer> digits =
ssn
.substring(0, 9)
.chars()
.map(c -> c - '0') // remove offset from 0x00 to '0'
.boxed() // IntStream -> Stream<Integer>
.collect(Collectors.toList());
Integer k1 = 11 - processDigits.apply(digits, f) % 11;
digits.add(k1);
Integer k2 = 11 - processDigits.apply(digits, g) % 11;
boolean isControlCiphersCorrect =
(k1.toString() + k2.toString())
.equals(ssn.substring(9));
if (!isControlCiphersCorrect)
throw new IllegalArgumentException(
String.format(
"""
Control ciphers do not match the given input: %s
Expected: %s""",
emphasizeStringRange(ssn, 9, 11),
(k1 + " " + k2)
)
);
}
/**
* Validate an SSN based on the persons info.
* {@link #setBirthday(Date) setBirthday()} and {@link #setGender(char) setGender()}
* must have been run in order for this function to work.
* @param ssn The SSN to validate
*/
private void checkSSN(String ssn) throws IllegalArgumentException {
if (!ssn.matches("\\d{11}"))
throw new IllegalArgumentException("The amount of digits does not match the expected amount");
checkSSNBirthday(ssn);
checkSSNMaleFemaleDigit(ssn);
checkSSNControlCiphers(ssn);
}
public void setSSN(String ssn) throws IllegalArgumentException {
checkSSN(ssn);
this.ssn = ssn;
}
}

View File

@ -0,0 +1,58 @@
package encapsulation;
import java.util.EmptyStackException;
import java.util.Stack;
public class RPNCalc {
// Using stack instead of Deque in order to get access to elementAt(i)
private Stack<Double> stack = new Stack<Double>();
public void push(double num) {
this.stack.push(num);
}
public double pop() {
Double result = this.stack.pop();
return result.isNaN() ? Double.NaN : result;
}
public double peek(int i) {
return this.getSize() >= (i + 1) && i >= 0
? this.stack.elementAt(this.getSize() - 1 - i)
: Double.NaN;
}
public int getSize() {
return this.stack.size();
}
public void performOperation(char op) throws IllegalArgumentException {
double a, b;
try {
a = this.stack.pop();
b = this.stack.pop();
} catch (EmptyStackException e) {
throw new IllegalArgumentException("Not enough numbers in the calculator");
}
switch (op) {
case '+':
this.stack.push(b + a);
break;
case '-':
this.stack.push(b - a);
break;
case '*':
this.stack.push(b * a);
break;
case '/':
this.stack.push(b / a);
break;
case '^':
this.stack.push(Math.pow(a, b));
break;
default:
throw new IllegalArgumentException("No such operator implemented: " + op);
}
}
}

View File

@ -0,0 +1,83 @@
package encapsulation;
class Vehicle {
private final char type;
private final char fuel;
private String registrationNumber;
Vehicle(char type, char fuel, String registrationNumber) {
this.type = type;
this.fuel = fuel;
this.setRegistrationNumber(registrationNumber);
}
public char getVehicleType() {
return this.type;
}
public char getFuelType() {
return this.fuel;
}
public String getRegistrationNumber() {
return this.registrationNumber;
}
public void setRegistrationNumber(String registrationNumber) {
if (!checkRegistrationNumber(registrationNumber))
throw new IllegalArgumentException("Could not parse registration number.");
if (!checkFuelType(this.fuel, registrationNumber))
throw new IllegalArgumentException("This registration number is not valid for a vehicle with this type of fuel. The fuel type has to be given in upper case.");
if (!checkVehicleType(this.type, registrationNumber))
throw new IllegalArgumentException("This registration number is not valid for a vehicle of this type.");
this.registrationNumber = registrationNumber;
}
/**
* Test whether or not the registration number is syntactically valid.
*
* @return Whether or not the test was passed
*/
private static boolean checkRegistrationNumber(String registrationNumber) {
return registrationNumber.matches("[A-Z]{2}\\d{4,5}");
}
/**
* Test whether or not the registration number is valid based on the fuel type.
*
* @return Whether or not the test was passed
*/
private static boolean checkFuelType(char fuel, String registrationNumber) {
switch(fuel) {
case 'H':
return registrationNumber.matches("HY\\d+");
case 'E':
return registrationNumber.matches("(?:EL|EK)\\d+");
case 'D':
case 'G':
return registrationNumber.matches("(?!EL|EK|HY)[A-Z]{2}\\d+");
default:
return false;
}
}
/**
* Test whether or not the registration number is valid based on the vehicle type.
*
* @return Whether or not the test was passed
*/
private static boolean checkVehicleType(char type, String registrationNumber) {
switch(type) {
case 'C':
return registrationNumber.matches("[A-Z]{2}\\d{5}");
case 'M':
return registrationNumber.matches("(?!HY)[A-Z]{2}\\d{4}");
default:
return false;
}
}
}

View File

@ -0,0 +1,57 @@
package interfaces;
import static org.junit.Assert.assertThrows;
import java.util.Iterator;
import java.util.List;
import java.util.Optional;
import java.util.function.BinaryOperator;
public class BinaryComputingIterator implements Iterator<Double> {
private Iterator<Double> iterator1;
private Iterator<Double> iterator2;
private Optional<Double> default1 = Optional.empty();
private Optional<Double> default2 = Optional.empty();
private BinaryOperator<Double> operator;
public BinaryComputingIterator(Iterator<Double> iterator1,
Iterator<Double> iterator2,
BinaryOperator<Double> operator)
{
this.iterator1 = iterator1;
this.iterator2 = iterator2;
this.operator = operator;
}
public BinaryComputingIterator(Iterator<Double> iterator1,
Iterator<Double> iterator2,
Double default1,
Double default2,
BinaryOperator<Double> operator)
{
this.iterator1 = iterator1;
this.iterator2 = iterator2;
this.operator = operator;
this.default1 = Optional.ofNullable(default1);
this.default2 = Optional.ofNullable(default2);
}
@Override
public Double next() {
return this.operator.apply(
this.iterator1.hasNext() ? this.iterator1.next() : this.default1.orElseThrow(),
this.iterator2.hasNext() ? this.iterator2.next() : this.default2.orElseThrow()
);
}
@Override
public boolean hasNext() {
return this.default1.isPresent() || this.default2.isPresent()
? this.iterator1.hasNext() || this.iterator2.hasNext()
: this.iterator1.hasNext() && this.iterator2.hasNext();
}
}

View File

@ -0,0 +1,64 @@
package interfaces;
import java.util.EmptyStackException;
import java.util.HashMap;
import java.util.Map;
import java.util.Stack;
import java.util.function.BinaryOperator;
public class RPNCalc {
// Using stack instead of Deque in order to get access to elementAt(i)
private Stack<Double> stack = new Stack<>();
private Map<Character, BinaryOperator<Double>> operators = new HashMap<>();
public void push(double num) {
this.stack.push(num);
}
public double pop() {
try {
return this.stack.pop();
} catch (EmptyStackException e) {
return Double.NaN;
}
}
public double peek(int i) {
return this.getSize() >= (i + 1) && i >= 0
? this.stack.elementAt(this.getSize() - 1 - i)
: Double.NaN;
}
public int getSize() {
return this.stack.size();
}
public boolean addOperator(char op, BinaryOperator<Double> opFunc) {
if (this.operators.get(op) != null)
return false;
this.operators.put(op, opFunc);
return true;
}
public void removeOperator(char op) {
this.operators.remove(op);
}
public void performOperation(char op)
throws UnsupportedOperationException, IllegalArgumentException {
if (this.operators.get(op) == null)
throw new UnsupportedOperationException("Operator " + op + " does not exist");
double a, b;
try {
a = this.stack.pop();
b = this.stack.pop();
} catch (EmptyStackException e) {
throw new IllegalArgumentException("Not enough numbers in the calculator");
}
this.stack.push(this.operators.get(op).apply(b, a));
}
}

View File

@ -0,0 +1,78 @@
package meta;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import encapsulation.RPNCalc;
import objectstructures.Person;
public class ClassDiagram {
private Class<?> cls;
ClassDiagram(Class<?> cls) {
this.cls = cls;
}
private char detectModifiers(Member m) {
String firstModifier =
Modifier
.toString(m.getModifiers())
.split("\\s")[0];
return firstModifier == "public" ? '+' : '-';
}
private String formatFields() {
return
Stream.of(this.cls.getDeclaredFields())
.map(f -> detectModifiers(f) + f.getName() + ": " + f.getType().getSimpleName())
.collect(Collectors.joining("\n "));
}
private String formatParameters(Method m) {
return
Stream.of(m.getParameters())
.map(p -> p.getType().getSimpleName() + " " + p.getName())
.collect(Collectors.joining(", "));
}
private String formatMethods() {
return
Stream.of(this.cls.getDeclaredMethods())
.map(m ->
String.format(
"%c%s(%s): %s"
, detectModifiers(m)
, m.getName()
, formatParameters(m)
, m.getReturnType().getSimpleName()
))
.collect(Collectors.joining("\n "));
}
@Override
public String toString() {
return String.format(
"""
class %s {
%s
%s
}
"""
, this.cls.getName()
, this.formatFields()
, this.formatMethods()
);
}
public static void main(String[] args) {
ClassDiagram cd = new ClassDiagram(Person.class);
System.out.println(cd);
}
}

View File

@ -0,0 +1,13 @@
/**
* @author hal
*
*/
open module ovinger {
requires javafx.base;
requires javafx.controls;
requires javafx.fxml;
requires javafx.graphics;
requires junit;
requires org.eclipse.xtext.xbase.lib;
// requires no.hal.jex.jextest.lib;
}

View File

@ -0,0 +1,81 @@
package objectstructures;
public class CoffeeCup {
private double capacity;
private double currentVolume;
public CoffeeCup() {
this.capacity = 0.0;
this.currentVolume = 0.0;
}
public CoffeeCup(double capacity, double currentVolume){
if(isValidCapacity(capacity)){
this.capacity = capacity;
}
else{
throw new IllegalArgumentException("Illegal capacity given.");
}
if(isValidVolume(currentVolume)){
this.currentVolume = currentVolume;
}
else{
throw new IllegalArgumentException("Illegal volume given.");
}
}
public double getCapacity() {
return capacity;
}
public double getCurrentVolume() {
return currentVolume;
}
private boolean isValidCapacity(double capacity){
if(capacity >= 0.0){
return true;
}
return false;
}
public void increaseCupSize(double biggerCapacity){
if(isValidCapacity(biggerCapacity)){
this.capacity += biggerCapacity;
}
}
private boolean isValidVolume(double volume){
if(volume > this.capacity || volume < 0.0){
return false;
}
return true;
}
private boolean canDrink(double volume){
if(this.currentVolume >= volume){
return true;
}
return false;
}
public void drinkCoffee(double volume){
if(isValidVolume(volume) && canDrink(volume)){
this.currentVolume -= volume;
}
else{
throw new IllegalArgumentException("You can't drink that much coffee!");
}
}
public void fillCoffee(double volume){
if(isValidVolume(this.currentVolume + volume)){
this.currentVolume += volume;
}
else{
throw new IllegalArgumentException("You just poured coffee all over the table. Good job.");
}
}
}

View File

@ -0,0 +1,102 @@
package objectstructures;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Optional;
public class Person {
private String name;
private char gender;
private List<Person> children = new ArrayList<>();
private Optional<Person> mother = Optional.empty();
private Optional<Person> father = Optional.empty();
public Person(String name, char gender) {
this.name = name;
this.gender = gender;
}
public String getName() {
return name;
}
public char getGender() {
return gender;
}
public Collection<Person> getChildren() {
return children;
}
public int getChildCount() {
return children.size();
}
public Person getChild(int n) {
if (0 > n || n >= this.children.size())
throw new IllegalArgumentException("Child " + n + " doesn't exist");
return this.children.get(n);
}
public Person getMother() {
return mother.orElse(null);
}
public Person getFather() {
return father.orElse(null);
}
public void addChild(Person child) {
this.children.add(child);
if (this.gender == 'M' && child.getFather() != this)
child.setFather(this);
else if (this.gender == 'F' && child.getMother() != this)
child.setMother(this);
}
public void removeChild(Person child) {
this.children.remove(child);
if (this.gender == 'M' && child.getFather() == this)
child.setFather(null);
else if (this.gender == 'F' && child.getMother() == this)
child.setMother(null);
}
private void setParent(Person newParent,
Optional<Person> currentParent,
char gender)
{
if (newParent != null && newParent.getGender() != gender)
throw new IllegalArgumentException("Gender mismatch");
if (newParent == this)
throw new IllegalArgumentException("A person can not be its own parent");
var previousParent = currentParent;
currentParent = Optional.ofNullable(newParent);
if (newParent.getGender() == 'M')
this.father = Optional.ofNullable(newParent);
else
this.mother = Optional.ofNullable(newParent);
currentParent.ifPresent(p -> {
if (!p.getChildren().contains(this)) p.addChild(this);
});
previousParent.ifPresent(p -> p.removeChild(this));
}
public void setMother(Person mother) {
setParent(mother, this.mother, 'F');
}
public void setFather(Person father) {
setParent(father, this.father, 'M');
}
}

View File

@ -0,0 +1,23 @@
package setupcheck;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.layout.AnchorPane;
import javafx.stage.Stage;
public class GuiWorks extends Application {
public void start(Stage stage) {
Label hello = new Label("Om du ser denne teksten, så kan du kompilere og kjøre Java!");
AnchorPane pane = new AnchorPane();
pane.getChildren().add(hello);
stage.setScene(new Scene(pane));
stage.show();
}
public static void main(String... args) {
Application.launch(args);
}
}

View File

@ -0,0 +1,22 @@
package setupcheck;
public class SimpleClass {
private int myVal;
public SimpleClass(int myVal) {
this.myVal = myVal;
}
public int getMyVal() {
return myVal;
}
public static void main(String... args) {
Object o = "**The program runs**"; // Don't write code like this at home
// Check that preview features are enabled
if (o instanceof String s) {
System.out.println(s.substring(2, s.length()-2));
}
}
}

View File

@ -0,0 +1,90 @@
package stateandbehavior;
public class LineEditor {
private String text = "";
private int insertionIndex = 0;
/** flytter tekstinnsettingsposisjonen ett tegn til venstre (tilsvarende bruk av venstre piltast) */
public void left() {
this.insertionIndex -= (this.insertionIndex != 0) ? 1 : 0;
}
/** flytter tekstinnsettingsposisjonen ett tegn til høyre (tilsvarende bruk av høyre piltast) */
public void right() {
this.insertionIndex += (this.insertionIndex != this.text.length()) ? 1 : 0;
}
/** skyter teksten angitt av argumentet s inn i teksten på tekstinnsettingsposisjonen og forskyver tekstinnsettingsposisjonen mot høyre tilsvarende */
public void insertString(String s) {
this.text = this.text.substring(0, this.insertionIndex)
+ s
+ this.text.substring(this.insertionIndex);
this.insertionIndex += s.length();
}
/** fjerner tegnet til venstre for tekstinnsettingsposisjonen */
public void deleteLeft() {
if (this.insertionIndex != 0) {
this.text = this.text.substring(0, this.insertionIndex - 1)
+ this.text.substring(this.insertionIndex);
this.insertionIndex--;
}
}
/** fjerner tegnet til høyre for tekstinnsettingsposisjonen */
public void deleteRight() {
if (this.insertionIndex != this.text.length())
this.text = this.text.substring(0, this.insertionIndex)
+ this.text.substring(this.insertionIndex + 1);
}
/** returnerer teksten */
public String getText() {
return this.text;
}
/** oppdaterer teksten til å være den nye teksten */
public void setText(String s) {
this.text = s;
}
/** returnerer hvor i teksten redigeringer nå skjer */
public int getInsertionIndex() {
return this.insertionIndex;
}
/** oppdaterer hvor i teksten redigeringer skal skje */
public void setInsertionIndex(int i) {
this.insertionIndex = i;
}
@Override
public String toString() {
return this.text.substring(0, this.insertionIndex)
+ "|"
+ this.text.substring(this.insertionIndex);
}
public static void main(String[] args) {
LineEditor lineeditor = new LineEditor();
lineeditor.setText("test");
System.out.println(lineeditor);
lineeditor.right();
System.out.println(lineeditor);
lineeditor.left();
System.out.println(lineeditor);
lineeditor.setInsertionIndex(2);
System.out.println(lineeditor);
lineeditor.deleteRight();
System.out.println(lineeditor);
lineeditor.deleteLeft();
System.out.println(lineeditor);
lineeditor.insertString("ex");
System.out.println(lineeditor);
}
}

View File

@ -0,0 +1,161 @@
package stateandbehavior;
import java.util.Arrays;
public class Rectangle {
private int[] minPoint;
private int[] maxPoint;
public Rectangle() {
this.minPoint = (new int[] {});
this.maxPoint = (new int[] {});
}
/**
* returnerer henholdsvis x- og y-koordinatene til punktet med
* lavest (x,y)-verdier som er inneholdt i dette rektanglet. Dersom dette rektanglet er tomt, så skal 0 returneres.
*/
public int getMinX() {
return this.isEmpty() ? 0 : this.minPoint[0];
}
/**
* Se {@link #getMinX() getMinX()}
*/
public int getMinY() {
return this.isEmpty() ? 0 : this.minPoint[1];
}
/**
* returnerer henholdsvis x- og y-koordinatene til punktet med høyest (x,y)-verdier som er inneholdt i dette rektanglet. Dersom dette rektanglet er tomt, så skal 0 returneres.
*/
public int getMaxX() {
return this.isEmpty() ? 0 : this.maxPoint[0];
}
/**
* Se {@link #getMaxX() getMaxX()}
*/
public int getMaxY() {
return this.isEmpty() ? 0 : this.maxPoint[1];
}
/** returnerer henholdsvis bredden og høyden til rektanglet. Begge skal returnere 0, dersom dette rektanglet er tomt. */
public int getWidth() {
if (this.isEmpty()) return 0;
return this.maxPoint[0] - this.minPoint[0] + 1;
}
public int getHeight() {
if (this.isEmpty()) return 0;
return this.maxPoint[1] - this.minPoint[1] + 1;
}
/** returnerer true om rektanglet er tomt, dvs. om bredden og/eller høyden er 0. */
public boolean isEmpty(){
return this.minPoint.length == 0;
}
/** returnerer true om punktet (x,y) er inneholdt i dette rektanglet, og false ellers. */
public boolean contains(int x, int y) {
if (this.isEmpty()) return false;
return Arrays.asList(
(this.maxPoint[0] >= x),
(this.maxPoint[1] >= y),
(this.minPoint[0] <= x),
(this.minPoint[1] <= y))
.stream()
.allMatch(b -> b);
}
/** returnerer true om hele rect, dvs. alle punktene i rect, er inneholdt i dette rektanglet, og false ellers. Dersom rect er tomt, så skal false returneres. */
public boolean contains(Rectangle rect) {
if (rect.isEmpty()) return false;
return this.contains(rect.getMaxX(), rect.getMaxY())
&& this.contains(rect.getMinX(), rect.getMinY());
}
/** utvider (om nødvendig) dette rektanglet slik at det (akkurat) inneholder punktet (x,y). Etter kallet skal altså contains(x, y) returnere true. Returnerer true om dette rektanglet faktisk ble endret, ellers false. */
public boolean add(int x, int y) {
if (this.isEmpty()) {
this.maxPoint = (new int[] {x, y});
this.minPoint = (new int[] {x, y});
return true;
}
boolean wasChanged = false;
if (x > this.maxPoint[0]) {
this.maxPoint[0] = x;
wasChanged = true;
}
if (y > this.maxPoint[1]) {
this.maxPoint[1] = y;
wasChanged = true;
}
if (x < this.minPoint[0]) {
this.minPoint[0] = x;
wasChanged = true;
}
if (y < this.minPoint[1]) {
this.minPoint[1] = y;
wasChanged = true;
}
return wasChanged;
}
/** utvider (om nødvendig) dette rektanglet slik at det (akkurat) inneholder hele rect-argumentet. Returnerer true om dette rektanglet faktisk ble endret, ellers false. Dersom rect er tomt, så skal dette rektanglet ikke endres. */
public boolean add(Rectangle rect) {
boolean addedp1 = this.add(rect.getMaxX(), rect.getMaxY());
boolean addedp2 = this.add(rect.getMinX(), rect.getMinY());
return (addedp1 || addedp2);
}
/** returnerer et nytt Rectangle-objekt som tilsvarer kombisjonen av dette rektanglet og rect-argumentet. Alle punktene som finnes i ett av rektanglene skal altså være inneholdt i rektanglet som returneres. */
public Rectangle union(Rectangle rect) {
Rectangle result = new Rectangle();
result.add(this);
result.add(rect);
return result;
}
/** returnerer et nytt Rectangle-objekt som tilsvarer overlappet mellom dette rektanglet og rect-argumentet. Alle punktene som finnes i begge rektanglene skal altså være inneholdt i rektanglet som returneres. */
public Rectangle intersection(Rectangle rect) {
if (this.intersects(rect)) {
int maxX = Math.max(this.minPoint[0], rect.getMinX());
int maxY = Math.max(this.minPoint[1], rect.getMinY());
int minX = Math.min(this.maxPoint[0], rect.getMaxX());
int minY = Math.min(this.maxPoint[1], rect.getMaxY());
Rectangle result = new Rectangle();
result.add(minX, minY);
result.add(maxX, maxY);
return result;
} else {
return new Rectangle();
}
}
/** returnerer true om dette rektanglet og rect-argumentet overlapper, dvs. om det finnes ett eller flere punkter som er inneholdt i begge disse rektanglene. */
public boolean intersects(Rectangle rect) {
if (this.isEmpty() || rect.isEmpty()) return false;
return !((this.minPoint[0] > rect.getMaxX() || rect.getMinX() > this.maxPoint[0])
&& (this.minPoint[1] > rect.getMaxY() || rect.getMinY() > this.maxPoint[1]));
}
@Override
public String toString() {
if (this.isEmpty()) return "Empty";
return String.format("(%d, %d), (%d, %d), [%d x %d]",
minPoint[0],
minPoint[1],
maxPoint[0],
maxPoint[1],
getWidth(),
getHeight()
);
}
}

View File

@ -0,0 +1,111 @@
package stateandbehavior;
public class StopWatch {
private int totalTicks = 0;
private int totalTime = 0;
private int lapTimeOffset = 0; // amount of ticks at end of last lap
private int lastLapTimeOffset = -1; // amount of ticks at start of last lap
private boolean hasBeenStarted = false;
private boolean hasBeenStopped = false;
/** returnerer true om klokken har blitt startet eller false om den ikke har blitt startet */
public boolean isStarted() {
return this.hasBeenStarted;
}
/** returnerer true om klokken har blitt stoppet eller false om den ikke har blitt stoppet. Merk at her snakker vi om at klokken har blitt stoppet, ikke om klokken går eller ikke. */
public boolean isStopped() {
return this.hasBeenStopped;
}
/** returnerer det totale antall tikk (millisekunder) som har gått i levetiden til klokken uavhengig om klokken har vært startet eller stoppet. */
public int getTicks() {
return this.totalTicks;
}
/** returnerer antall tikk som har gått under tidtakningen. Hvis tidtakningen ikke har startet returner -1. Merk at hvis klokken er startet, men ikke stoppet, skal metoden returnere antall tikk som har gått siden klokken ble startet til nå. Hvis klokken er stoppet skal metoden returnere antall tikk som har gått fra klokken ble startet til klokken ble stoppet. */
public int getTime() {
return this.hasBeenStarted ? totalTime : -1;
}
/** returnerer antall tikk som har gått under nåværende rundetid til nå. Hvis tidtakningen ikke har startet returner -1. */
public int getLapTime() {
return this.hasBeenStarted ? totalTime - lapTimeOffset : -1;
}
/** returnerer lengden på forrige avsluttede rundetid. Hvis det ikke er noen tidligere rundetider returner -1. */
public int getLastLapTime() {
return this.lastLapTimeOffset != -1
? lapTimeOffset - lastLapTimeOffset
: -1;
}
/** forteller klokken at ticks antall tikk har gått. */
public void tick(int ticks) {
// this.totalTicks += ticks;
if (this.hasBeenStarted ^ this.hasBeenStopped)
this.totalTime += ticks;
}
/** starter klokken. */
public void start() {
this.hasBeenStarted = true;
}
/** avslutter nåværende rundetid og starter en ny. */
public void lap() {
this.lastLapTimeOffset = this.lapTimeOffset;
this.lapTimeOffset = this.totalTime;
}
/** stopper klokken. */
public void stop() {
if (this.hasBeenStarted) {
this.lap();
this.hasBeenStopped = true;
}
}
public String toString() {
return String.format(
"""
{
id: %d
ticks: %d
time: %d
currentLap: %d
lastLap: %d
hasStarted: %b
hasStopped: %b
}
"""
, this.hashCode()
, this.totalTicks
, this.getTime()
, this.getLapTime()
, this.getLastLapTime()
, this.hasBeenStarted
, this.hasBeenStopped
);
}
public static void main(String[] args) {
StopWatch sw = new StopWatch();
System.out.println(sw);
sw.tick(1);
System.out.println(sw);
sw.start();
System.out.println(sw);
sw.tick(3);
System.out.println(sw);
sw.lap();
System.out.println(sw);
sw.tick(2);
System.out.println(sw);
sw.stop();
System.out.println(sw);
sw.tick(2);
System.out.println(sw);
}
}

View File

@ -0,0 +1,165 @@
package encapsulation;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
public class LineEditorTest {
private LineEditor lineEditor;
@BeforeEach
public void setup() {
lineEditor = new LineEditor();
}
@Test
public void testConstructor() {
Assertions.assertEquals("", lineEditor.getText());
Assertions.assertEquals(0, lineEditor.getInsertionIndex());
}
@Test
public void testGetSetText() {
lineEditor.setText("ABC");
Assertions.assertEquals("ABC", lineEditor.getText());
Assertions.assertEquals(3, lineEditor.getInsertionIndex());
lineEditor.setText("");
Assertions.assertEquals("", lineEditor.getText());
Assertions.assertEquals(0, lineEditor.getInsertionIndex());
}
@Test
public void testGetSetInsertionIndex() {
lineEditor.setText("ABC");
Assertions.assertEquals("ABC", lineEditor.getText());
Assertions.assertEquals(3, lineEditor.getInsertionIndex());
lineEditor.setInsertionIndex(0);
Assertions.assertEquals("ABC", lineEditor.getText());
Assertions.assertEquals(0, lineEditor.getInsertionIndex());
lineEditor.setInsertionIndex(3);
Assertions.assertEquals("ABC", lineEditor.getText());
Assertions.assertEquals(3, lineEditor.getInsertionIndex());
}
@Test
public void testLeft() {
lineEditor.setText("Ja");
lineEditor.setInsertionIndex(2);
lineEditor.left();
Assertions.assertEquals("Ja", lineEditor.getText());
Assertions.assertEquals(1, lineEditor.getInsertionIndex());
lineEditor.left();
Assertions.assertEquals(0, lineEditor.getInsertionIndex());
lineEditor.left();
Assertions.assertEquals(0, lineEditor.getInsertionIndex());
}
@Test
public void testRight() {
lineEditor.setText("Ja");
lineEditor.setInsertionIndex(0);
lineEditor.right();
Assertions.assertEquals("Ja", lineEditor.getText());
Assertions.assertEquals(1, lineEditor.getInsertionIndex());
lineEditor.right();
Assertions.assertEquals(2, lineEditor.getInsertionIndex());
lineEditor.right();
Assertions.assertEquals(2, lineEditor.getInsertionIndex());
}
@Test
public void testDeleteLeft() {
lineEditor.setText("Ja");
lineEditor.setInsertionIndex(0);
lineEditor.deleteLeft();
Assertions.assertEquals("Ja", lineEditor.getText());
Assertions.assertEquals(0, lineEditor.getInsertionIndex());
lineEditor.setInsertionIndex(1);
lineEditor.deleteLeft();
Assertions.assertEquals("a", lineEditor.getText());
Assertions.assertEquals(0, lineEditor.getInsertionIndex());
lineEditor.deleteLeft();
Assertions.assertEquals("a", lineEditor.getText());
Assertions.assertEquals(0, lineEditor.getInsertionIndex());
lineEditor.setText("Ja");
lineEditor.setInsertionIndex(2);
lineEditor.deleteLeft();
Assertions.assertEquals("J", lineEditor.getText());
Assertions.assertEquals(1, lineEditor.getInsertionIndex());
lineEditor.deleteLeft();
Assertions.assertEquals("", lineEditor.getText());
Assertions.assertEquals(0, lineEditor.getInsertionIndex());
lineEditor.deleteLeft();
Assertions.assertEquals("", lineEditor.getText());
Assertions.assertEquals(0, lineEditor.getInsertionIndex());
}
@Test
public void testDeleteRight() {
lineEditor.setText("Ja");
lineEditor.setInsertionIndex(2);
lineEditor.deleteRight();
Assertions.assertEquals("Ja", lineEditor.getText());
Assertions.assertEquals(2, lineEditor.getInsertionIndex());
lineEditor.setInsertionIndex(1);
lineEditor.deleteRight();
Assertions.assertEquals("J", lineEditor.getText());
Assertions.assertEquals(1, lineEditor.getInsertionIndex());
lineEditor.deleteRight();
Assertions.assertEquals("J", lineEditor.getText());
Assertions.assertEquals(1, lineEditor.getInsertionIndex());
lineEditor.setText("Ja");
lineEditor.setInsertionIndex(0);
lineEditor.deleteRight();
Assertions.assertEquals("a", lineEditor.getText());
Assertions.assertEquals(0, lineEditor.getInsertionIndex());
lineEditor.deleteRight();
Assertions.assertEquals("", lineEditor.getText());
Assertions.assertEquals(0, lineEditor.getInsertionIndex());
lineEditor.deleteRight();
Assertions.assertEquals("", lineEditor.getText());
Assertions.assertEquals(0, lineEditor.getInsertionIndex());
}
@Test
public void testInsertString() {
lineEditor.insertString("");
Assertions.assertEquals("", lineEditor.getText());
Assertions.assertEquals(0, lineEditor.getInsertionIndex());
lineEditor.insertString("Java");
Assertions.assertEquals("Java", lineEditor.getText());
Assertions.assertEquals(4, lineEditor.getInsertionIndex());
lineEditor.insertString(" er gøy!");
Assertions.assertEquals("Java er gøy!", lineEditor.getText());
Assertions.assertEquals(12, lineEditor.getInsertionIndex());
lineEditor.setText("Javagøy!");
lineEditor.setInsertionIndex(4);
lineEditor.insertString(" er ");
Assertions.assertEquals("Java er gøy!", lineEditor.getText());
Assertions.assertEquals(8, lineEditor.getInsertionIndex());
lineEditor.setText("er gøy!");
lineEditor.setInsertionIndex(0);
lineEditor.insertString("Java ");
Assertions.assertEquals("Java er gøy!", lineEditor.getText());
Assertions.assertEquals(5, lineEditor.getInsertionIndex());
}
}

View File

@ -0,0 +1,176 @@
package encapsulation;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
public class LineEditorTest2 {
private LineEditor lineEditor;
@BeforeEach
public void setup() {
lineEditor = new LineEditor();
}
@Test
public void testConstructor() {
Assertions.assertEquals("", lineEditor.getText());
Assertions.assertEquals(0, lineEditor.getInsertionIndex());
}
@Test
public void testGetSetText() {
lineEditor.setText("ABC");
Assertions.assertEquals("ABC", lineEditor.getText());
Assertions.assertEquals(3, lineEditor.getInsertionIndex());
lineEditor.setText("");
Assertions.assertEquals("", lineEditor.getText());
Assertions.assertEquals(0, lineEditor.getInsertionIndex());
}
@Test
public void testGetSetInsertionIndex() {
lineEditor.setText("ABC");
Assertions.assertEquals("ABC", lineEditor.getText());
Assertions.assertEquals(3, lineEditor.getInsertionIndex());
lineEditor.setInsertionIndex(0);
Assertions.assertEquals("ABC", lineEditor.getText());
Assertions.assertEquals(0, lineEditor.getInsertionIndex());
lineEditor.setInsertionIndex(3);
Assertions.assertEquals("ABC", lineEditor.getText());
Assertions.assertEquals(3, lineEditor.getInsertionIndex());
Assertions.assertThrows(IllegalArgumentException.class, () -> {
lineEditor.setInsertionIndex(-1);
});
Assertions.assertThrows(IllegalArgumentException.class, () -> {
lineEditor.setInsertionIndex(4);
});
}
@Test
public void testLeft() {
lineEditor.setText("Ja");
lineEditor.setInsertionIndex(2);
lineEditor.left();
Assertions.assertEquals("Ja", lineEditor.getText());
Assertions.assertEquals(1, lineEditor.getInsertionIndex());
lineEditor.left();
Assertions.assertEquals("Ja", lineEditor.getText());
Assertions.assertEquals(0, lineEditor.getInsertionIndex());
lineEditor.left();
Assertions.assertEquals("Ja", lineEditor.getText());
Assertions.assertEquals(0, lineEditor.getInsertionIndex());
}
@Test
public void testRight() {
lineEditor.setText("Ja");
lineEditor.setInsertionIndex(0);
lineEditor.right();
Assertions.assertEquals("Ja", lineEditor.getText());
Assertions.assertEquals(1, lineEditor.getInsertionIndex());
lineEditor.right();
lineEditor.setText("Ja");
Assertions.assertEquals(2, lineEditor.getInsertionIndex());
lineEditor.right();
lineEditor.setText("Ja");
Assertions.assertEquals(2, lineEditor.getInsertionIndex());
}
@Test
public void testDeleteLeft() {
lineEditor.setText("Ja");
lineEditor.setInsertionIndex(0);
lineEditor.deleteLeft();
Assertions.assertEquals("Ja", lineEditor.getText());
Assertions.assertEquals(0, lineEditor.getInsertionIndex());
lineEditor.setInsertionIndex(1);
lineEditor.deleteLeft();
Assertions.assertEquals("a", lineEditor.getText());
Assertions.assertEquals(0, lineEditor.getInsertionIndex());
lineEditor.deleteLeft();
Assertions.assertEquals("a", lineEditor.getText());
Assertions.assertEquals(0, lineEditor.getInsertionIndex());
lineEditor.setText("Ja");
lineEditor.setInsertionIndex(2);
lineEditor.deleteLeft();
Assertions.assertEquals("J", lineEditor.getText());
Assertions.assertEquals(1, lineEditor.getInsertionIndex());
lineEditor.deleteLeft();
Assertions.assertEquals("", lineEditor.getText());
Assertions.assertEquals(0, lineEditor.getInsertionIndex());
lineEditor.deleteLeft();
Assertions.assertEquals("", lineEditor.getText());
Assertions.assertEquals(0, lineEditor.getInsertionIndex());
}
@Test
public void testDeleteRight() {
lineEditor.setText("Ja");
lineEditor.setInsertionIndex(2);
lineEditor.deleteRight();
Assertions.assertEquals("Ja", lineEditor.getText());
Assertions.assertEquals(2, lineEditor.getInsertionIndex());
lineEditor.setInsertionIndex(1);
lineEditor.deleteRight();
Assertions.assertEquals("J", lineEditor.getText());
Assertions.assertEquals(1, lineEditor.getInsertionIndex());
lineEditor.deleteRight();
Assertions.assertEquals("J", lineEditor.getText());
Assertions.assertEquals(1, lineEditor.getInsertionIndex());
lineEditor.setText("Ja");
lineEditor.setInsertionIndex(0);
lineEditor.deleteRight();
Assertions.assertEquals("a", lineEditor.getText());
Assertions.assertEquals(0, lineEditor.getInsertionIndex());
lineEditor.deleteRight();
Assertions.assertEquals("", lineEditor.getText());
Assertions.assertEquals(0, lineEditor.getInsertionIndex());
lineEditor.deleteRight();
Assertions.assertEquals("", lineEditor.getText());
Assertions.assertEquals(0, lineEditor.getInsertionIndex());
}
@Test
public void testInsertString() {
lineEditor.insertString("");
Assertions.assertEquals("", lineEditor.getText());
Assertions.assertEquals(0, lineEditor.getInsertionIndex());
lineEditor.insertString("Java");
Assertions.assertEquals("Java", lineEditor.getText());
Assertions.assertEquals(4, lineEditor.getInsertionIndex());
lineEditor.insertString(" er gøy!");
Assertions.assertEquals("Java er gøy!", lineEditor.getText());
Assertions.assertEquals(12, lineEditor.getInsertionIndex());
lineEditor.setText("Javagøy!");
lineEditor.setInsertionIndex(4);
lineEditor.insertString(" er ");
Assertions.assertEquals("Java er gøy!", lineEditor.getText());
Assertions.assertEquals(8, lineEditor.getInsertionIndex());
lineEditor.setText("er gøy!");
lineEditor.setInsertionIndex(0);
lineEditor.insertString("Java ");
Assertions.assertEquals("Java er gøy!", lineEditor.getText());
Assertions.assertEquals(5, lineEditor.getInsertionIndex());
}
}

View File

@ -0,0 +1,133 @@
package encapsulation;
import static org.junit.jupiter.api.Assertions.*;
import java.util.Date;
import java.util.Random;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
public class PersonTest {
private Person person;
@BeforeEach
public void setup() {
person = new Person();
}
private void testInvalidName(String invalidName, String existingName) {
Assertions.assertThrows(IllegalArgumentException.class, () -> {
person.setName(invalidName);
});
Assertions.assertEquals(existingName, person.getName());
}
@Test
void testSetName() throws Exception {
String name = person.getName();
testInvalidName("Ola", name);
testInvalidName("O N", name);
testInvalidName("Ola Mellom Nordmann", name);
testInvalidName("O. Nordmann", name);
Assertions.assertDoesNotThrow(() -> {
person.setName("Espen Askeladd");
});
Assertions.assertEquals("Espen Askeladd", person.getName());
}
@Test
void testSetBirthday() {
long today = new Date().getTime();
long offset = 1000L * 60L * 60L * 24L * 100L; // About 100 days
// Test with incorrect birthday
Assertions.assertThrows(IllegalArgumentException.class, () -> {
Date theFuture = new Date(today + offset);
person.setBirthday(theFuture);
});
// Test with correct birthday
Date thePast = new Date(today - offset);
Assertions.assertDoesNotThrow(() -> {
person.setBirthday(thePast);
});
Assertions.assertEquals(thePast, person.getBirthday());
}
private void testInvalidEmail(String invalidEmail, String existingEmail, Class<? extends Exception> ex) {
Assertions.assertThrows(ex, () -> {
person.setEmail(invalidEmail);
});
Assertions.assertEquals(existingEmail, person.getEmail());
}
private String generateValidDomain() {
Random random = new Random();
int length = random.nextInt(63) + 1;
String validCharacters = "abcdefghijklmnopqrstuvwxyz0123456789";
String domain = "";
for (int currentChar = 0; currentChar < length; currentChar++) {
int character = random.nextInt(36);
domain += validCharacters.substring(character, character + 1);
}
return domain;
}
@Test
void testSetEmail() {
person.setName("Ola Nordmann");
String email = person.getEmail();
testInvalidEmail("ola.nordmann@ntnu", email, IllegalArgumentException.class);
testInvalidEmail("ola.nordmann(at)ntnu.no", email, IllegalArgumentException.class);
testInvalidEmail("espen.askeladd@eventyr.no", email, IllegalArgumentException.class);
Assertions.assertDoesNotThrow(() -> {
person.setEmail("ola.nordmann@ntnu.no");
});
assertEquals("ola.nordmann@ntnu.no", person.getEmail());
}
@Test
public void testExtraCountryTopLevelDomains(){
String[] cTLDs = {"ad", "ae", "af", "ag", "ai", "al", "am", "ao", "aq", "ar", "as", "at", "au", "aw", "ax", "az", "ba", "bb", "bd", "be", "bf", "bg", "bh", "bi", "bj", "bl", "bm", "bn", "bo", "bq", "br", "bs", "bt", "bv", "bw", "by", "bz", "ca", "cc", "cd", "cf", "cg", "ch", "ci", "ck", "cl", "cm", "cn", "co", "cr", "cu", "cv", "cw", "cx", "cy", "cz", "de", "dj", "dk", "dm", "do", "dz", "ec", "ee", "eg", "eh", "er", "es", "et", "fi", "fj", "fk", "fm", "fo", "fr", "ga", "gb", "gd", "ge", "gf", "gg", "gh", "gi", "gl", "gm", "gn", "gp", "gq", "gr", "gs", "gt", "gu", "gw", "gy", "hk", "hm", "hn", "hr", "ht", "hu", "id", "ie", "il", "im", "in", "io", "iq", "ir", "is", "it", "je", "jm", "jo", "jp", "ke", "kg", "kh", "ki", "km", "kn", "kp", "kr", "kw", "ky", "kz", "la", "lb", "lc", "li", "lk", "lr", "ls", "lt", "lu", "lv", "ly", "ma", "mc", "md", "me", "mf", "mg", "mh", "mk", "ml", "mm", "mn", "mo", "mp", "mq", "mr", "ms", "mt", "mu", "mv", "mw", "mx", "my", "mz", "na", "nc", "ne", "nf", "ng", "ni", "nl", "no", "np", "nr", "nu", "nz", "om", "pa", "pe", "pf", "pg", "ph", "pk", "pl", "pm", "pn", "pr", "ps", "pt", "pw", "py", "qa", "re", "ro", "rs", "ru", "rw", "sa", "sb", "sc", "sd", "se", "sg", "sh", "si", "sj", "sk", "sl", "sm", "sn", "so", "sr", "ss", "st", "sv", "sx", "sy", "sz", "tc", "td", "tf", "tg", "th", "tj", "tk", "tl", "tm", "tn", "to", "tr", "tt", "tv", "tw", "tz", "ua", "ug", "um", "us", "uy", "uz", "va", "vc", "ve", "vg", "vi", "vn", "vu", "wf", "ws", "ye", "yt", "za", "zm", "zw"};
String[] invalidCTLDs = {"aa", "ab", "ac", "ah", "aj", "ak", "an", "ap", "av", "ay", "bc", "bk", "bp", "bu", "bx", "cb", "ce", "cj", "cp", "cq", "cs", "ct", "da", "db", "dc", "dd", "df", "dg", "dh", "di", "dl", "dn", "dp", "dq", "dr", "ds", "dt", "du", "dv", "dw", "dx", "dy", "ea", "eb", "ed", "ef", "ei", "ej", "ek", "el", "em", "en", "eo", "ep", "eq", "eu", "ev", "ew", "ex", "ey", "ez", "fa", "fb", "fc", "fd", "fe", "ff", "fg", "fh", "fl", "fn", "fp", "fq", "fs", "ft", "fu", "fv", "fw", "fx", "fy", "fz", "gc", "gj", "gk", "go", "gv", "gx", "gz", "ha", "hb", "hc", "hd", "he", "hf", "hg", "hh", "hi", "hj", "hl", "ho", "hp", "hq", "hs", "hv", "hw", "hx", "hy", "hz", "ia", "ib", "ic", "if", "ig", "ih", "ii", "ij", "ik", "ip", "iu", "iv", "iw", "ix", "iy", "iz", "ja", "jb", "jc", "jd", "jf", "jg", "jh", "ji", "jj", "jk", "jl", "jn", "jq", "jr", "js", "jt", "ju", "jv", "jw", "jx", "jy", "jz", "ka", "kb", "kc", "kd", "kf", "kj", "kk", "kl", "ko", "kq", "ks", "kt", "ku", "kv", "kx", "ld", "le", "lf", "lg", "lh", "lj", "ll", "lm", "ln", "lo", "lp", "lq", "lw", "lx", "lz", "mb", "mi", "mj", "nb", "nd", "nh", "nj", "nk", "nm", "nn", "nq", "ns", "nt", "nv", "nw", "nx", "ny", "oa", "ob", "oc", "od", "oe", "of", "og", "oh", "oi", "oj", "ok", "ol", "on", "oo", "op", "oq", "or", "os", "ot", "ou", "ov", "ow", "ox", "oy", "oz", "pb", "pc", "pd", "pi", "pj", "po", "pp", "pq", "pu", "pv", "px", "pz", "qb", "qc", "qd", "qe", "qf", "qg", "qh", "qi", "qj", "qk", "ql", "qm", "qn", "qo", "qp", "qq", "qr", "qs", "qt", "qu", "qv", "qw", "qx", "qy", "qz", "ra", "rb", "rc", "rd", "rf", "rg", "rh", "ri", "rj", "rk", "rl", "rm", "rn", "rp", "rq", "rr", "rt", "rv", "rx", "ry", "rz", "sf", "sp", "sq", "su", "sw", "ta", "tb", "te", "ti", "tp", "tq", "ts", "tu", "tx", "ty", "ub", "uc", "ud", "ue", "uf", "uh", "ui", "uj", "uk", "ul", "un", "uo", "up", "uq", "ur", "ut", "uu", "uv", "uw", "ux", "vb", "vd", "vf", "vh", "vj", "vk", "vl", "vm", "vo", "vp", "vq", "vr", "vs", "vt", "vv", "vw", "vx", "vy", "vz", "wa", "wb", "wc", "wd", "we", "wg", "wh", "wi", "wj", "wk", "wl", "wm", "wn", "wo", "wp", "wq", "wr", "wt", "wu", "wv", "ww", "wx", "wy", "wz", "xa", "xb", "xc", "xd", "xe", "xf", "xg", "xh", "xi", "xj", "xk", "xl", "xm", "xn", "xo", "xp", "xq", "xr", "xs", "xt", "xu", "xv", "xw", "xx", "xy", "xz", "ya", "yb", "yc", "yd", "yf", "yg", "yh", "yi", "yj", "yk", "yl", "ym", "yn", "yo", "yp", "yq", "yr", "ys", "yu", "yv", "yw", "yx", "yy", "yz", "zb", "zc", "zd", "ze", "zf", "zg", "zh", "zi", "zj", "zk", "zl", "zn", "zo", "zp", "zq", "zr", "zs", "zt", "zu", "zv", "zx", "zy", "zz"};
person.setName("John Doe");
String email = person.getEmail();
for (String cTLD : invalidCTLDs) {
testInvalidEmail("john.doe@ntnu." + cTLD, email, IllegalArgumentException.class);
}
for (String cTLD : cTLDs) {
final String localemail = "john.doe@" + generateValidDomain() + "." + cTLD;
Assertions.assertDoesNotThrow(() -> {
person.setEmail(localemail);
});
Assertions.assertEquals(localemail, person.getEmail());
}
}
@Test
void testSetGender() {
String validGenders = "FM\0";
char gender = person.getGender();
for (char c = '\0'; c < '\uFFFF'; c++) {
if (validGenders.indexOf(c) < 0) {
gender = person.getGender();
final char localc = c;
Assertions.assertThrows(IllegalArgumentException.class, () -> {
person.setGender(localc);
});
Assertions.assertEquals(gender, person.getGender());
}
else {
final char localc = c;
Assertions.assertDoesNotThrow(() -> {
person.setGender(localc);
});
Assertions.assertEquals(localc, person.getGender());
}
}
}
}

View File

@ -0,0 +1,63 @@
package encapsulation;
import static org.junit.jupiter.api.Assertions.*;
import java.util.Date;
import java.util.Random;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
public class PersonTest2 {
private Person person;
private static int[]
factors1 = {3, 7, 6, 1, 8, 9, 4, 5, 2},
factors2 = {5, 4, 3, 2, 7, 6, 5, 4, 3, 2};
@BeforeEach
public void setup() {
person = new Person();
}
@SuppressWarnings("deprecation")
@Test
public void testSetSSN() {
person.setBirthday(new Date(94, 0, 1));
person.setGender('M');
Assertions.assertDoesNotThrow(() -> {
person.setSSN("010194111" + generateValid(1, 1, 1, "010194"));
});
assertEquals("01019411156", person.getSSN());
Assertions.assertThrows(Exception.class, () -> {
person.setSSN("010194112" + generateValid(1, 1, 2, "010194"));
});
assertEquals("01019411156", person.getSSN());
Assertions.assertThrows(Exception.class, () -> {
person.setSSN("01019411122");
});
assertEquals("01019411156", person.getSSN());
}
private static String generateValid(int n1, int n2, int n3, String birthday) {
birthday = birthday + n1 + n2 + n3;
int k1 = 0, k2 = 0;
for(int i = 0; i < birthday.length(); i++) {
int num = Character.getNumericValue(birthday.charAt(i));
k1 += factors1[i] * num;
k2 += factors2[i] * num;
}
k1 = 11 - (k1 % 11);
if (k1 == 11) {
k1 = 0;
}
k2 += k1 * factors2[9];
k2 = 11 - (k2 % 11);
if (k2 == 11) {
k2 = 0;
}
return k1 + "" + k2;
}
}

View File

@ -0,0 +1,113 @@
package encapsulation;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
public class RPNCalcTest{
private RPNCalc calc;
@BeforeEach
public void setup() {
calc = new RPNCalc();
}
@Test
public void testPush() {
calc.push(1.0);
Assertions.assertEquals(1.0, calc.peek(0));
calc.push(2.0);
Assertions.assertEquals(2.0, calc.peek(0));
calc.push(3.0);
Assertions.assertEquals(3.0, calc.peek(0));
}
@Test
public void testPop() {
calc.push(1.0);
calc.push(2.0);
calc.push(3.0);
Assertions.assertEquals(3.0, calc.peek(0));
Assertions.assertEquals(3.0, calc.pop());
Assertions.assertEquals(2.0, calc.peek(0));
Assertions.assertEquals(2.0, calc.pop());
Assertions.assertEquals(1.0, calc.peek(0));
calc.push(2.0);
Assertions.assertEquals(2.0, calc.peek(0));
Assertions.assertEquals(2.0, calc.pop());
Assertions.assertEquals(1.0, calc.peek(0));
Assertions.assertEquals(1.0, calc.pop());
Assertions.assertEquals(0, calc.getSize());
}
@Test
public void testPeek() {
calc.push(0.0);
calc.push(1.0);
calc.push(2.0);
Assertions.assertEquals(2.0, calc.peek(0));
Assertions.assertEquals(1.0, calc.peek(1));
Assertions.assertEquals(0.0, calc.peek(2));
}
@Test
public void testEmptyStack() {
Assertions.assertEquals(Double.NaN, calc.peek(3));
Assertions.assertEquals(Double.NaN, calc.peek(-1));
}
@Test
public void testGetSize() {
Assertions.assertEquals(0, calc.getSize());
calc.push(1.0);
Assertions.assertEquals(1, calc.getSize());
calc.push(2.0);
Assertions.assertEquals(2, calc.getSize());
}
@Test
public void testAddOperation() {
calc.push(3.0);
calc.push(4.0);
calc.performOperation('+');
Assertions.assertEquals(1, calc.getSize());
Assertions.assertEquals(7.0, calc.peek(0));
}
@Test
public void testSubOperation() {
calc.push(7.0);
calc.push(2.0);
calc.performOperation('-');
Assertions.assertEquals(1, calc.getSize());
Assertions.assertEquals(5.0, calc.peek(0));
}
@Test
public void testMultOperation() {
calc.push(5.0);
calc.push(2.0);
calc.performOperation('*');
Assertions.assertEquals(1, calc.getSize());
Assertions.assertEquals(10.0, calc.peek(0));
}
@Test
public void testDivOperation() {
calc.push(10.0);
calc.push(4.0);
calc.performOperation('/');
Assertions.assertEquals(1, calc.getSize());
Assertions.assertEquals(2.5, calc.peek(0));
}
}

View File

@ -0,0 +1,159 @@
package encapsulation;
import static org.junit.jupiter.api.Assertions.*;
import java.util.Date;
import java.util.Random;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
public class VehicleTest {
Vehicle vehicle;
private void checkVehicleState(char vehicleType, char fuelType, String registrationNumber, Vehicle vehicle) {
Assertions.assertEquals(vehicleType, vehicle.getVehicleType());
Assertions.assertEquals(fuelType, vehicle.getFuelType());
Assertions.assertEquals(registrationNumber, vehicle.getRegistrationNumber());
}
private static void checkInvalidConstructor(char vehicleType, char fuelType, String registrationNumber) {
assertThrows(IllegalArgumentException.class, () -> {
new Vehicle(vehicleType, fuelType, registrationNumber);
});
}
private static void checkInvalidsetRegistration(Vehicle vehicle, String originalRegNum, String newRegNum) {
assertThrows(IllegalArgumentException.class, () -> {
vehicle.setRegistrationNumber(newRegNum);
});
assertEquals(originalRegNum, vehicle.getRegistrationNumber());
}
@Test
void testConstructor() {
vehicle = new Vehicle('C', 'D', "BN12345");
checkVehicleState('C', 'D', "BN12345", vehicle);
vehicle = new Vehicle('M', 'E', "EL1234");
checkVehicleState('M', 'E', "EL1234", vehicle);
checkInvalidConstructor('C', 'Y', "BN12345");
checkInvalidConstructor('M', 'H', "HY1234");
checkInvalidConstructor('P', 'D', "BN12345");
checkInvalidConstructor('C', 'G', "A1234");
checkInvalidConstructor('C', 'G', "A12345");
checkInvalidConstructor('C', 'G', "A123456");
checkInvalidConstructor('C', 'G', "A12344");
checkInvalidConstructor('C', 'G', "AÆ12345");
checkInvalidConstructor('C', 'G', "ab12345");
checkInvalidConstructor('C', 'G', "A12345");
checkInvalidConstructor('C', 'G', "A1B12345");
checkInvalidConstructor('M', 'G', "A1234");
checkInvalidConstructor('M', 'G', "A12345");
checkInvalidConstructor('M', 'G', "A123");
checkInvalidConstructor('M', 'G', "AB12345");
checkInvalidConstructor('M', 'G', "ABC1234");
checkInvalidConstructor('M', 'G', "ABC12345");
checkInvalidConstructor('C', 'G', "AÅ1234");
checkInvalidConstructor('C', 'G', "ab1234");
checkInvalidConstructor('C', 'G', "EL12345");
checkInvalidConstructor('C', 'G', "EK12345");
checkInvalidConstructor('C', 'G', "HY12345");
checkInvalidConstructor('C', 'D', "EL12345");
checkInvalidConstructor('C', 'D', "EK12345");
checkInvalidConstructor('C', 'D', "HY12345");
checkInvalidConstructor('C', 'H', "EL12345");
checkInvalidConstructor('C', 'H', "EK12345");
checkInvalidConstructor('C', 'H', "BN12345");
checkInvalidConstructor('C', 'E', "HY12345");
checkInvalidConstructor('C', 'E', "BN12345");
checkInvalidConstructor('M', 'G', "EL1234");
checkInvalidConstructor('M', 'G', "EK1234");
checkInvalidConstructor('M', 'G', "HY1234");
checkInvalidConstructor('M', 'D', "EL1234");
checkInvalidConstructor('M', 'D', "EK1234");
checkInvalidConstructor('M', 'D', "HY1234");
checkInvalidConstructor('M', 'E', "HY1234");
checkInvalidConstructor('M', 'E', "BN1234");
}
@Test
void testSetRegistrationNumber() {
vehicle = new Vehicle('C', 'D', "BN12345");
vehicle.setRegistrationNumber("AB54321");
checkVehicleState('C', 'D', "AB54321", vehicle);
vehicle = new Vehicle('M', 'E', "EK1234");
vehicle.setRegistrationNumber("EL4321");
checkVehicleState('M', 'E', "EL4321", vehicle);
vehicle = new Vehicle('C', 'D', "BN12345");
Assertions.assertThrows(IllegalArgumentException.class, () -> {
vehicle.setRegistrationNumber("AB654321");
});
checkVehicleState('C', 'D', "BN12345", vehicle);
vehicle = new Vehicle('M', 'E', "EL1234");
Assertions.assertThrows(IllegalArgumentException.class, () -> {
vehicle.setRegistrationNumber("HY1234");
});
checkVehicleState('M', 'E', "EL1234", vehicle);
vehicle = new Vehicle('C', 'G', "AB12345");
checkInvalidsetRegistration(vehicle, vehicle.getRegistrationNumber(), "A1234");
checkInvalidsetRegistration(vehicle, vehicle.getRegistrationNumber(), "A12345");
checkInvalidsetRegistration(vehicle, vehicle.getRegistrationNumber(), "AB1234");
checkInvalidsetRegistration(vehicle, vehicle.getRegistrationNumber(), "AB123456");
checkInvalidsetRegistration(vehicle, vehicle.getRegistrationNumber(), "ABC1234");
checkInvalidsetRegistration(vehicle, vehicle.getRegistrationNumber(), "AÆ12345");
checkInvalidsetRegistration(vehicle, vehicle.getRegistrationNumber(), "ab12345");
checkInvalidsetRegistration(vehicle, vehicle.getRegistrationNumber(), "A1B2345");
checkInvalidsetRegistration(vehicle, vehicle.getRegistrationNumber(), "A1234");
checkInvalidsetRegistration(vehicle, vehicle.getRegistrationNumber(), "A12345");
checkInvalidsetRegistration(vehicle, vehicle.getRegistrationNumber(), "AB1234");
checkInvalidsetRegistration(vehicle, vehicle.getRegistrationNumber(), "AB123456");
checkInvalidsetRegistration(vehicle, vehicle.getRegistrationNumber(), "ABC1234");
checkInvalidsetRegistration(vehicle, vehicle.getRegistrationNumber(), "AÆ12345");
checkInvalidsetRegistration(vehicle, vehicle.getRegistrationNumber(), "ab12345");
checkInvalidsetRegistration(vehicle, vehicle.getRegistrationNumber(), "A1B2345");
vehicle = new Vehicle('M', 'G', "AB1234");
checkInvalidsetRegistration(vehicle, vehicle.getRegistrationNumber(), "A1234");
checkInvalidsetRegistration(vehicle, vehicle.getRegistrationNumber(), "A12345");;
checkInvalidsetRegistration(vehicle, vehicle.getRegistrationNumber(), "AB123");
checkInvalidsetRegistration(vehicle, vehicle.getRegistrationNumber(), "AB12345");
checkInvalidsetRegistration(vehicle, vehicle.getRegistrationNumber(), "ABC1234");
checkInvalidsetRegistration(vehicle, vehicle.getRegistrationNumber(), "ABC12345");
checkInvalidsetRegistration(vehicle, vehicle.getRegistrationNumber(), "AÅ1234");
checkInvalidsetRegistration(vehicle, vehicle.getRegistrationNumber(), "ab1234");
vehicle = new Vehicle('C', 'G', "AB12345");
checkInvalidsetRegistration(vehicle, vehicle.getRegistrationNumber(), "EL12345");
checkInvalidsetRegistration(vehicle, vehicle.getRegistrationNumber(), "EK12345");
checkInvalidsetRegistration(vehicle, vehicle.getRegistrationNumber(), "HY12345");
vehicle = new Vehicle('C', 'D', "AB12345");
checkInvalidsetRegistration(vehicle, vehicle.getRegistrationNumber(), "EL12345");
checkInvalidsetRegistration(vehicle, vehicle.getRegistrationNumber(), "EK12345");
checkInvalidsetRegistration(vehicle, vehicle.getRegistrationNumber(), "HY12345");
vehicle = new Vehicle('C', 'H', "HY12345");
checkInvalidsetRegistration(vehicle, vehicle.getRegistrationNumber(), "EL12345");
checkInvalidsetRegistration(vehicle, vehicle.getRegistrationNumber(), "EK12345");
checkInvalidsetRegistration(vehicle, vehicle.getRegistrationNumber(), "BN12345");
vehicle = new Vehicle('C', 'E', "EL12345");
checkInvalidsetRegistration(vehicle, vehicle.getRegistrationNumber(), "HY12345");
checkInvalidsetRegistration(vehicle, vehicle.getRegistrationNumber(), "BN12345");
vehicle = new Vehicle('M', 'G', "AB1234");
checkInvalidsetRegistration(vehicle, vehicle.getRegistrationNumber(), "EL1234");
checkInvalidsetRegistration(vehicle, vehicle.getRegistrationNumber(), "EK1234");
checkInvalidsetRegistration(vehicle, vehicle.getRegistrationNumber(), "HY1234");
vehicle = new Vehicle('M', 'D', "AB1234");
checkInvalidsetRegistration(vehicle, vehicle.getRegistrationNumber(), "EL1234");
checkInvalidsetRegistration(vehicle, vehicle.getRegistrationNumber(), "EK1234");
checkInvalidsetRegistration(vehicle, vehicle.getRegistrationNumber(), "HY1234");
vehicle = new Vehicle('M', 'E', "EK1234");
checkInvalidsetRegistration(vehicle, vehicle.getRegistrationNumber(), "HY1234");
checkInvalidsetRegistration(vehicle, vehicle.getRegistrationNumber(), "BN1234");
}
}

View File

@ -0,0 +1,67 @@
package interfaces;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
public class BinaryComputingIteratorTest{
private Iterator<Double> iterator1, iterator2, iteratorShort;
@BeforeEach
public void setup() {
iterator1 = Arrays.asList(0.5, -2.0).iterator();
iterator2 = Arrays.asList(5.0, 3.0).iterator();
iteratorShort = Arrays.asList(5.0).iterator();
}
@Test
public void testMultiplication() {
BinaryComputingIterator binaryIt = new BinaryComputingIterator(iterator1, iterator2, (a, b) -> a * b);
Assertions.assertEquals(2.5, binaryIt.next());
Assertions.assertTrue(binaryIt.hasNext());
Assertions.assertEquals(-6.0, binaryIt.next());
Assertions.assertFalse(binaryIt.hasNext());
}
@Test
public void testAddition() {
BinaryComputingIterator binaryIt = new BinaryComputingIterator(iterator1, iterator2, (a, b) -> a + b);
Assertions.assertEquals(5.5, binaryIt.next());
Assertions.assertTrue(binaryIt.hasNext());
Assertions.assertEquals(1.0, binaryIt.next());
Assertions.assertFalse(binaryIt.hasNext());
}
@Test
public void testShortIterator() {
BinaryComputingIterator binaryIt = new BinaryComputingIterator(iterator1, iteratorShort, (a, b) -> a * b);
Assertions.assertEquals(2.5, binaryIt.next());
Assertions.assertFalse(binaryIt.hasNext());
}
@Test
public void testShortIteratorAndDefault() {
BinaryComputingIterator binaryIt = new BinaryComputingIterator(iterator1, iteratorShort, null, 2.0, (a, b) -> a * b);
Assertions.assertEquals(2.5, binaryIt.next());
Assertions.assertTrue(binaryIt.hasNext());
Assertions.assertEquals(-4.0, binaryIt.next());
Assertions.assertFalse(binaryIt.hasNext());
}
@Test
public void testEmptyIterator() {
BinaryComputingIterator binaryIt = new BinaryComputingIterator(new ArrayList().iterator(), new ArrayList().iterator(), (a, b) -> a * b);
Assertions.assertFalse(binaryIt.hasNext());
}
@Test
public void testEmptyIteratorAndDefault() {
BinaryComputingIterator binaryIt = new BinaryComputingIterator(new ArrayList().iterator(), new ArrayList().iterator(), 1.0, 2.0, (a, b) -> a * b);
Assertions.assertFalse(binaryIt.hasNext());
}
}

View File

@ -0,0 +1,59 @@
package interfaces;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
public class RPNCalcTest {
private RPNCalc calc;
@BeforeEach
public void setup() {
calc = new RPNCalc();
}
@Test
public void testPerformOperationWithoutOperation() {
Assertions.assertThrows(UnsupportedOperationException.class, () -> {
calc.performOperation('+');
});
}
@Test
public void testPerformOperation() {
calc.addOperator('+', (a, b) -> a * b); // Use "incorrect" definition to filter out cheating
calc.addOperator('l', (a, b) -> a * (a + b));
calc.push(4);
calc.push(3);
calc.performOperation('+');
Assertions.assertEquals(12.0, calc.pop());
Assertions.assertEquals(Double.NaN, calc.pop());
calc.push(4);
calc.push(3);
calc.performOperation('l');
Assertions.assertEquals(28.0, calc.pop());
Assertions.assertEquals(Double.NaN, calc.pop());
}
@Test
public void testAddOperator() {
Assertions.assertTrue(calc.addOperator('+', (a, b) -> a + b));
Assertions.assertTrue(calc.addOperator('-', (a, b) -> a - b));
Assertions.assertFalse(calc.addOperator('+', (a, b) -> a + b));
Assertions.assertFalse(calc.addOperator('-', (a, b) -> a * b));
}
@Test
public void testRemoveOperator() {
calc.addOperator('+', (a, b) -> a + b);
calc.removeOperator('+');
Assertions.assertThrows(UnsupportedOperationException.class, () -> {
calc.performOperation('+');
});
}
}

Some files were not shown because too many files have changed in this diff Show More