From 7fcdc1c788725f866de71fc9dfd8c4d1cb132b57 Mon Sep 17 00:00:00 2001 From: Orangerot Date: Fri, 24 May 2024 17:42:08 +0200 Subject: Initial commit --- 00-pflichtenheft/.gitignore | 302 + 00-pflichtenheft/.gitlab-ci.yml | 15 + 00-pflichtenheft/.latexmkrc | 7 + 00-pflichtenheft/Makefile | 10 + 00-pflichtenheft/assets/KIT_Deckblatt.pdf | Bin 0 -> 11836 bytes 00-pflichtenheft/assets/logo.pdf | Bin 0 -> 11797 bytes 00-pflichtenheft/assets/logo.svg | 211 + 00-pflichtenheft/assets/ui/help-desktop.png | Bin 0 -> 194287 bytes .../ui/listening-progess-account-dropdown.png | Bin 0 -> 215738 bytes .../assets/ui/listening-progress-desktop.png | Bin 0 -> 206009 bytes .../assets/ui/listening-progress-mobile.png | Bin 0 -> 362434 bytes 00-pflichtenheft/assets/ui/login-desktop.png | Bin 0 -> 140797 bytes 00-pflichtenheft/assets/ui/login-mobile.png | Bin 0 -> 212227 bytes .../assets/ui/podcasts-desktop-change-language.png | Bin 0 -> 436348 bytes 00-pflichtenheft/assets/ui/podcasts-desktop.png | Bin 0 -> 429403 bytes 00-pflichtenheft/assets/ui/podcasts-mobile.png | Bin 0 -> 346260 bytes 00-pflichtenheft/assets/ui/settings-desktop-1.png | Bin 0 -> 197214 bytes 00-pflichtenheft/assets/ui/settings-desktop-2.png | Bin 0 -> 187062 bytes 00-pflichtenheft/assets/ui/settings-mobile-1.png | Bin 0 -> 290715 bytes 00-pflichtenheft/assets/ui/settings-mobile-2.png | Bin 0 -> 297685 bytes 00-pflichtenheft/pflichtenheft.tex | 94 + 00-pflichtenheft/pgf-umlsd.sty | 329 + 00-pflichtenheft/sections/2-produkteinsatz.tex | 73 + .../sections/TikzPictures/ActivityLogin.tex | 56 + .../sections/TikzPictures/ActivityRegister.tex | 60 + .../sections/TikzPictures/ActivityResetPass.tex | 70 + .../SequenceSynchroniseSubscription.tex | 79 + .../sections/TikzPictures/UseCaseUML.tex | 46 + 00-pflichtenheft/sections/anforderungsanalyse.tex | 219 + 00-pflichtenheft/sections/benutzeroberflaeche.tex | 183 + 00-pflichtenheft/sections/einleitung.tex | 34 + 00-pflichtenheft/sections/glossar.tex | 272 + 00-pflichtenheft/sections/produktdaten.tex | 83 + 00-pflichtenheft/sections/produktfunktionen.tex | 215 + 00-pflichtenheft/sections/produktuebersicht.tex | 184 + 00-pflichtenheft/sections/tests.tex | 605 + 00-pflichtenheft/tikz-uml.sty | 5377 +++++++++ 00-pflichtenheft/titlepage.tex | 75 + 01-pflichtenheft-kolloquium/.gitignore | 302 + 01-pflichtenheft-kolloquium/.gitlab-ci.yml | 15 + 01-pflichtenheft-kolloquium/CHANGELOG.md | 34 + 01-pflichtenheft-kolloquium/Makefile | 10 + 01-pflichtenheft-kolloquium/README.md | 118 + .../logos/banner_2020_kit.jpg | Bin 0 -> 96829 bytes 01-pflichtenheft-kolloquium/notizen | 40 + 01-pflichtenheft-kolloquium/presentation.tex | 60 + 01-pflichtenheft-kolloquium/sdqbeamer.cls | 954 ++ .../slides/einf\303\274hrung.tex" | 40 + 01-pflichtenheft-kolloquium/slides/features.tex | 37 + .../slides/synchronisation.tex | 36 + 01-pflichtenheft-kolloquium/slides/zielsetzung.tex | 39 + 01-pflichtenheft-kolloquium/tikz-uml.sty | 5377 +++++++++ 10-entwurfsheft/.gitignore | 302 + 10-entwurfsheft/.gitlab-ci.yml | 36 + 10-entwurfsheft/.latexmkrc | 7 + 10-entwurfsheft/Makefile | 18 + 10-entwurfsheft/README.md | 31 + 10-entwurfsheft/assets/.gitignore | 3 + 10-entwurfsheft/assets/KIT_Deckblatt.pdf | Bin 0 -> 11836 bytes .../assets/diagrams/backendComponentDiagram.puml | 61 + 10-entwurfsheft/assets/diagrams/classdiagram.puml | 463 + .../assets/diagrams/componentdiagram.puml | 53 + 10-entwurfsheft/assets/diagrams/db.puml | 78 + 10-entwurfsheft/assets/diagrams/deployment.puml | 59 + .../diagrams/sequencediagram-forgotAndResetPW.puml | 41 + .../sequencediagram-getEpisodeActions.puml | 38 + ...ncediagram-getEpisodeActionsOfPodcastSince.puml | 32 + .../diagrams/sequencediagram-getSubscriptions.puml | 38 + .../assets/diagrams/sequencediagram-register.puml | 26 + .../sequencediagram-uploadEpisodeActions.puml | 38 + .../sequencediagram-uploadSubscriptions.puml | 32 + 10-entwurfsheft/assets/episode.png | Bin 0 -> 10778 bytes 10-entwurfsheft/assets/help.png | Bin 0 -> 14423 bytes 10-entwurfsheft/assets/lastupdate.png | Bin 0 -> 9363 bytes 10-entwurfsheft/assets/logo.pdf | Bin 0 -> 11797 bytes 10-entwurfsheft/assets/logo.svg | 211 + 10-entwurfsheft/assets/navbar.png | Bin 0 -> 8940 bytes 10-entwurfsheft/assets/password-margin.png | Bin 0 -> 8701 bytes 10-entwurfsheft/assets/password.png | Bin 0 -> 8781 bytes 10-entwurfsheft/assets/subscription.png | Bin 0 -> 79929 bytes 10-entwurfsheft/entwurfsheft.tex | 90 + 10-entwurfsheft/pgf-umlsd.sty | 329 + 10-entwurfsheft/sections/apidoc.tex | 558 + 10-entwurfsheft/sections/aufbau.tex | 89 + 10-entwurfsheft/sections/backend.tex | 28 + 10-entwurfsheft/sections/changes.tex | 41 + 10-entwurfsheft/sections/einleitung.tex | 22 + 10-entwurfsheft/sections/frontend.tex | 150 + 10-entwurfsheft/sections/glossar.tex | 371 + 10-entwurfsheft/sections/structure.tex | 104 + 10-entwurfsheft/tikz-uml.sty | 5377 +++++++++ 10-entwurfsheft/titlepage.tex | 75 + 11-entwurfsheft-kolloquium/.gitignore | 302 + 11-entwurfsheft-kolloquium/.gitlab-ci.yml | 36 + 11-entwurfsheft-kolloquium/CHANGELOG.md | 34 + 11-entwurfsheft-kolloquium/Makefile | 18 + 11-entwurfsheft-kolloquium/README.md | 118 + 11-entwurfsheft-kolloquium/assets/.gitignore | 3 + 11-entwurfsheft-kolloquium/assets/.gitingnore | 3 + .../assets/KIT_Deckblatt.pdf | Bin 0 -> 11836 bytes .../assets/diagrams/backendComponentDiagram.puml | 61 + .../diagrams/classdiagram-authentication.puml | 112 + .../diagrams/classdiagram-episode-actions.puml | 84 + .../assets/diagrams/classdiagram-model.puml | 109 + .../diagrams/classdiagram-subscriptions.puml | 75 + .../assets/diagrams/classdiagram-util.puml | 43 + .../assets/diagrams/classdiagram.puml | 68 + .../assets/diagrams/componentdiagram.puml | 53 + 11-entwurfsheft-kolloquium/assets/diagrams/db.puml | 78 + .../assets/diagrams/deployment.puml | 59 + .../diagrams/sequencediagram-forgotAndResetPW.puml | 41 + .../sequencediagram-getEpisodeActions.puml | 38 + ...ncediagram-getEpisodeActionsOfPodcastSince.puml | 32 + .../diagrams/sequencediagram-getSubscriptions.puml | 36 + .../assets/diagrams/sequencediagram-register.puml | 26 + .../sequencediagram-uploadEpisodeActions.puml | 38 + .../sequencediagram-uploadSubscriptions.puml | 32 + 11-entwurfsheft-kolloquium/assets/episode.png | Bin 0 -> 10778 bytes 11-entwurfsheft-kolloquium/assets/help.png | Bin 0 -> 14423 bytes 11-entwurfsheft-kolloquium/assets/lastupdate.png | Bin 0 -> 9363 bytes 11-entwurfsheft-kolloquium/assets/logo.pdf | Bin 0 -> 11797 bytes 11-entwurfsheft-kolloquium/assets/logo.svg | 211 + 11-entwurfsheft-kolloquium/assets/navbar.png | Bin 0 -> 8940 bytes .../assets/password-margin.png | Bin 0 -> 8701 bytes 11-entwurfsheft-kolloquium/assets/password.png | Bin 0 -> 8781 bytes 11-entwurfsheft-kolloquium/assets/subscription.png | Bin 0 -> 79929 bytes .../logos/banner_2020_kit.jpg | Bin 0 -> 96829 bytes 11-entwurfsheft-kolloquium/notizen | 40 + 11-entwurfsheft-kolloquium/presentation.tex | 52 + 11-entwurfsheft-kolloquium/presentation.tex.orig | 68 + 11-entwurfsheft-kolloquium/sdqbeamer.cls | 975 ++ 11-entwurfsheft-kolloquium/slides/apiExtension.tex | 39 + 11-entwurfsheft-kolloquium/slides/architecture.tex | 7 + 11-entwurfsheft-kolloquium/slides/changes.tex | 22 + 11-entwurfsheft-kolloquium/slides/classdiagram.tex | 57 + 11-entwurfsheft-kolloquium/slides/frontend.tex | 21 + 11-entwurfsheft-kolloquium/slides/pattern.tex | 26 + 11-entwurfsheft-kolloquium/slides/sequence.tex | 7 + 20-implementierungsheft/.gitignore | 305 + 20-implementierungsheft/.gitlab-ci.yml | 36 + 20-implementierungsheft/.latexmkrc | 7 + 20-implementierungsheft/Makefile | 18 + 20-implementierungsheft/README.md | 31 + 20-implementierungsheft/assets/.gitignore | 3 + 20-implementierungsheft/assets/KIT_Deckblatt.pdf | Bin 0 -> 11836 bytes .../assets/diagrams/backendComponentDiagram.puml | 61 + .../assets/diagrams/class_after.puml | 574 + .../assets/diagrams/classdiagram.puml | 463 + .../assets/diagrams/componentdiagram.puml | 79 + 20-implementierungsheft/assets/diagrams/db.puml | 78 + .../assets/diagrams/deployment.puml | 59 + .../assets/diagrams/gantt-plan.puml | 31 + .../assets/diagrams/gantt-reality.puml | 39 + .../diagrams/sequencediagram-forgotAndResetPW.puml | 41 + .../sequencediagram-getEpisodeActions.puml | 38 + ...ncediagram-getEpisodeActionsOfPodcastSince.puml | 32 + .../diagrams/sequencediagram-getSubscriptions.puml | 38 + .../assets/diagrams/sequencediagram-register.puml | 26 + .../sequencediagram-uploadEpisodeActions.puml | 38 + .../sequencediagram-uploadSubscriptions.puml | 32 + 20-implementierungsheft/assets/episode.png | Bin 0 -> 10778 bytes 20-implementierungsheft/assets/errorlog.png | Bin 0 -> 23928 bytes .../assets/floatinglabelinput.png | Bin 0 -> 6125 bytes .../assets/gantt-plan-eps-converted-to.pdf | Bin 0 -> 99852 bytes 20-implementierungsheft/assets/gantt-plan.eps | 8314 ++++++++++++++ 20-implementierungsheft/assets/gantt-plan.pdf | 0 .../assets/gantt-reality-eps-converted-to.pdf | Bin 0 -> 135081 bytes 20-implementierungsheft/assets/gantt-reality.eps | 11142 +++++++++++++++++++ 20-implementierungsheft/assets/gantt.png | Bin 0 -> 39076 bytes 20-implementierungsheft/assets/help.png | Bin 0 -> 14423 bytes 20-implementierungsheft/assets/lastupdate.png | Bin 0 -> 9363 bytes 20-implementierungsheft/assets/loading.png | Bin 0 -> 5236 bytes 20-implementierungsheft/assets/logo.pdf | Bin 0 -> 11797 bytes 20-implementierungsheft/assets/logo.svg | 211 + 20-implementierungsheft/assets/navbar.png | Bin 0 -> 8940 bytes 20-implementierungsheft/assets/password-margin.png | Bin 0 -> 8701 bytes 20-implementierungsheft/assets/password.png | Bin 0 -> 8781 bytes 20-implementierungsheft/assets/passwordinput.png | Bin 0 -> 6856 bytes .../assets/passwordvalidator.png | Bin 0 -> 24197 bytes 20-implementierungsheft/assets/progresstime.png | Bin 0 -> 4222 bytes 20-implementierungsheft/assets/subscription.png | Bin 0 -> 79929 bytes 20-implementierungsheft/implementierungsheft.tex | 98 + .../sections/anforderungsanalyse.tex | 185 + 20-implementierungsheft/sections/apidoc.tex | 191 + 20-implementierungsheft/sections/aufbau.tex | 47 + 20-implementierungsheft/sections/backend.tex | 28 + 20-implementierungsheft/sections/changes.tex | 241 + 20-implementierungsheft/sections/einleitung.tex | 11 + 20-implementierungsheft/sections/features.tex | 43 + 20-implementierungsheft/sections/features.tex.orig | 82 + 20-implementierungsheft/sections/frontend.tex | 196 + 20-implementierungsheft/sections/glossar.tex | 371 + 20-implementierungsheft/sections/methodology.tex | 16 + 20-implementierungsheft/sections/tests.tex | 21 + 20-implementierungsheft/sections/timeline.tex | 7 + 20-implementierungsheft/titlepage.tex | 75 + 21-implementierungsheft-kolloquium/.gitignore | 302 + 21-implementierungsheft-kolloquium/.gitlab-ci.yml | 37 + 21-implementierungsheft-kolloquium/CHANGELOG.md | 34 + 21-implementierungsheft-kolloquium/Makefile | 19 + 21-implementierungsheft-kolloquium/README.md | 118 + .../assets/.gitignore | 3 + .../assets/.gitingnore | 3 + .../assets/commits-dashboard.png | Bin 0 -> 44171 bytes .../assets/commits-server.png | Bin 0 -> 36705 bytes .../assets/contributors-dashboard.png | Bin 0 -> 193750 bytes .../assets/contributors-server.png | Bin 0 -> 222521 bytes .../assets/diagrams/backendComponentDiagram.puml | 61 + .../diagrams/classdiagram-authentication.puml | 112 + .../diagrams/classdiagram-episode-actions.puml | 84 + .../assets/diagrams/classdiagram-model.puml | 109 + .../diagrams/classdiagram-subscriptions.puml | 75 + .../assets/diagrams/classdiagram-util.puml | 43 + .../assets/diagrams/classdiagram.puml | 68 + .../assets/diagrams/componentdiagram.puml | 53 + .../assets/diagrams/db.puml | 78 + .../assets/diagrams/deployment.puml | 59 + .../diagrams/sequencediagram-forgotAndResetPW.puml | 41 + .../sequencediagram-getEpisodeActions.puml | 38 + ...ncediagram-getEpisodeActionsOfPodcastSince.puml | 32 + .../diagrams/sequencediagram-getSubscriptions.puml | 38 + .../assets/diagrams/sequencediagram-register.puml | 26 + .../sequencediagram-uploadEpisodeActions.puml | 38 + .../sequencediagram-uploadSubscriptions.puml | 32 + .../assets/episode.png | Bin 0 -> 10778 bytes .../assets/gantt-plan.puml | 31 + .../assets/gantt-reality.puml | 39 + 21-implementierungsheft-kolloquium/assets/help.png | Bin 0 -> 14423 bytes .../assets/lastupdate.png | Bin 0 -> 9363 bytes 21-implementierungsheft-kolloquium/assets/logo.svg | 211 + .../assets/navbar.png | Bin 0 -> 8940 bytes .../assets/password-margin.png | Bin 0 -> 8701 bytes .../assets/password.png | Bin 0 -> 8781 bytes .../assets/subscription.png | Bin 0 -> 79929 bytes .../logos/banner_2020_kit.jpg | Bin 0 -> 96829 bytes 21-implementierungsheft-kolloquium/notizen | 40 + .../presentation.tex | 53 + 21-implementierungsheft-kolloquium/sdqbeamer.cls | 975 ++ .../slides/changes.tex | 84 + .../slides/classdiagram.tex | 36 + .../slides/components.tex | 7 + .../slides/difficulties.tex | 34 + .../slides/einf\303\274hrung.tex" | 40 + .../slides/features.tex | 37 + .../slides/gantt.tex | 11 + .../slides/integrationstrategie.tex | 82 + .../slides/pattern.tex | 12 + .../slides/requirements.tex | 58 + .../slides/statistics.tex | 85 + .../slides/synchronisation.tex | 36 + .../slides/zielsetzung.tex | 39 + 21-implementierungsheft-kolloquium/tikz-uml.sty | 5377 +++++++++ 30-qualitaetsheft/.gitignore | 305 + 30-qualitaetsheft/.gitlab-ci.yml | 36 + 30-qualitaetsheft/.latexmkrc | 7 + 30-qualitaetsheft/Makefile | 18 + 30-qualitaetsheft/README.md | 31 + 30-qualitaetsheft/assets/.gitignore | 3 + 30-qualitaetsheft/assets/ChangedPassword.PNG | Bin 0 -> 2976 bytes 30-qualitaetsheft/assets/KIT_Deckblatt.pdf | Bin 0 -> 11836 bytes 30-qualitaetsheft/assets/WrongUser.PNG | Bin 0 -> 2847 bytes .../assets/coverage/authenticationService.png | Bin 0 -> 26274 bytes .../assets/coverage/episodeActionService.png | Bin 0 -> 14649 bytes 30-qualitaetsheft/assets/coverage/server.png | Bin 0 -> 42666 bytes .../assets/coverage/subscriptionsService.png | Bin 0 -> 14334 bytes 30-qualitaetsheft/assets/coverage/util.png | Bin 0 -> 17510 bytes .../assets/lasttest/goodSpecsApdex.png | Bin 0 -> 20917 bytes .../assets/lasttest/goodSpecsResponse.png | Bin 0 -> 22682 bytes .../assets/lasttest/goodSpecsTable.png | Bin 0 -> 46496 bytes .../assets/lasttest/minimumSpecsApdex.png | Bin 0 -> 22565 bytes .../assets/lasttest/minimumSpecsResponse.png | Bin 0 -> 21284 bytes .../assets/lasttest/minimumSpecsTable.png | Bin 0 -> 46556 bytes 30-qualitaetsheft/assets/logo.pdf | Bin 0 -> 11797 bytes 30-qualitaetsheft/assets/logo.svg | 211 + 30-qualitaetsheft/assets/notifications.png | Bin 0 -> 34492 bytes 30-qualitaetsheft/assets/select-subscriptions.png | Bin 0 -> 67845 bytes .../assets/sources/checkstyle-after-processed.txt | 1 + .../assets/sources/checkstyle-after.txt | 22 + .../assets/sources/checkstyle-before-processed.txt | 23 + .../assets/sources/checkstyle-before.txt | 820 ++ .../sources/linter-dashboard-after-processed.txt | 1 + .../assets/sources/linter-dashboard-after.txt | 10 + .../sources/linter-dashboard-before-processed.txt | 22 + .../assets/sources/linter-dashboard-before.txt | 358 + 30-qualitaetsheft/assets/unsubscribe-podcasts.png | Bin 0 -> 80156 bytes 30-qualitaetsheft/qualitaetsheft.tex | 106 + 30-qualitaetsheft/sections/backend.tex | 28 + 30-qualitaetsheft/sections/changes.tex | 67 + 30-qualitaetsheft/sections/codestyle.tex | 224 + 30-qualitaetsheft/sections/coverage.tex | 59 + 30-qualitaetsheft/sections/einleitung.tex | 13 + 30-qualitaetsheft/sections/frontend.tex | 196 + 30-qualitaetsheft/sections/glossar.tex | 391 + 30-qualitaetsheft/sections/lasttest.tex | 105 + 30-qualitaetsheft/sections/problems.tex | 65 + 30-qualitaetsheft/sections/testfaelle.tex | 182 + 30-qualitaetsheft/sections/tests.tex | 21 + 30-qualitaetsheft/titlepage.tex | 75 + 31-qualitaetsheft-kolloquium/.gitignore | 303 + 31-qualitaetsheft-kolloquium/.gitlab-ci.yml | 37 + 31-qualitaetsheft-kolloquium/CHANGELOG.md | 34 + 31-qualitaetsheft-kolloquium/Makefile | 19 + 31-qualitaetsheft-kolloquium/README.md | 118 + 31-qualitaetsheft-kolloquium/assets/.gitignore | 3 + 31-qualitaetsheft-kolloquium/assets/.gitingnore | 3 + .../assets/commits-dashboard.png | Bin 0 -> 44171 bytes .../assets/commits-server.png | Bin 0 -> 36705 bytes .../assets/contributors-dashboard.png | Bin 0 -> 193750 bytes .../assets/contributors-server.png | Bin 0 -> 222521 bytes .../assets/coverage/authenticationService.png | Bin 0 -> 26274 bytes .../assets/coverage/episodeActionService.png | Bin 0 -> 14649 bytes .../assets/coverage/server.png | Bin 0 -> 42666 bytes .../assets/coverage/subscriptionsService.png | Bin 0 -> 14334 bytes .../assets/coverage/util.png | Bin 0 -> 17510 bytes .../assets/diagrams/backendComponentDiagram.puml | 61 + .../diagrams/classdiagram-authentication.puml | 112 + .../diagrams/classdiagram-episode-actions.puml | 84 + .../assets/diagrams/classdiagram-model.puml | 109 + .../diagrams/classdiagram-subscriptions.puml | 75 + .../assets/diagrams/classdiagram-util.puml | 43 + .../assets/diagrams/classdiagram.puml | 68 + .../assets/diagrams/componentdiagram.puml | 53 + .../assets/diagrams/db.puml | 78 + .../assets/diagrams/deployment.puml | 59 + .../diagrams/sequencediagram-forgotAndResetPW.puml | 41 + .../sequencediagram-getEpisodeActions.puml | 38 + ...ncediagram-getEpisodeActionsOfPodcastSince.puml | 32 + .../diagrams/sequencediagram-getSubscriptions.puml | 38 + .../assets/diagrams/sequencediagram-register.puml | 26 + .../sequencediagram-uploadEpisodeActions.puml | 38 + .../sequencediagram-uploadSubscriptions.puml | 32 + 31-qualitaetsheft-kolloquium/assets/episode.png | Bin 0 -> 10778 bytes .../assets/gantt-plan.puml | 31 + .../assets/gantt-reality.puml | 39 + 31-qualitaetsheft-kolloquium/assets/help.png | Bin 0 -> 14423 bytes .../assets/lasttest/goodSpecsApdex.png | Bin 0 -> 20917 bytes .../assets/lasttest/goodSpecsResponse.png | Bin 0 -> 22682 bytes .../assets/lasttest/goodSpecsTable.png | Bin 0 -> 46496 bytes .../assets/lasttest/minimumSpecsApdex.png | Bin 0 -> 22565 bytes .../assets/lasttest/minimumSpecsResponse.png | Bin 0 -> 21284 bytes .../assets/lasttest/minimumSpecsTable.png | Bin 0 -> 46556 bytes 31-qualitaetsheft-kolloquium/assets/lastupdate.png | Bin 0 -> 9363 bytes 31-qualitaetsheft-kolloquium/assets/logo.svg | 211 + 31-qualitaetsheft-kolloquium/assets/navbar.png | Bin 0 -> 8940 bytes .../assets/password-margin.png | Bin 0 -> 8701 bytes 31-qualitaetsheft-kolloquium/assets/password.png | Bin 0 -> 8781 bytes .../assets/sources/checkstyle-after-processed.txt | 1 + .../assets/sources/checkstyle-after.txt | 22 + .../assets/sources/checkstyle-before-processed.txt | 23 + .../assets/sources/checkstyle-before.txt | 820 ++ .../sources/linter-dashboard-after-processed.txt | 1 + .../assets/sources/linter-dashboard-after.txt | 10 + .../sources/linter-dashboard-before-processed.txt | 22 + .../assets/sources/linter-dashboard-before.txt | 358 + .../assets/subscription.png | Bin 0 -> 79929 bytes .../logos/banner_2020_kit.jpg | Bin 0 -> 96829 bytes .../logos/kitlogo_de_rgb.pdf | Bin 0 -> 11304 bytes .../logos/kitlogo_en_rgb.pdf | Bin 0 -> 6459 bytes 31-qualitaetsheft-kolloquium/logos/pse_logo.pdf | Bin 0 -> 11797 bytes 31-qualitaetsheft-kolloquium/notizen | 40 + 31-qualitaetsheft-kolloquium/presentation.tex | 62 + 31-qualitaetsheft-kolloquium/sdqbeamer.cls | 975 ++ 31-qualitaetsheft-kolloquium/slides/changes.tex | 146 + .../slides/classdiagram.tex | 36 + 31-qualitaetsheft-kolloquium/slides/codestyle.tex | 179 + 31-qualitaetsheft-kolloquium/slides/components.tex | 7 + 31-qualitaetsheft-kolloquium/slides/coverage.tex | 23 + .../slides/difficulties.tex | 34 + .../slides/einf\303\274hrung.tex" | 40 + 31-qualitaetsheft-kolloquium/slides/features.tex | 37 + 31-qualitaetsheft-kolloquium/slides/gantt.tex | 11 + .../slides/integrationstrategie.tex | 82 + 31-qualitaetsheft-kolloquium/slides/pattern.tex | 12 + .../slides/performance.tex | 56 + 31-qualitaetsheft-kolloquium/slides/problems.tex | 59 + .../slides/requirements.tex | 58 + 31-qualitaetsheft-kolloquium/slides/statistics.tex | 85 + .../slides/synchronisation.tex | 36 + 31-qualitaetsheft-kolloquium/slides/testing.tex | 59 + .../slides/zielsetzung.tex | 39 + 31-qualitaetsheft-kolloquium/tikz-uml.sty | 5377 +++++++++ 40-abschlusspraesentation/.gitignore | 26 + 40-abschlusspraesentation/.gitlab-ci.yml | 25 + 40-abschlusspraesentation/LICENSE | 674 ++ 40-abschlusspraesentation/Makefile | 10 + 40-abschlusspraesentation/README.md | 26 + 40-abschlusspraesentation/assets/.gitignore | 3 + .../assets/LastUpdateComp.png | Bin 0 -> 7796 bytes 40-abschlusspraesentation/assets/balken-old.svg | 301 + .../assets/balken-optimized.svg | 301 + 40-abschlusspraesentation/assets/bootstrap.svg | 1 + 40-abschlusspraesentation/assets/checkstyle.png | Bin 0 -> 137220 bytes 40-abschlusspraesentation/assets/coffee.gif | Bin 0 -> 568658 bytes 40-abschlusspraesentation/assets/commits.gnumeric | Bin 0 -> 4680 bytes 40-abschlusspraesentation/assets/commits.svg | 1662 +++ 40-abschlusspraesentation/assets/component6.drawio | 114 + 40-abschlusspraesentation/assets/component6.svg | 4 + .../assets/diagrams/componentdiagram.puml | 79 + .../assets/diagrams/packageDiagram1.puml | 67 + .../assets/diagrams/packageDiagram2.puml | 59 + .../assets/diagrams/packageDiagram3.puml | 51 + .../assets/diagrams/packageDiagram4.puml | 59 + 40-abschlusspraesentation/assets/docker.webp | Bin 0 -> 10998 bytes .../assets/fontawesome/LICENSE.txt | 165 + .../assets/fontawesome/css/all.css | 7955 +++++++++++++ .../assets/fontawesome/css/all.min.css | 9 + .../assets/fontawesome/css/brands.css | 1522 +++ .../assets/fontawesome/css/brands.min.css | 6 + .../assets/fontawesome/css/fontawesome.css | 6372 +++++++++++ .../assets/fontawesome/css/fontawesome.min.css | 9 + .../assets/fontawesome/css/regular.css | 19 + .../assets/fontawesome/css/regular.min.css | 6 + .../assets/fontawesome/css/solid.css | 19 + .../assets/fontawesome/css/solid.min.css | 6 + .../assets/fontawesome/webfonts/fa-brands-400.ttf | Bin 0 -> 187208 bytes .../fontawesome/webfonts/fa-brands-400.woff2 | Bin 0 -> 108020 bytes .../assets/fontawesome/webfonts/fa-regular-400.ttf | Bin 0 -> 63952 bytes .../fontawesome/webfonts/fa-regular-400.woff2 | Bin 0 -> 24948 bytes .../assets/fontawesome/webfonts/fa-solid-900.ttf | Bin 0 -> 394628 bytes .../assets/fontawesome/webfonts/fa-solid-900.woff2 | Bin 0 -> 150124 bytes .../fontawesome/webfonts/fa-v4compatibility.ttf | Bin 0 -> 10172 bytes .../fontawesome/webfonts/fa-v4compatibility.woff2 | Bin 0 -> 4564 bytes 40-abschlusspraesentation/assets/gitlab.svg | 60 + 40-abschlusspraesentation/assets/jmeter.svg | 159 + 40-abschlusspraesentation/assets/latex.svg | 8 + 40-abschlusspraesentation/assets/lets-encrypt.svg | 1 + 40-abschlusspraesentation/assets/logo.png | Bin 0 -> 20530 bytes 40-abschlusspraesentation/assets/logo.svg | 234 + 40-abschlusspraesentation/assets/mariadb.svg | 15 + 40-abschlusspraesentation/assets/performance.svg | 466 + 40-abschlusspraesentation/assets/plantuml.svg | 41 + 40-abschlusspraesentation/assets/spring.svg | 51 + 40-abschlusspraesentation/assets/tachodiagramm.svg | 1683 +++ .../assets/ui-components/full (Kopie 1).png | Bin 0 -> 92372 bytes .../assets/ui-components/full.png | Bin 0 -> 92372 bytes .../assets/ui-components/layer1.png | Bin 0 -> 11541 bytes .../assets/ui-components/layer2.png | Bin 0 -> 23536 bytes .../assets/ui-components/layer3.png | Bin 0 -> 71591 bytes 40-abschlusspraesentation/assets/vue.svg | 2 + 40-abschlusspraesentation/package-lock.json | 4126 +++++++ 40-abschlusspraesentation/package.json | 23 + 40-abschlusspraesentation/presentation.md | 222 + 40-abschlusspraesentation/script.js | 16 + 40-abschlusspraesentation/style.css | 29 + 444 files changed, 100165 insertions(+) create mode 100644 00-pflichtenheft/.gitignore create mode 100644 00-pflichtenheft/.gitlab-ci.yml create mode 100644 00-pflichtenheft/.latexmkrc create mode 100644 00-pflichtenheft/Makefile create mode 100644 00-pflichtenheft/assets/KIT_Deckblatt.pdf create mode 100644 00-pflichtenheft/assets/logo.pdf create mode 100644 00-pflichtenheft/assets/logo.svg create mode 100644 00-pflichtenheft/assets/ui/help-desktop.png create mode 100644 00-pflichtenheft/assets/ui/listening-progess-account-dropdown.png create mode 100644 00-pflichtenheft/assets/ui/listening-progress-desktop.png create mode 100644 00-pflichtenheft/assets/ui/listening-progress-mobile.png create mode 100644 00-pflichtenheft/assets/ui/login-desktop.png create mode 100644 00-pflichtenheft/assets/ui/login-mobile.png create mode 100644 00-pflichtenheft/assets/ui/podcasts-desktop-change-language.png create mode 100644 00-pflichtenheft/assets/ui/podcasts-desktop.png create mode 100644 00-pflichtenheft/assets/ui/podcasts-mobile.png create mode 100644 00-pflichtenheft/assets/ui/settings-desktop-1.png create mode 100644 00-pflichtenheft/assets/ui/settings-desktop-2.png create mode 100644 00-pflichtenheft/assets/ui/settings-mobile-1.png create mode 100644 00-pflichtenheft/assets/ui/settings-mobile-2.png create mode 100644 00-pflichtenheft/pflichtenheft.tex create mode 100644 00-pflichtenheft/pgf-umlsd.sty create mode 100644 00-pflichtenheft/sections/2-produkteinsatz.tex create mode 100644 00-pflichtenheft/sections/TikzPictures/ActivityLogin.tex create mode 100644 00-pflichtenheft/sections/TikzPictures/ActivityRegister.tex create mode 100644 00-pflichtenheft/sections/TikzPictures/ActivityResetPass.tex create mode 100644 00-pflichtenheft/sections/TikzPictures/SequenceSynchroniseSubscription.tex create mode 100644 00-pflichtenheft/sections/TikzPictures/UseCaseUML.tex create mode 100644 00-pflichtenheft/sections/anforderungsanalyse.tex create mode 100644 00-pflichtenheft/sections/benutzeroberflaeche.tex create mode 100644 00-pflichtenheft/sections/einleitung.tex create mode 100644 00-pflichtenheft/sections/glossar.tex create mode 100644 00-pflichtenheft/sections/produktdaten.tex create mode 100644 00-pflichtenheft/sections/produktfunktionen.tex create mode 100644 00-pflichtenheft/sections/produktuebersicht.tex create mode 100644 00-pflichtenheft/sections/tests.tex create mode 100644 00-pflichtenheft/tikz-uml.sty create mode 100644 00-pflichtenheft/titlepage.tex create mode 100644 01-pflichtenheft-kolloquium/.gitignore create mode 100644 01-pflichtenheft-kolloquium/.gitlab-ci.yml create mode 100644 01-pflichtenheft-kolloquium/CHANGELOG.md create mode 100644 01-pflichtenheft-kolloquium/Makefile create mode 100644 01-pflichtenheft-kolloquium/README.md create mode 100644 01-pflichtenheft-kolloquium/logos/banner_2020_kit.jpg create mode 100644 01-pflichtenheft-kolloquium/notizen create mode 100644 01-pflichtenheft-kolloquium/presentation.tex create mode 100644 01-pflichtenheft-kolloquium/sdqbeamer.cls create mode 100644 "01-pflichtenheft-kolloquium/slides/einf\303\274hrung.tex" create mode 100644 01-pflichtenheft-kolloquium/slides/features.tex create mode 100644 01-pflichtenheft-kolloquium/slides/synchronisation.tex create mode 100644 01-pflichtenheft-kolloquium/slides/zielsetzung.tex create mode 100644 01-pflichtenheft-kolloquium/tikz-uml.sty create mode 100644 10-entwurfsheft/.gitignore create mode 100644 10-entwurfsheft/.gitlab-ci.yml create mode 100644 10-entwurfsheft/.latexmkrc create mode 100644 10-entwurfsheft/Makefile create mode 100644 10-entwurfsheft/README.md create mode 100644 10-entwurfsheft/assets/.gitignore create mode 100644 10-entwurfsheft/assets/KIT_Deckblatt.pdf create mode 100644 10-entwurfsheft/assets/diagrams/backendComponentDiagram.puml create mode 100644 10-entwurfsheft/assets/diagrams/classdiagram.puml create mode 100644 10-entwurfsheft/assets/diagrams/componentdiagram.puml create mode 100644 10-entwurfsheft/assets/diagrams/db.puml create mode 100644 10-entwurfsheft/assets/diagrams/deployment.puml create mode 100644 10-entwurfsheft/assets/diagrams/sequencediagram-forgotAndResetPW.puml create mode 100644 10-entwurfsheft/assets/diagrams/sequencediagram-getEpisodeActions.puml create mode 100644 10-entwurfsheft/assets/diagrams/sequencediagram-getEpisodeActionsOfPodcastSince.puml create mode 100644 10-entwurfsheft/assets/diagrams/sequencediagram-getSubscriptions.puml create mode 100644 10-entwurfsheft/assets/diagrams/sequencediagram-register.puml create mode 100644 10-entwurfsheft/assets/diagrams/sequencediagram-uploadEpisodeActions.puml create mode 100644 10-entwurfsheft/assets/diagrams/sequencediagram-uploadSubscriptions.puml create mode 100644 10-entwurfsheft/assets/episode.png create mode 100644 10-entwurfsheft/assets/help.png create mode 100644 10-entwurfsheft/assets/lastupdate.png create mode 100644 10-entwurfsheft/assets/logo.pdf create mode 100644 10-entwurfsheft/assets/logo.svg create mode 100644 10-entwurfsheft/assets/navbar.png create mode 100644 10-entwurfsheft/assets/password-margin.png create mode 100644 10-entwurfsheft/assets/password.png create mode 100644 10-entwurfsheft/assets/subscription.png create mode 100644 10-entwurfsheft/entwurfsheft.tex create mode 100644 10-entwurfsheft/pgf-umlsd.sty create mode 100644 10-entwurfsheft/sections/apidoc.tex create mode 100644 10-entwurfsheft/sections/aufbau.tex create mode 100644 10-entwurfsheft/sections/backend.tex create mode 100644 10-entwurfsheft/sections/changes.tex create mode 100644 10-entwurfsheft/sections/einleitung.tex create mode 100644 10-entwurfsheft/sections/frontend.tex create mode 100644 10-entwurfsheft/sections/glossar.tex create mode 100644 10-entwurfsheft/sections/structure.tex create mode 100644 10-entwurfsheft/tikz-uml.sty create mode 100644 10-entwurfsheft/titlepage.tex create mode 100644 11-entwurfsheft-kolloquium/.gitignore create mode 100644 11-entwurfsheft-kolloquium/.gitlab-ci.yml create mode 100644 11-entwurfsheft-kolloquium/CHANGELOG.md create mode 100644 11-entwurfsheft-kolloquium/Makefile create mode 100644 11-entwurfsheft-kolloquium/README.md create mode 100644 11-entwurfsheft-kolloquium/assets/.gitignore create mode 100644 11-entwurfsheft-kolloquium/assets/.gitingnore create mode 100644 11-entwurfsheft-kolloquium/assets/KIT_Deckblatt.pdf create mode 100644 11-entwurfsheft-kolloquium/assets/diagrams/backendComponentDiagram.puml create mode 100644 11-entwurfsheft-kolloquium/assets/diagrams/classdiagram-authentication.puml create mode 100644 11-entwurfsheft-kolloquium/assets/diagrams/classdiagram-episode-actions.puml create mode 100644 11-entwurfsheft-kolloquium/assets/diagrams/classdiagram-model.puml create mode 100644 11-entwurfsheft-kolloquium/assets/diagrams/classdiagram-subscriptions.puml create mode 100644 11-entwurfsheft-kolloquium/assets/diagrams/classdiagram-util.puml create mode 100644 11-entwurfsheft-kolloquium/assets/diagrams/classdiagram.puml create mode 100644 11-entwurfsheft-kolloquium/assets/diagrams/componentdiagram.puml create mode 100644 11-entwurfsheft-kolloquium/assets/diagrams/db.puml create mode 100644 11-entwurfsheft-kolloquium/assets/diagrams/deployment.puml create mode 100644 11-entwurfsheft-kolloquium/assets/diagrams/sequencediagram-forgotAndResetPW.puml create mode 100644 11-entwurfsheft-kolloquium/assets/diagrams/sequencediagram-getEpisodeActions.puml create mode 100644 11-entwurfsheft-kolloquium/assets/diagrams/sequencediagram-getEpisodeActionsOfPodcastSince.puml create mode 100644 11-entwurfsheft-kolloquium/assets/diagrams/sequencediagram-getSubscriptions.puml create mode 100644 11-entwurfsheft-kolloquium/assets/diagrams/sequencediagram-register.puml create mode 100644 11-entwurfsheft-kolloquium/assets/diagrams/sequencediagram-uploadEpisodeActions.puml create mode 100644 11-entwurfsheft-kolloquium/assets/diagrams/sequencediagram-uploadSubscriptions.puml create mode 100644 11-entwurfsheft-kolloquium/assets/episode.png create mode 100644 11-entwurfsheft-kolloquium/assets/help.png create mode 100644 11-entwurfsheft-kolloquium/assets/lastupdate.png create mode 100644 11-entwurfsheft-kolloquium/assets/logo.pdf create mode 100644 11-entwurfsheft-kolloquium/assets/logo.svg create mode 100644 11-entwurfsheft-kolloquium/assets/navbar.png create mode 100644 11-entwurfsheft-kolloquium/assets/password-margin.png create mode 100644 11-entwurfsheft-kolloquium/assets/password.png create mode 100644 11-entwurfsheft-kolloquium/assets/subscription.png create mode 100644 11-entwurfsheft-kolloquium/logos/banner_2020_kit.jpg create mode 100644 11-entwurfsheft-kolloquium/notizen create mode 100644 11-entwurfsheft-kolloquium/presentation.tex create mode 100644 11-entwurfsheft-kolloquium/presentation.tex.orig create mode 100644 11-entwurfsheft-kolloquium/sdqbeamer.cls create mode 100644 11-entwurfsheft-kolloquium/slides/apiExtension.tex create mode 100644 11-entwurfsheft-kolloquium/slides/architecture.tex create mode 100644 11-entwurfsheft-kolloquium/slides/changes.tex create mode 100644 11-entwurfsheft-kolloquium/slides/classdiagram.tex create mode 100644 11-entwurfsheft-kolloquium/slides/frontend.tex create mode 100644 11-entwurfsheft-kolloquium/slides/pattern.tex create mode 100644 11-entwurfsheft-kolloquium/slides/sequence.tex create mode 100644 20-implementierungsheft/.gitignore create mode 100644 20-implementierungsheft/.gitlab-ci.yml create mode 100644 20-implementierungsheft/.latexmkrc create mode 100644 20-implementierungsheft/Makefile create mode 100644 20-implementierungsheft/README.md create mode 100644 20-implementierungsheft/assets/.gitignore create mode 100644 20-implementierungsheft/assets/KIT_Deckblatt.pdf create mode 100644 20-implementierungsheft/assets/diagrams/backendComponentDiagram.puml create mode 100644 20-implementierungsheft/assets/diagrams/class_after.puml create mode 100644 20-implementierungsheft/assets/diagrams/classdiagram.puml create mode 100644 20-implementierungsheft/assets/diagrams/componentdiagram.puml create mode 100644 20-implementierungsheft/assets/diagrams/db.puml create mode 100644 20-implementierungsheft/assets/diagrams/deployment.puml create mode 100644 20-implementierungsheft/assets/diagrams/gantt-plan.puml create mode 100644 20-implementierungsheft/assets/diagrams/gantt-reality.puml create mode 100644 20-implementierungsheft/assets/diagrams/sequencediagram-forgotAndResetPW.puml create mode 100644 20-implementierungsheft/assets/diagrams/sequencediagram-getEpisodeActions.puml create mode 100644 20-implementierungsheft/assets/diagrams/sequencediagram-getEpisodeActionsOfPodcastSince.puml create mode 100644 20-implementierungsheft/assets/diagrams/sequencediagram-getSubscriptions.puml create mode 100644 20-implementierungsheft/assets/diagrams/sequencediagram-register.puml create mode 100644 20-implementierungsheft/assets/diagrams/sequencediagram-uploadEpisodeActions.puml create mode 100644 20-implementierungsheft/assets/diagrams/sequencediagram-uploadSubscriptions.puml create mode 100644 20-implementierungsheft/assets/episode.png create mode 100644 20-implementierungsheft/assets/errorlog.png create mode 100644 20-implementierungsheft/assets/floatinglabelinput.png create mode 100644 20-implementierungsheft/assets/gantt-plan-eps-converted-to.pdf create mode 100644 20-implementierungsheft/assets/gantt-plan.eps create mode 100644 20-implementierungsheft/assets/gantt-plan.pdf create mode 100644 20-implementierungsheft/assets/gantt-reality-eps-converted-to.pdf create mode 100644 20-implementierungsheft/assets/gantt-reality.eps create mode 100644 20-implementierungsheft/assets/gantt.png create mode 100644 20-implementierungsheft/assets/help.png create mode 100644 20-implementierungsheft/assets/lastupdate.png create mode 100644 20-implementierungsheft/assets/loading.png create mode 100644 20-implementierungsheft/assets/logo.pdf create mode 100644 20-implementierungsheft/assets/logo.svg create mode 100644 20-implementierungsheft/assets/navbar.png create mode 100644 20-implementierungsheft/assets/password-margin.png create mode 100644 20-implementierungsheft/assets/password.png create mode 100644 20-implementierungsheft/assets/passwordinput.png create mode 100644 20-implementierungsheft/assets/passwordvalidator.png create mode 100644 20-implementierungsheft/assets/progresstime.png create mode 100644 20-implementierungsheft/assets/subscription.png create mode 100644 20-implementierungsheft/implementierungsheft.tex create mode 100644 20-implementierungsheft/sections/anforderungsanalyse.tex create mode 100644 20-implementierungsheft/sections/apidoc.tex create mode 100644 20-implementierungsheft/sections/aufbau.tex create mode 100644 20-implementierungsheft/sections/backend.tex create mode 100644 20-implementierungsheft/sections/changes.tex create mode 100644 20-implementierungsheft/sections/einleitung.tex create mode 100644 20-implementierungsheft/sections/features.tex create mode 100644 20-implementierungsheft/sections/features.tex.orig create mode 100644 20-implementierungsheft/sections/frontend.tex create mode 100644 20-implementierungsheft/sections/glossar.tex create mode 100644 20-implementierungsheft/sections/methodology.tex create mode 100644 20-implementierungsheft/sections/tests.tex create mode 100644 20-implementierungsheft/sections/timeline.tex create mode 100644 20-implementierungsheft/titlepage.tex create mode 100644 21-implementierungsheft-kolloquium/.gitignore create mode 100644 21-implementierungsheft-kolloquium/.gitlab-ci.yml create mode 100644 21-implementierungsheft-kolloquium/CHANGELOG.md create mode 100644 21-implementierungsheft-kolloquium/Makefile create mode 100644 21-implementierungsheft-kolloquium/README.md create mode 100644 21-implementierungsheft-kolloquium/assets/.gitignore create mode 100644 21-implementierungsheft-kolloquium/assets/.gitingnore create mode 100644 21-implementierungsheft-kolloquium/assets/commits-dashboard.png create mode 100644 21-implementierungsheft-kolloquium/assets/commits-server.png create mode 100644 21-implementierungsheft-kolloquium/assets/contributors-dashboard.png create mode 100644 21-implementierungsheft-kolloquium/assets/contributors-server.png create mode 100644 21-implementierungsheft-kolloquium/assets/diagrams/backendComponentDiagram.puml create mode 100644 21-implementierungsheft-kolloquium/assets/diagrams/classdiagram-authentication.puml create mode 100644 21-implementierungsheft-kolloquium/assets/diagrams/classdiagram-episode-actions.puml create mode 100644 21-implementierungsheft-kolloquium/assets/diagrams/classdiagram-model.puml create mode 100644 21-implementierungsheft-kolloquium/assets/diagrams/classdiagram-subscriptions.puml create mode 100644 21-implementierungsheft-kolloquium/assets/diagrams/classdiagram-util.puml create mode 100644 21-implementierungsheft-kolloquium/assets/diagrams/classdiagram.puml create mode 100644 21-implementierungsheft-kolloquium/assets/diagrams/componentdiagram.puml create mode 100644 21-implementierungsheft-kolloquium/assets/diagrams/db.puml create mode 100644 21-implementierungsheft-kolloquium/assets/diagrams/deployment.puml create mode 100644 21-implementierungsheft-kolloquium/assets/diagrams/sequencediagram-forgotAndResetPW.puml create mode 100644 21-implementierungsheft-kolloquium/assets/diagrams/sequencediagram-getEpisodeActions.puml create mode 100644 21-implementierungsheft-kolloquium/assets/diagrams/sequencediagram-getEpisodeActionsOfPodcastSince.puml create mode 100644 21-implementierungsheft-kolloquium/assets/diagrams/sequencediagram-getSubscriptions.puml create mode 100644 21-implementierungsheft-kolloquium/assets/diagrams/sequencediagram-register.puml create mode 100644 21-implementierungsheft-kolloquium/assets/diagrams/sequencediagram-uploadEpisodeActions.puml create mode 100644 21-implementierungsheft-kolloquium/assets/diagrams/sequencediagram-uploadSubscriptions.puml create mode 100644 21-implementierungsheft-kolloquium/assets/episode.png create mode 100644 21-implementierungsheft-kolloquium/assets/gantt-plan.puml create mode 100644 21-implementierungsheft-kolloquium/assets/gantt-reality.puml create mode 100644 21-implementierungsheft-kolloquium/assets/help.png create mode 100644 21-implementierungsheft-kolloquium/assets/lastupdate.png create mode 100644 21-implementierungsheft-kolloquium/assets/logo.svg create mode 100644 21-implementierungsheft-kolloquium/assets/navbar.png create mode 100644 21-implementierungsheft-kolloquium/assets/password-margin.png create mode 100644 21-implementierungsheft-kolloquium/assets/password.png create mode 100644 21-implementierungsheft-kolloquium/assets/subscription.png create mode 100644 21-implementierungsheft-kolloquium/logos/banner_2020_kit.jpg create mode 100644 21-implementierungsheft-kolloquium/notizen create mode 100644 21-implementierungsheft-kolloquium/presentation.tex create mode 100644 21-implementierungsheft-kolloquium/sdqbeamer.cls create mode 100644 21-implementierungsheft-kolloquium/slides/changes.tex create mode 100644 21-implementierungsheft-kolloquium/slides/classdiagram.tex create mode 100644 21-implementierungsheft-kolloquium/slides/components.tex create mode 100644 21-implementierungsheft-kolloquium/slides/difficulties.tex create mode 100644 "21-implementierungsheft-kolloquium/slides/einf\303\274hrung.tex" create mode 100644 21-implementierungsheft-kolloquium/slides/features.tex create mode 100644 21-implementierungsheft-kolloquium/slides/gantt.tex create mode 100644 21-implementierungsheft-kolloquium/slides/integrationstrategie.tex create mode 100644 21-implementierungsheft-kolloquium/slides/pattern.tex create mode 100644 21-implementierungsheft-kolloquium/slides/requirements.tex create mode 100644 21-implementierungsheft-kolloquium/slides/statistics.tex create mode 100644 21-implementierungsheft-kolloquium/slides/synchronisation.tex create mode 100644 21-implementierungsheft-kolloquium/slides/zielsetzung.tex create mode 100644 21-implementierungsheft-kolloquium/tikz-uml.sty create mode 100644 30-qualitaetsheft/.gitignore create mode 100644 30-qualitaetsheft/.gitlab-ci.yml create mode 100644 30-qualitaetsheft/.latexmkrc create mode 100644 30-qualitaetsheft/Makefile create mode 100644 30-qualitaetsheft/README.md create mode 100644 30-qualitaetsheft/assets/.gitignore create mode 100644 30-qualitaetsheft/assets/ChangedPassword.PNG create mode 100644 30-qualitaetsheft/assets/KIT_Deckblatt.pdf create mode 100644 30-qualitaetsheft/assets/WrongUser.PNG create mode 100644 30-qualitaetsheft/assets/coverage/authenticationService.png create mode 100644 30-qualitaetsheft/assets/coverage/episodeActionService.png create mode 100644 30-qualitaetsheft/assets/coverage/server.png create mode 100644 30-qualitaetsheft/assets/coverage/subscriptionsService.png create mode 100644 30-qualitaetsheft/assets/coverage/util.png create mode 100644 30-qualitaetsheft/assets/lasttest/goodSpecsApdex.png create mode 100644 30-qualitaetsheft/assets/lasttest/goodSpecsResponse.png create mode 100644 30-qualitaetsheft/assets/lasttest/goodSpecsTable.png create mode 100644 30-qualitaetsheft/assets/lasttest/minimumSpecsApdex.png create mode 100644 30-qualitaetsheft/assets/lasttest/minimumSpecsResponse.png create mode 100644 30-qualitaetsheft/assets/lasttest/minimumSpecsTable.png create mode 100644 30-qualitaetsheft/assets/logo.pdf create mode 100644 30-qualitaetsheft/assets/logo.svg create mode 100644 30-qualitaetsheft/assets/notifications.png create mode 100644 30-qualitaetsheft/assets/select-subscriptions.png create mode 100644 30-qualitaetsheft/assets/sources/checkstyle-after-processed.txt create mode 100644 30-qualitaetsheft/assets/sources/checkstyle-after.txt create mode 100644 30-qualitaetsheft/assets/sources/checkstyle-before-processed.txt create mode 100644 30-qualitaetsheft/assets/sources/checkstyle-before.txt create mode 100644 30-qualitaetsheft/assets/sources/linter-dashboard-after-processed.txt create mode 100644 30-qualitaetsheft/assets/sources/linter-dashboard-after.txt create mode 100644 30-qualitaetsheft/assets/sources/linter-dashboard-before-processed.txt create mode 100644 30-qualitaetsheft/assets/sources/linter-dashboard-before.txt create mode 100644 30-qualitaetsheft/assets/unsubscribe-podcasts.png create mode 100644 30-qualitaetsheft/qualitaetsheft.tex create mode 100644 30-qualitaetsheft/sections/backend.tex create mode 100644 30-qualitaetsheft/sections/changes.tex create mode 100644 30-qualitaetsheft/sections/codestyle.tex create mode 100644 30-qualitaetsheft/sections/coverage.tex create mode 100644 30-qualitaetsheft/sections/einleitung.tex create mode 100644 30-qualitaetsheft/sections/frontend.tex create mode 100644 30-qualitaetsheft/sections/glossar.tex create mode 100644 30-qualitaetsheft/sections/lasttest.tex create mode 100644 30-qualitaetsheft/sections/problems.tex create mode 100644 30-qualitaetsheft/sections/testfaelle.tex create mode 100644 30-qualitaetsheft/sections/tests.tex create mode 100644 30-qualitaetsheft/titlepage.tex create mode 100644 31-qualitaetsheft-kolloquium/.gitignore create mode 100644 31-qualitaetsheft-kolloquium/.gitlab-ci.yml create mode 100644 31-qualitaetsheft-kolloquium/CHANGELOG.md create mode 100644 31-qualitaetsheft-kolloquium/Makefile create mode 100644 31-qualitaetsheft-kolloquium/README.md create mode 100644 31-qualitaetsheft-kolloquium/assets/.gitignore create mode 100644 31-qualitaetsheft-kolloquium/assets/.gitingnore create mode 100644 31-qualitaetsheft-kolloquium/assets/commits-dashboard.png create mode 100644 31-qualitaetsheft-kolloquium/assets/commits-server.png create mode 100644 31-qualitaetsheft-kolloquium/assets/contributors-dashboard.png create mode 100644 31-qualitaetsheft-kolloquium/assets/contributors-server.png create mode 100644 31-qualitaetsheft-kolloquium/assets/coverage/authenticationService.png create mode 100644 31-qualitaetsheft-kolloquium/assets/coverage/episodeActionService.png create mode 100644 31-qualitaetsheft-kolloquium/assets/coverage/server.png create mode 100644 31-qualitaetsheft-kolloquium/assets/coverage/subscriptionsService.png create mode 100644 31-qualitaetsheft-kolloquium/assets/coverage/util.png create mode 100644 31-qualitaetsheft-kolloquium/assets/diagrams/backendComponentDiagram.puml create mode 100644 31-qualitaetsheft-kolloquium/assets/diagrams/classdiagram-authentication.puml create mode 100644 31-qualitaetsheft-kolloquium/assets/diagrams/classdiagram-episode-actions.puml create mode 100644 31-qualitaetsheft-kolloquium/assets/diagrams/classdiagram-model.puml create mode 100644 31-qualitaetsheft-kolloquium/assets/diagrams/classdiagram-subscriptions.puml create mode 100644 31-qualitaetsheft-kolloquium/assets/diagrams/classdiagram-util.puml create mode 100644 31-qualitaetsheft-kolloquium/assets/diagrams/classdiagram.puml create mode 100644 31-qualitaetsheft-kolloquium/assets/diagrams/componentdiagram.puml create mode 100644 31-qualitaetsheft-kolloquium/assets/diagrams/db.puml create mode 100644 31-qualitaetsheft-kolloquium/assets/diagrams/deployment.puml create mode 100644 31-qualitaetsheft-kolloquium/assets/diagrams/sequencediagram-forgotAndResetPW.puml create mode 100644 31-qualitaetsheft-kolloquium/assets/diagrams/sequencediagram-getEpisodeActions.puml create mode 100644 31-qualitaetsheft-kolloquium/assets/diagrams/sequencediagram-getEpisodeActionsOfPodcastSince.puml create mode 100644 31-qualitaetsheft-kolloquium/assets/diagrams/sequencediagram-getSubscriptions.puml create mode 100644 31-qualitaetsheft-kolloquium/assets/diagrams/sequencediagram-register.puml create mode 100644 31-qualitaetsheft-kolloquium/assets/diagrams/sequencediagram-uploadEpisodeActions.puml create mode 100644 31-qualitaetsheft-kolloquium/assets/diagrams/sequencediagram-uploadSubscriptions.puml create mode 100644 31-qualitaetsheft-kolloquium/assets/episode.png create mode 100644 31-qualitaetsheft-kolloquium/assets/gantt-plan.puml create mode 100644 31-qualitaetsheft-kolloquium/assets/gantt-reality.puml create mode 100644 31-qualitaetsheft-kolloquium/assets/help.png create mode 100644 31-qualitaetsheft-kolloquium/assets/lasttest/goodSpecsApdex.png create mode 100644 31-qualitaetsheft-kolloquium/assets/lasttest/goodSpecsResponse.png create mode 100644 31-qualitaetsheft-kolloquium/assets/lasttest/goodSpecsTable.png create mode 100644 31-qualitaetsheft-kolloquium/assets/lasttest/minimumSpecsApdex.png create mode 100644 31-qualitaetsheft-kolloquium/assets/lasttest/minimumSpecsResponse.png create mode 100644 31-qualitaetsheft-kolloquium/assets/lasttest/minimumSpecsTable.png create mode 100644 31-qualitaetsheft-kolloquium/assets/lastupdate.png create mode 100644 31-qualitaetsheft-kolloquium/assets/logo.svg create mode 100644 31-qualitaetsheft-kolloquium/assets/navbar.png create mode 100644 31-qualitaetsheft-kolloquium/assets/password-margin.png create mode 100644 31-qualitaetsheft-kolloquium/assets/password.png create mode 100644 31-qualitaetsheft-kolloquium/assets/sources/checkstyle-after-processed.txt create mode 100644 31-qualitaetsheft-kolloquium/assets/sources/checkstyle-after.txt create mode 100644 31-qualitaetsheft-kolloquium/assets/sources/checkstyle-before-processed.txt create mode 100644 31-qualitaetsheft-kolloquium/assets/sources/checkstyle-before.txt create mode 100644 31-qualitaetsheft-kolloquium/assets/sources/linter-dashboard-after-processed.txt create mode 100644 31-qualitaetsheft-kolloquium/assets/sources/linter-dashboard-after.txt create mode 100644 31-qualitaetsheft-kolloquium/assets/sources/linter-dashboard-before-processed.txt create mode 100644 31-qualitaetsheft-kolloquium/assets/sources/linter-dashboard-before.txt create mode 100644 31-qualitaetsheft-kolloquium/assets/subscription.png create mode 100644 31-qualitaetsheft-kolloquium/logos/banner_2020_kit.jpg create mode 100644 31-qualitaetsheft-kolloquium/logos/kitlogo_de_rgb.pdf create mode 100644 31-qualitaetsheft-kolloquium/logos/kitlogo_en_rgb.pdf create mode 100644 31-qualitaetsheft-kolloquium/logos/pse_logo.pdf create mode 100644 31-qualitaetsheft-kolloquium/notizen create mode 100644 31-qualitaetsheft-kolloquium/presentation.tex create mode 100644 31-qualitaetsheft-kolloquium/sdqbeamer.cls create mode 100644 31-qualitaetsheft-kolloquium/slides/changes.tex create mode 100644 31-qualitaetsheft-kolloquium/slides/classdiagram.tex create mode 100644 31-qualitaetsheft-kolloquium/slides/codestyle.tex create mode 100644 31-qualitaetsheft-kolloquium/slides/components.tex create mode 100644 31-qualitaetsheft-kolloquium/slides/coverage.tex create mode 100644 31-qualitaetsheft-kolloquium/slides/difficulties.tex create mode 100644 "31-qualitaetsheft-kolloquium/slides/einf\303\274hrung.tex" create mode 100644 31-qualitaetsheft-kolloquium/slides/features.tex create mode 100644 31-qualitaetsheft-kolloquium/slides/gantt.tex create mode 100644 31-qualitaetsheft-kolloquium/slides/integrationstrategie.tex create mode 100644 31-qualitaetsheft-kolloquium/slides/pattern.tex create mode 100644 31-qualitaetsheft-kolloquium/slides/performance.tex create mode 100644 31-qualitaetsheft-kolloquium/slides/problems.tex create mode 100644 31-qualitaetsheft-kolloquium/slides/requirements.tex create mode 100644 31-qualitaetsheft-kolloquium/slides/statistics.tex create mode 100644 31-qualitaetsheft-kolloquium/slides/synchronisation.tex create mode 100644 31-qualitaetsheft-kolloquium/slides/testing.tex create mode 100644 31-qualitaetsheft-kolloquium/slides/zielsetzung.tex create mode 100644 31-qualitaetsheft-kolloquium/tikz-uml.sty create mode 100644 40-abschlusspraesentation/.gitignore create mode 100644 40-abschlusspraesentation/.gitlab-ci.yml create mode 100644 40-abschlusspraesentation/LICENSE create mode 100644 40-abschlusspraesentation/Makefile create mode 100644 40-abschlusspraesentation/README.md create mode 100644 40-abschlusspraesentation/assets/.gitignore create mode 100644 40-abschlusspraesentation/assets/LastUpdateComp.png create mode 100644 40-abschlusspraesentation/assets/balken-old.svg create mode 100644 40-abschlusspraesentation/assets/balken-optimized.svg create mode 100644 40-abschlusspraesentation/assets/bootstrap.svg create mode 100644 40-abschlusspraesentation/assets/checkstyle.png create mode 100644 40-abschlusspraesentation/assets/coffee.gif create mode 100644 40-abschlusspraesentation/assets/commits.gnumeric create mode 100644 40-abschlusspraesentation/assets/commits.svg create mode 100644 40-abschlusspraesentation/assets/component6.drawio create mode 100644 40-abschlusspraesentation/assets/component6.svg create mode 100644 40-abschlusspraesentation/assets/diagrams/componentdiagram.puml create mode 100644 40-abschlusspraesentation/assets/diagrams/packageDiagram1.puml create mode 100644 40-abschlusspraesentation/assets/diagrams/packageDiagram2.puml create mode 100644 40-abschlusspraesentation/assets/diagrams/packageDiagram3.puml create mode 100644 40-abschlusspraesentation/assets/diagrams/packageDiagram4.puml create mode 100644 40-abschlusspraesentation/assets/docker.webp create mode 100644 40-abschlusspraesentation/assets/fontawesome/LICENSE.txt create mode 100644 40-abschlusspraesentation/assets/fontawesome/css/all.css create mode 100644 40-abschlusspraesentation/assets/fontawesome/css/all.min.css create mode 100644 40-abschlusspraesentation/assets/fontawesome/css/brands.css create mode 100644 40-abschlusspraesentation/assets/fontawesome/css/brands.min.css create mode 100644 40-abschlusspraesentation/assets/fontawesome/css/fontawesome.css create mode 100644 40-abschlusspraesentation/assets/fontawesome/css/fontawesome.min.css create mode 100644 40-abschlusspraesentation/assets/fontawesome/css/regular.css create mode 100644 40-abschlusspraesentation/assets/fontawesome/css/regular.min.css create mode 100644 40-abschlusspraesentation/assets/fontawesome/css/solid.css create mode 100644 40-abschlusspraesentation/assets/fontawesome/css/solid.min.css create mode 100644 40-abschlusspraesentation/assets/fontawesome/webfonts/fa-brands-400.ttf create mode 100644 40-abschlusspraesentation/assets/fontawesome/webfonts/fa-brands-400.woff2 create mode 100644 40-abschlusspraesentation/assets/fontawesome/webfonts/fa-regular-400.ttf create mode 100644 40-abschlusspraesentation/assets/fontawesome/webfonts/fa-regular-400.woff2 create mode 100644 40-abschlusspraesentation/assets/fontawesome/webfonts/fa-solid-900.ttf create mode 100644 40-abschlusspraesentation/assets/fontawesome/webfonts/fa-solid-900.woff2 create mode 100644 40-abschlusspraesentation/assets/fontawesome/webfonts/fa-v4compatibility.ttf create mode 100644 40-abschlusspraesentation/assets/fontawesome/webfonts/fa-v4compatibility.woff2 create mode 100644 40-abschlusspraesentation/assets/gitlab.svg create mode 100644 40-abschlusspraesentation/assets/jmeter.svg create mode 100644 40-abschlusspraesentation/assets/latex.svg create mode 100644 40-abschlusspraesentation/assets/lets-encrypt.svg create mode 100644 40-abschlusspraesentation/assets/logo.png create mode 100644 40-abschlusspraesentation/assets/logo.svg create mode 100644 40-abschlusspraesentation/assets/mariadb.svg create mode 100644 40-abschlusspraesentation/assets/performance.svg create mode 100644 40-abschlusspraesentation/assets/plantuml.svg create mode 100644 40-abschlusspraesentation/assets/spring.svg create mode 100644 40-abschlusspraesentation/assets/tachodiagramm.svg create mode 100644 40-abschlusspraesentation/assets/ui-components/full (Kopie 1).png create mode 100644 40-abschlusspraesentation/assets/ui-components/full.png create mode 100644 40-abschlusspraesentation/assets/ui-components/layer1.png create mode 100644 40-abschlusspraesentation/assets/ui-components/layer2.png create mode 100644 40-abschlusspraesentation/assets/ui-components/layer3.png create mode 100644 40-abschlusspraesentation/assets/vue.svg create mode 100644 40-abschlusspraesentation/package-lock.json create mode 100644 40-abschlusspraesentation/package.json create mode 100644 40-abschlusspraesentation/presentation.md create mode 100644 40-abschlusspraesentation/script.js create mode 100644 40-abschlusspraesentation/style.css diff --git a/00-pflichtenheft/.gitignore b/00-pflichtenheft/.gitignore new file mode 100644 index 0000000..87ec682 --- /dev/null +++ b/00-pflichtenheft/.gitignore @@ -0,0 +1,302 @@ +## Core latex/pdflatex auxiliary files: +*.aux +*.lof +*.log +*.lot +*.fls +*.out +*.toc +*.fmt +*.fot +*.cb +*.cb2 +.*.lb + +## Intermediate documents: +*.dvi +*.xdv +*-converted-to.* +# these rules might exclude image files for figures etc. +# *.ps +# *.eps +*.pdf +!assets/*.pdf + +## Generated if empty string is given at "Please type another file name for output:" +.pdf + +## Bibliography auxiliary files (bibtex/biblatex/biber): +*.bbl +*.bcf +*.blg +*-blx.aux +*-blx.bib +*.run.xml + +## Build tool auxiliary files: +*.fdb_latexmk +*.synctex +*.synctex(busy) +*.synctex.gz +*.synctex.gz(busy) +*.pdfsync + +## Build tool directories for auxiliary files +# latexrun +latex.out/ + +## Auxiliary and intermediate files from other packages: +# algorithms +*.alg +*.loa + +# achemso +acs-*.bib + +# amsthm +*.thm + +# beamer +*.nav +*.pre +*.snm +*.vrb + +# changes +*.soc + +# comment +*.cut + +# cprotect +*.cpt + +# elsarticle (documentclass of Elsevier journals) +*.spl + +# endnotes +*.ent + +# fixme +*.lox + +# feynmf/feynmp +*.mf +*.mp +*.t[1-9] +*.t[1-9][0-9] +*.tfm + +#(r)(e)ledmac/(r)(e)ledpar +*.end +*.?end +*.[1-9] +*.[1-9][0-9] +*.[1-9][0-9][0-9] +*.[1-9]R +*.[1-9][0-9]R +*.[1-9][0-9][0-9]R +*.eledsec[1-9] +*.eledsec[1-9]R +*.eledsec[1-9][0-9] +*.eledsec[1-9][0-9]R +*.eledsec[1-9][0-9][0-9] +*.eledsec[1-9][0-9][0-9]R + +# glossaries +*.acn +*.acr +*.glg +*.glo +*.gls +*.glsdefs +*.lzo +*.lzs +*.slg +*.slo +*.sls + +# uncomment this for glossaries-extra (will ignore makeindex's style files!) +*.ist + +# gnuplot +*.gnuplot +*.table + +# gnuplottex +*-gnuplottex-* + +# gregoriotex +*.gaux +*.glog +*.gtex + +# htlatex +*.4ct +*.4tc +*.idv +*.lg +*.trc +*.xref + +# hyperref +*.brf + +# knitr +*-concordance.tex +# TODO Uncomment the next line if you use knitr and want to ignore its generated tikz files +# *.tikz +*-tikzDictionary + +# listings +*.lol + +# luatexja-ruby +*.ltjruby + +# makeidx +*.idx +*.ilg +*.ind + +# minitoc +*.maf +*.mlf +*.mlt +*.mtc[0-9]* +*.slf[0-9]* +*.slt[0-9]* +*.stc[0-9]* + +# minted +_minted* +*.pyg + +# morewrites +*.mw + +# newpax +*.newpax + +# nomencl +*.nlg +*.nlo +*.nls + +# pax +*.pax + +# pdfpcnotes +*.pdfpc + +# sagetex +*.sagetex.sage +*.sagetex.py +*.sagetex.scmd + +# scrwfile +*.wrt + +# svg +svg-inkscape/ + +# sympy +*.sout +*.sympy +sympy-plots-for-*.tex/ + +# pdfcomment +*.upa +*.upb + +# pythontex +*.pytxcode +pythontex-files-*/ + +# tcolorbox +*.listing + +# thmtools +*.loe + +# TikZ & PGF +*.dpth +*.md5 +*.auxlock + +# titletoc +*.ptc + +# todonotes +*.tdo + +# vhistory +*.hst +*.ver + +# easy-todo +*.lod + +# xcolor +*.xcp + +# xmpincl +*.xmpi + +# xindy +*.xdy + +# xypic precompiled matrices and outlines +*.xyc +*.xyd + +# endfloat +*.ttt +*.fff + +# Latexian +TSWLatexianTemp* + +## Editors: +# WinEdt +*.bak +*.sav + +# Texpad +.texpadtmp + +# LyX +*.lyx~ + +# Kile +*.backup + +# gummi +.*.swp + +# KBibTeX +*~[0-9]* + +# TeXnicCenter +*.tps + +# auto folder when using emacs and auctex +./auto/* +*.el + +# expex forward references with \gathertags +*-tags.tex + +# standalone packages +*.sta + +# Makeindex log files +*.lpz + +# xwatermark package +*.xwm + +# REVTeX puts footnotes in the bibliography by default, unless the nofootinbib +# option is specified. Footnotes are the stored in a file with suffix Notes.bib. +# Uncomment the next line to have this generated file ignored. +#*Notes.bib diff --git a/00-pflichtenheft/.gitlab-ci.yml b/00-pflichtenheft/.gitlab-ci.yml new file mode 100644 index 0000000..2cf812b --- /dev/null +++ b/00-pflichtenheft/.gitlab-ci.yml @@ -0,0 +1,15 @@ +image: texlive/texlive + +pages: + script: + - mkdir public + - make + - mv pflichtenheft.pdf public + artifacts: + paths: + - public + rules: + - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH + + + diff --git a/00-pflichtenheft/.latexmkrc b/00-pflichtenheft/.latexmkrc new file mode 100644 index 0000000..bec2b38 --- /dev/null +++ b/00-pflichtenheft/.latexmkrc @@ -0,0 +1,7 @@ +# https://tex.stackexchange.com/questions/1226/how-to-make-latexmk-use-makeglossaries +add_cus_dep('glo', 'gls', 0, 'makeglo2gls'); +add_cus_dep('acn', 'acr', 0, 'makeglo2gls'); +sub makeglo2gls { + system("makeglossaries $_[0]"); +} + diff --git a/00-pflichtenheft/Makefile b/00-pflichtenheft/Makefile new file mode 100644 index 0000000..6277cc3 --- /dev/null +++ b/00-pflichtenheft/Makefile @@ -0,0 +1,10 @@ +MAIN = pflichtenheft +FLAGS = -pdf + +all: + latexmk $(FLAGS) $(MAIN) +dev: + latexmk $(FLAGS) -pvc $(MAIN) +clean: + latexmk -C + diff --git a/00-pflichtenheft/assets/KIT_Deckblatt.pdf b/00-pflichtenheft/assets/KIT_Deckblatt.pdf new file mode 100644 index 0000000..7de8ed4 Binary files /dev/null and b/00-pflichtenheft/assets/KIT_Deckblatt.pdf differ diff --git a/00-pflichtenheft/assets/logo.pdf b/00-pflichtenheft/assets/logo.pdf new file mode 100644 index 0000000..91fd334 Binary files /dev/null and b/00-pflichtenheft/assets/logo.pdf differ diff --git a/00-pflichtenheft/assets/logo.svg b/00-pflichtenheft/assets/logo.svg new file mode 100644 index 0000000..1609066 --- /dev/null +++ b/00-pflichtenheft/assets/logo.svg @@ -0,0 +1,211 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Podcast Synchronisation made Efficient + + + + diff --git a/00-pflichtenheft/assets/ui/help-desktop.png b/00-pflichtenheft/assets/ui/help-desktop.png new file mode 100644 index 0000000..b3d1231 Binary files /dev/null and b/00-pflichtenheft/assets/ui/help-desktop.png differ diff --git a/00-pflichtenheft/assets/ui/listening-progess-account-dropdown.png b/00-pflichtenheft/assets/ui/listening-progess-account-dropdown.png new file mode 100644 index 0000000..df69bdc Binary files /dev/null and b/00-pflichtenheft/assets/ui/listening-progess-account-dropdown.png differ diff --git a/00-pflichtenheft/assets/ui/listening-progress-desktop.png b/00-pflichtenheft/assets/ui/listening-progress-desktop.png new file mode 100644 index 0000000..cf69719 Binary files /dev/null and b/00-pflichtenheft/assets/ui/listening-progress-desktop.png differ diff --git a/00-pflichtenheft/assets/ui/listening-progress-mobile.png b/00-pflichtenheft/assets/ui/listening-progress-mobile.png new file mode 100644 index 0000000..4fe4e1f Binary files /dev/null and b/00-pflichtenheft/assets/ui/listening-progress-mobile.png differ diff --git a/00-pflichtenheft/assets/ui/login-desktop.png b/00-pflichtenheft/assets/ui/login-desktop.png new file mode 100644 index 0000000..bb26027 Binary files /dev/null and b/00-pflichtenheft/assets/ui/login-desktop.png differ diff --git a/00-pflichtenheft/assets/ui/login-mobile.png b/00-pflichtenheft/assets/ui/login-mobile.png new file mode 100644 index 0000000..78f619d Binary files /dev/null and b/00-pflichtenheft/assets/ui/login-mobile.png differ diff --git a/00-pflichtenheft/assets/ui/podcasts-desktop-change-language.png b/00-pflichtenheft/assets/ui/podcasts-desktop-change-language.png new file mode 100644 index 0000000..68e2339 Binary files /dev/null and b/00-pflichtenheft/assets/ui/podcasts-desktop-change-language.png differ diff --git a/00-pflichtenheft/assets/ui/podcasts-desktop.png b/00-pflichtenheft/assets/ui/podcasts-desktop.png new file mode 100644 index 0000000..33337a1 Binary files /dev/null and b/00-pflichtenheft/assets/ui/podcasts-desktop.png differ diff --git a/00-pflichtenheft/assets/ui/podcasts-mobile.png b/00-pflichtenheft/assets/ui/podcasts-mobile.png new file mode 100644 index 0000000..d4939f7 Binary files /dev/null and b/00-pflichtenheft/assets/ui/podcasts-mobile.png differ diff --git a/00-pflichtenheft/assets/ui/settings-desktop-1.png b/00-pflichtenheft/assets/ui/settings-desktop-1.png new file mode 100644 index 0000000..ed995fb Binary files /dev/null and b/00-pflichtenheft/assets/ui/settings-desktop-1.png differ diff --git a/00-pflichtenheft/assets/ui/settings-desktop-2.png b/00-pflichtenheft/assets/ui/settings-desktop-2.png new file mode 100644 index 0000000..cdce478 Binary files /dev/null and b/00-pflichtenheft/assets/ui/settings-desktop-2.png differ diff --git a/00-pflichtenheft/assets/ui/settings-mobile-1.png b/00-pflichtenheft/assets/ui/settings-mobile-1.png new file mode 100644 index 0000000..acdf7ca Binary files /dev/null and b/00-pflichtenheft/assets/ui/settings-mobile-1.png differ diff --git a/00-pflichtenheft/assets/ui/settings-mobile-2.png b/00-pflichtenheft/assets/ui/settings-mobile-2.png new file mode 100644 index 0000000..2389d6b Binary files /dev/null and b/00-pflichtenheft/assets/ui/settings-mobile-2.png differ diff --git a/00-pflichtenheft/pflichtenheft.tex b/00-pflichtenheft/pflichtenheft.tex new file mode 100644 index 0000000..cd174f4 --- /dev/null +++ b/00-pflichtenheft/pflichtenheft.tex @@ -0,0 +1,94 @@ +% \documentclass[a4paper, UTF8, 12pt]{article} +% \documentclass[a4paper, UTF8, 12pt]{scrbook} +\documentclass[parskip=half, a4paper, 12pt]{scrartcl} + +\usepackage[german]{babel} +\usepackage[dvipsnames]{xcolor} +\usepackage{tikz} +\usetikzlibrary{positioning} +\usetikzlibrary{calc} +\usetikzlibrary{arrows} +\usetikzlibrary{intersections} +\usepackage{tikz-uml} +\usepackage{pgf-umlsd} +\usepgflibrary{arrows} % for pgf-umlsd +\tikzumlset{fill usecase=white} +\usepackage[margin=2.5cm]{geometry} +\usepackage{csquotes} +\usepackage[T1]{fontenc} +\usepackage{amsmath} +\usepackage{pdflscape} +\usepackage{graphicx} +\usepackage{caption} +\usepackage{subcaption} +\usepackage{float} +\usepackage{enumitem} +\usepackage{textpos} +\usepackage{hyperref} +\usepackage{fancyhdr} +\usepackage{multicol} + +\hypersetup{ +% ‘texdoc hyperref‘ for options + pdftitle={ + PSE\textsuperscript{2} + - Podcast Synchronisation made Efficient Pflichtenheft + }, + bookmarks=true, +} +\usepackage{csquotes} +\usepackage[toc]{glossaries} +\usepackage{lastpage} + +\include{sections/glossar} + +\title{Pflichtenheft} +\author{KIT Students et al} +\date{2.12.2022} + +\pagestyle{fancy} +\setkomafont{pageheadfoot}{\footnotesize\scshape} +\fancyhead{} % clear all header fields +% \fancyhead[L]{Pflichtenheft} +\fancyhead[L]{PSE\textsuperscript{2} - Podcast Synchronisation made Efficient} +% \fancyhead[R]{2.12.2022} +\fancyfoot{} % clear all footer fields +\fancyfoot[R]{\thepage{} / \pageref{LastPage}} +\fancyfoot[L]{Praxis der Softwareentwicklung} +\fancyfoot[C]{} + +\begin{document} + +\include{titlepage} +\setcounter{page}{1} + +\tableofcontents + +\include{sections/einleitung} +\newpage + +\include{sections/anforderungsanalyse} +\newpage + +\include{sections/2-produkteinsatz} +\newpage + +\include{sections/produktuebersicht} +\newpage + +\include{sections/produktfunktionen} +\newpage + +\include{sections/produktdaten} +\newpage + +\include{sections/benutzeroberflaeche} +\newpage + +\include{sections/tests} +\newpage + +\printglossaries +\glsaddall + +\end{document} diff --git a/00-pflichtenheft/pgf-umlsd.sty b/00-pflichtenheft/pgf-umlsd.sty new file mode 100644 index 0000000..99847db --- /dev/null +++ b/00-pflichtenheft/pgf-umlsd.sty @@ -0,0 +1,329 @@ +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% Start of pgf-umlsd.sty +% +% Some macros for UML Sequence Diagrams. +% Home page of project: http://pgf-umlsd.googlecode.com/ +% Author: Xu Yuan , Southeast University, China +% Contributor: Nobel Huang , Southeast University, China +% +% History: +% v0.7 2012/03/05 +% - unify interface of call and callself +% - non-instantaneous message +% - bugfix: conflits with tikz library backgrounds +% v0.6 2011/07/27 +% - Fix Issue 6 reported by frankmorgner@gmail.com +% - diagram without a thread +% - allows empty diagram +% - New manual +% v0.5 2009/09/30 Fix Issue 2 reported by vlado.handziski +% - Nested callself is supported +% - Rename sdloop and sdframe to sdblock +% v0.4 2008/12/08 Fix Issue 1 reported by MathStuf: +% Nested sdloop environment hides outer loop +% v0.3 2008/11/10 in Berlin, fix for the PGF cvs version: +% - the list items in \foreach are not evaluated by default now, +% the `evaluate' opinion should be used +% v0.2 2008/03/20 create project at http://pgf-umlsd.googlecode.com/ +% - use `shadows' library +% Thanks for Dr. Ludger Humbert's feedback! +% - reduce the parameter numbers, the user can write the content +% of instance (such as no colon) +% - the user can redefine the `inststyle' +% - new option: switch underlining of the instance text +% - new option: switch rounded corners +% v0.1 2008/01/25 first release at http://www.fauskes.net/pgftikzexamples/ +% + +\NeedsTeXFormat{LaTeX2e}[1999/12/01] +\ProvidesPackage{pgf-umlsd}[2011/07/27 v0.6 Some LaTeX macros for UML +Sequence Diagrams.] + +\RequirePackage{tikz} +\usetikzlibrary{arrows,shadows} + +\RequirePackage{ifthen} + +% Options +% ? the instance name under line ? +\newif\ifpgfumlsdunderline\pgfumlsdunderlinetrue +\DeclareOption{underline}{\pgfumlsdunderlinetrue} +\DeclareOption{underline=true}{\pgfumlsdunderlinetrue} +\DeclareOption{underline=false}{\pgfumlsdunderlinefalse} +% ? the instance box with rounded corners ? +\newif\ifpgfumlsdroundedcorners\pgfumlsdroundedcornersfalse +\DeclareOption{roundedcorners}{\pgfumlsdroundedcornerstrue} +\DeclareOption{roundedcorners=true}{\pgfumlsdroundedcornerstrue} +\DeclareOption{roundedcorners=false}{\pgfumlsdroundedcornersfalse} +\ProcessOptions + +% new counters +\newcounter{preinst} +\newcounter{instnum} +\newcounter{threadnum} +\newcounter{seqlevel} % level +\newcounter{callevel} +\newcounter{callselflevel} +\newcounter{blocklevel} + +% new an instance +% Example: +% \newinst[edge distance]{var}{name:class} +\newcommand{\newinst}[3][0.2]{ + \stepcounter{instnum} + \path (inst\thepreinst.east)+(#1,0) node[inststyle] (inst\theinstnum) + {\ifpgfumlsdunderline + \underline{#3} + \else + #3 + \fi}; + \path (inst\theinstnum)+(0,-0.5*\unitfactor) node (#2) {}; + \tikzstyle{instcolor#2}=[] + \stepcounter{preinst} +} + +% new an instance thread +% Example: +% \newinst[color]{var}{name}{class} +\newcommand{\newthread}[3][gray!30]{ + \newinst{#2}{#3} + \stepcounter{threadnum} + \node[below of=inst\theinstnum,node distance=0.8cm] (thread\thethreadnum) {}; + \tikzstyle{threadcolor\thethreadnum}=[fill=#1] + \tikzstyle{instcolor#2}=[fill=#1] +} + +% draw running (thick) line, should not call directly +\newcommand*{\drawthread}[2]{ + \begin{pgfonlayer}{umlsd@threadlayer} + \draw[threadstyle] (#1.west) -- (#1.east) -- (#2.east) -- (#2.west) -- cycle; + \end{pgfonlayer} +} + +% a function call +% Example: +% \begin{call}[height]{caller}{function}{callee}{return} +% \end{call} +\newenvironment{call}[5][1]{ +\ifthenelse{\equal{#2}{#4}} +{ + \begin{callself}[#1]{#2}{#3}{#5} +} +{ + \begin{callanother}[#1]{#2}{#3}{#4}{#5} +} +} +{ +\ifthenelse{\equal{\f\thecallevel}{\t\thecallevel}} +{ + \end{callself} +} +{ + \end{callanother} +} +} + +% function call to another instance +% interal use only +\newenvironment*{callanother}[5][1]{ + \stepcounter{seqlevel} + \stepcounter{callevel} % push + \path + (#2)+(0,-\theseqlevel*\unitfactor-0.7*\unitfactor) node (cf\thecallevel) {} + (#4.\threadbias)+(0,-\theseqlevel*\unitfactor-0.7*\unitfactor) node (ct\thecallevel) {}; + + \draw[->,>=triangle 60] ({cf\thecallevel}) -- (ct\thecallevel) + node[midway, above] {#3}; + \def\l\thecallevel{#1} + \def\f\thecallevel{#2} + \def\t\thecallevel{#4} + \def\returnvalue{#5} + \tikzstyle{threadstyle}+=[instcolor#2] +} +{ + \addtocounter{seqlevel}{\l\thecallevel} + \path + (\f\thecallevel)+(0,-\theseqlevel*\unitfactor-0.7*\unitfactor) node (rf\thecallevel) {} + (\t\thecallevel.\threadbias)+(0,-\theseqlevel*\unitfactor-0.7*\unitfactor) node (rt\thecallevel) {}; + \draw[dashed,->,>=angle 60] ({rt\thecallevel}) -- (rf\thecallevel) + node[midway, above]{\returnvalue}; + \drawthread{ct\thecallevel}{rt\thecallevel} + \addtocounter{callevel}{-1} % pop +} + +% a function do not need call others +% interal use only +% Example: +% \begin{callself}[height]{caller}{function}{return} +% \end{callself} +\newenvironment*{callself}[4][1]{ + \stepcounter{seqlevel} + \stepcounter{callevel} % push + \stepcounter{callselflevel} + + \path + (#2)+(\thecallselflevel*0.1-0.1,-\theseqlevel*\unitfactor-0.7*\unitfactor) node (sc\thecallevel) {} + ({sc\thecallevel}.east)+(0,-0.33*\unitfactor) node (scb\thecallevel) {}; + + \draw[->,>=triangle 60] ({sc\thecallevel}.east) -- ++(0.8,0) + node[near start, above right] {#3} -- ++(0,-0.33*\unitfactor) + -- (scb\thecallevel); + \def\l\thecallevel{#1} + \def\f\thecallevel{#2} + \def\t\thecallevel{#2} + \def\returnvalue{#4} + \tikzstyle{threadstyle}+=[instcolor#2] +}{ + \addtocounter{seqlevel}{\l\thecallevel} + \path (\f\thecallevel)+(\thecallselflevel*0.1-0.1,-\theseqlevel*\unitfactor-0.33*\unitfactor) node + (sct\thecallevel) {}; + + \draw[dashed,->,>=angle 60] ({sct\thecallevel}.east) node + (sce\thecallevel) {} -- ++(0.8,0) -- node[midway, right]{\returnvalue} ++(0,-0.33*\unitfactor) -- ++(-0.8,0); + \drawthread{scb\thecallevel}{sce\thecallevel} + \addtocounter{callevel}{-1} % pop + \addtocounter{callselflevel}{-1} +} + +% message between threads +% Example: +% \mess[delay]{sender}{message content}{receiver} +\newcommand{\mess}[4][0]{ + \stepcounter{seqlevel} + \path + (#2)+(0,-\theseqlevel*\unitfactor-0.7*\unitfactor) node (mess from) {}; + \addtocounter{seqlevel}{#1} + \path + (#4)+(0,-\theseqlevel*\unitfactor-0.7*\unitfactor) node (mess to) {}; + \draw[->,>=angle 60] (mess from) -- (mess to) node[midway, above] + {#3}; + + \node (#3 from) at (mess from) {}; + \node (#3 to) at (mess to) {}; +} + +\newenvironment{messcall}[4][1]{ + \stepcounter{seqlevel} + \stepcounter{callevel} % push + \path + (#2)+(0,-\theseqlevel*\unitfactor-0.7*\unitfactor) node (cf\thecallevel) {} + (#4.\threadbias)+(0,-\theseqlevel*\unitfactor-0.7*\unitfactor) node (ct\thecallevel) {}; + + \draw[->,>=angle 60] ({cf\thecallevel}) -- (ct\thecallevel) + node[midway, above] {#3}; + \def\l\thecallevel{#1} + \def\f\thecallevel{#2} + \def\t\thecallevel{#4} + \tikzstyle{threadstyle}+=[instcolor#2] +} +{ + \addtocounter{seqlevel}{\l\thecallevel} + \path + (\f\thecallevel)+(0,-\theseqlevel*\unitfactor-0.7*\unitfactor) node (rf\thecallevel) {} + (\t\thecallevel.\threadbias)+(0,-\theseqlevel*\unitfactor-0.3*\unitfactor) node (rt\thecallevel) {}; + \drawthread{ct\thecallevel}{rt\thecallevel} + \addtocounter{callevel}{-1} % pop +} + +% In the situation of multi-threads, some objects are called at the +% same time. Currently, we have to adjust the bias of thread line +% manually. Possible parameters are: center, west, east +\newcommand{\setthreadbias}[1]{\global\def\threadbias{#1}} + +% This function makes the call earlier. +\newcommand{\prelevel}{\addtocounter{seqlevel}{-1}} + +% This function makes the call later. +\newcommand{\postlevel}{\addtocounter{seqlevel}{+1}} + +% a block box with caption +% \begin{sdblock}[caption background color]{caption}{comments} +% \end{sdblock} +\newenvironment{sdblock}[3][white]{ + \stepcounter{seqlevel} + \stepcounter{blocklevel} % push + \coordinate (blockbeg\theblocklevel) at (0,-\theseqlevel*\unitfactor-\unitfactor); + \stepcounter{seqlevel} + \def\blockcolor\theblocklevel{#1} + \def\blockname\theblocklevel{#2} + \def\blockcomm\theblocklevel{#3} + \begin{pgfinterruptboundingbox} +}{ + \coordinate (blockend) at (0,-\theseqlevel*\unitfactor-2*\unitfactor); + \path (current bounding box.east)+(0.2,0) node (boxeast) {} + (current bounding box.west |- {blockbeg\theblocklevel}) + (-0.2,0) + node (nw) {}; + \path (boxeast |- blockend) node (se) {}; + + % % title + \node[blockstyle] (blocktitle) at (nw) {\blockname\theblocklevel}; + \path (blocktitle.south east) + (0,0.2) node (set) {} + (blocktitle.south east) + (-0.2,0) node (seb) {} + (blocktitle.north east) + (0.2,0) node (comm) {}; + \draw[fill=\blockcolor\theblocklevel] (blocktitle.north west) -- (blocktitle.north east) -- + (set.center) -- (seb.center) -- (blocktitle.south west) -- cycle; + \node[blockstyle] (blocktitle) at (nw) {\blockname\theblocklevel}; + \node[blockcommentstyle] (blockcomment) at (comm) {\blockcomm\theblocklevel}; + + \coordinate (se) at (current bounding box.south east); + \end{pgfinterruptboundingbox} + + \draw (se) rectangle (nw); + + \addtocounter{blocklevel}{-1} % pop + \stepcounter{seqlevel} +} + +% the environment of sequence diagram +\newenvironment{sequencediagram}{ + % declare layers + \pgfdeclarelayer{umlsd@background} + \pgfdeclarelayer{umlsd@threadlayer} + \pgfsetlayers{umlsd@background,umlsd@threadlayer,main} + + \begin{tikzpicture} + \setlength{\unitlength}{1cm} + \tikzstyle{sequence}=[coordinate] + \tikzstyle{inststyle}=[rectangle, draw, anchor=west, minimum + height=0.8cm, minimum width=1.6cm, fill=white, + drop shadow={opacity=1,fill=black}] + \ifpgfumlsdroundedcorners + \tikzstyle{inststyle}+=[rounded corners=3mm] + \fi + \tikzstyle{blockstyle}=[anchor=north west] + \tikzstyle{blockcommentstyle}=[anchor=north west, font=\small] + \tikzstyle{dot}=[inner sep=0pt,fill=black,circle,minimum size=0.2pt] + \global\def\unitfactor{0.6} + \global\def\threadbias{center} + % reset counters + \setcounter{preinst}{0} + \setcounter{instnum}{0} + \setcounter{threadnum}{0} + \setcounter{seqlevel}{0} + \setcounter{callevel}{0} + \setcounter{callselflevel}{0} + \setcounter{blocklevel}{0} + + % origin + \node[coordinate] (inst0) {}; +} +{ + \begin{pgfonlayer}{umlsd@background} + \ifnum\c@instnum > 0 + \foreach \t [evaluate=\t] in {1,...,\theinstnum}{ + \draw[dotted] (inst\t) -- ++(0,-\theseqlevel*\unitfactor-2.2*\unitfactor); + } + \fi + \ifnum\c@threadnum > 0 + \foreach \t [evaluate=\t] in {1,...,\thethreadnum}{ + \path (thread\t)+(0,-\theseqlevel*\unitfactor-0.1*\unitfactor) node (threadend) {}; + \tikzstyle{threadstyle}+=[threadcolor\t] + \drawthread{thread\t}{threadend} + } + \fi + \end{pgfonlayer} +\end{tikzpicture}} + + +%%% End of pgf-umlsd.sty +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \ No newline at end of file diff --git a/00-pflichtenheft/sections/2-produkteinsatz.tex b/00-pflichtenheft/sections/2-produkteinsatz.tex new file mode 100644 index 0000000..2d98e82 --- /dev/null +++ b/00-pflichtenheft/sections/2-produkteinsatz.tex @@ -0,0 +1,73 @@ +\section{Produkteinsatz} + +Im Folgenden werden Anwendungsbereiche, Zielgruppen und Betriebsbedingungen des +Produkts geschildert. + +\subsection{Anwendungsbereiche} + +Der \Gls{podcast}-Synchronisationsserver dient dazu, +Betreibern von \Glspl{podcatcher} einen performanteren und schlankeren +Synchronisationsdienst zur Verfügung zu stellen, als aktuell erhältliche Synchronisationsserver. +Die Nutzer erhalten außerdem eine einfach zu bedienende und übersichtliche Möglichkeit, +ihre abonnierten \Glspl{podcast} über mehrere Geräte hinweg synchronisieren zu können. + +Damit das Produkt seinen Ansprüchen in Sachen Performanz und Nutzerfreundlichkeit +gerecht werden kann, wird ausdrücklich auf Features, die nicht zur Kernaufgabe gehören, +was die Synchronisation von \Glspl{podcast} ist, verzichtet. +Dazu gehören zum Beispiel Funktionalitäten, wie das Suchen von \Glspl{podcast} oder das +Anhören von \Glspl{episode}, welche nicht bereitgestellt werden. +Außerdem werden keine extra Konfigurationsmöglichkeiten für die Synchronisation angeboten. + +Es wird eine Schnittstelle in Form einer graphischen Weboberfläche +bereitgestellt, mithilfe welcher der Nutzer seinen Account verwalten und +synchronisierte \Glspl{podcast} einsehen kann. +Für die Synchronisation wird der Account mit einer oder mehreren \Gls{podcatcher}-Applikationen +verknüpft. +Dadurch werden lokal gespeicherte \Glspl{abo} und Abhörfortschritte auf den +Account und somit auf alle verknüpften \Gls{podcatcher} übertragen. + + +\subsection{Zielgruppen} +Zur Zielgruppe gehören Betreiber von \Gls{podcatcher}-Applikationen und deren Nutzer. +Durch den Synchronisationsserver können die Betreiber ihren Nutzern eine +schlanke und einfach bedienbare Möglichkeit zum Synchronisieren ihrer abonnierten +\Glspl{podcast} bieten. + +\subsection{Betriebsbedingungen} +Es müssen folgende Voraussetzungen auf Server- und Nutzerseite erfüllt sein. + +%\newcommand{\tabitem}{\vspace{.1cm}~~\llap{\textbullet}~~} +%\begin{tabular}{l l} +% Server: & Nutzer: \\ +% % \hline +% \tabitem Internetanbindung & +% \tabitem Internetanbindung \\ +% \tabitem Speicherplatz: mind. 12GB & +% \tabitem Aktueller Webbrowser \\ +% \tabitem Arbeitsspeicher: mind. 2GB & +% \tabitem \Gls{podcatcher} mit \Gls{gpodder} Unterstützung \\ +% \tabitem CPU: mind. 2 Kerne & \\ +% \tabitem JVM-Fähig (\Gls{java} 17) & \\ +% \tabitem MariaDB-Fähig (MariaDB Server 10.6) & \\ +%\end{tabular} + +\begin{multicols}{2} +Server: +\begin{itemize} + \item Speicherplatz: mind. 12GB + \item Arbeitsspeicher: mind. 2GB + \item CPU: mind. 2 Kerne + \item JVM-Fähig (\Gls{java} 17) + \item MariaDB-Fähig \\ (MariaDB Server 10.6) +\end{itemize} +\columnbreak +Nutzer: +\begin{itemize} + \item Internetanbindung + \item Aktueller Webbrowser + \item \Gls{podcatcher} mit \Gls{gpodder} Unterstützung + \item[] + \item[] +\end{itemize} +\end{multicols} + diff --git a/00-pflichtenheft/sections/TikzPictures/ActivityLogin.tex b/00-pflichtenheft/sections/TikzPictures/ActivityLogin.tex new file mode 100644 index 0000000..701eeef --- /dev/null +++ b/00-pflichtenheft/sections/TikzPictures/ActivityLogin.tex @@ -0,0 +1,56 @@ +\begin{tikzpicture} + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + % Nodeklassen für Aktivitätsdiagramm festlegen: % + %-------------------------------------------------------------------------------% + % start - Schwarzer ausgefüllter Kreis % + % activity - Abgerundetes Rechteck für Aktivitäten des Users % + % actBox - Rechteck für Reaktionen des Systems % + % decision - Karokästchen für Entscheidungen / Abzweigungen % + % end - Zielscheibe für das Ende der Aktivität % + %-------------------------------------------------------------------------------% + % Siehe: https://www-kseta.ttp.kit.edu/fellows/Tanja.Harbaum/tikz_tutorial.pdf % + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + \tikzset{start/.style={circle,minimum width=0.3cm,minimum height=0.3cm,draw,fill}} + \tikzset{activity/.style={rectangle,minimum width=1cm,minimum height=0.5cm,rounded corners=5pt,draw,align=center}} + \tikzset{actBox/.style={rectangle,minimum width=1cm,minimum height=0.5cm,draw,align=center}} + \tikzset{decision/.style={diamond,minimum width=1cm,minimum height=1cm,draw,align=center}} + \tikzset{end/.style={draw,double=white,circle,inner sep=1pt,minimum width=0.3cm,minimum height=0.3cm,draw,fill}} + + % Nodes und deren Position (voneinander abhängig) angeben + \node[start] (Start) {}; + \node[activity, right = of Start] (Aufruf) {Website aufrufen}; + \node[actBox, right = of Aufruf] (Login) {Login page}; + \node[decision, right = of Login] (Remembered) {Gemerkt?}; + \node[decision, below left = of Remembered] (RemEnd1) {}; + \node[activity, below = of RemEnd1] (Input) {E-Mail und\\ Passwort eingeben}; + \node[activity, below = of Input] (RememberOpt) {Option \string"Passwort merken\string"\\ aus-/abwählen}; + \node[activity, below = of RememberOpt] (LoginPress) {\string"Anmelden\string"-Button\\ drücken}; + \node[actBox, left = of LoginPress] (ErrorBox) {Fehlermeldung}; + \node[decision, below = of LoginPress] (CorrectEntry) {Korrekte\\ Eingabe?}; + \node[actBox, right = of CorrectEntry] (DashboardShow) {Dashboard\\ der Website}; + \path[name path=P1] (Remembered.south east) -| (DashboardShow); + \path[name path=P2] (RemEnd1.north east) -| (DashboardShow); + \node[end, below = of DashboardShow] (End) {}; + \coordinate (jump1) at (DashboardShow |- RemEnd1){}; + \draw($(Start.north west)+(-1,2)$) rectangle ($(End.south east)+(2,-1)$); + \path ($(Start.north west)+(-1,1.3)$) -- ($(Start -| End)+(2,1.3)$) node[midway]{\Large \textbf{Anmeldung}}; + + % Verbindungen zwischen den Nodes + + \draw[-stealth, thick](Start) -- (Aufruf); + \draw[-stealth, thick](Aufruf) -- (Login); + \draw[-stealth, thick](Login) -- (Remembered); + \draw[-stealth, thick](Remembered) -- node[left = 2mm, very near start]{nein} (RemEnd1); + \draw[-stealth, thick](Remembered.south east) -- (jump1) node[right = 2mm, near start]{ja} -- (DashboardShow); + \draw[-stealth, thick](RemEnd1) -- (Input); + \draw[-stealth, thick](Input) -- (RememberOpt); + \draw[-stealth, thick](RememberOpt) -- (LoginPress); + \draw[-stealth, thick](LoginPress) -- (CorrectEntry); + \draw[-stealth, thick](CorrectEntry) -| node[above, very near start]{falsch} (ErrorBox); + \draw[-stealth, thick](ErrorBox) |- (RemEnd1); + \draw[-stealth, thick](CorrectEntry) -- node[above, midway]{wahr} (DashboardShow); + \draw[-stealth, thick](DashboardShow) -- (End); + +\end{tikzpicture} \ No newline at end of file diff --git a/00-pflichtenheft/sections/TikzPictures/ActivityRegister.tex b/00-pflichtenheft/sections/TikzPictures/ActivityRegister.tex new file mode 100644 index 0000000..b50bafe --- /dev/null +++ b/00-pflichtenheft/sections/TikzPictures/ActivityRegister.tex @@ -0,0 +1,60 @@ +\begin{tikzpicture} + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + % Nodeklassen für Aktivitätsdiagramm festlegen: % + %-------------------------------------------------------------------------------% + % start - Schwarzer ausgefüllter Kreis % + % activity - Abgerundetes Rechteck für Aktivitäten des Users % + % actBox - Rechteck für Reaktionen des Systems % + % decision - Karokästchen für Entscheidungen / Abzweigungen % + % end - Zielscheibe für das Ende der Aktivität % + %-------------------------------------------------------------------------------% + % Siehe: https://www-kseta.ttp.kit.edu/fellows/Tanja.Harbaum/tikz_tutorial.pdf % + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + \tikzset{start/.style={circle,minimum width=0.3cm,minimum height=0.3cm,draw,fill}} + \tikzset{activity/.style={rectangle,minimum width=1cm,minimum height=0.5cm,rounded corners=5pt,draw,align=center}} + \tikzset{actBox/.style={rectangle,minimum width=1cm,minimum height=0.5cm,draw,align=center}} + \tikzset{decision/.style={diamond,minimum width=1cm,minimum height=1cm,draw,align=center}} + \tikzset{end/.style={draw,double=white,circle,inner sep=1pt,minimum width=0.3cm,minimum height=0.3cm,draw,fill}} + + % Nodes und deren Position (voneinander abhängig) angeben + \node[start] (Start) {}; + \node[activity, right = of Start] (Aufruf) {Website aufrufen}; + \node[actBox, right = of Aufruf] (Login1) {Login page}; + \node[activity, below = of Login1] (RegOpt) {\string"Registrieren\string"-Option\\ auswählen}; + \node[actBox, below = of RegOpt] (Registrieren) {Registrierungsfenster}; + \node[decision, below = of Registrieren] (EmailTakenEnd) {}; + \node[activity, below = of EmailTakenEnd] (Input) {E-Mail-Adresse und\\ zweimal neues\\ Passwort eingeben}; + \node[activity, below = of Input] (RegPress) {\string"Registrieren\string"-Button\\ drücken}; + \node[decision, left = of RegPress] (EmailTaken) {E-Mail\\ vergeben?}; + \node[actBox] (Error1) at (EmailTaken |- EmailTakenEnd) {Fehlermeldung}; + \node[actBox, below = of EmailTaken] (Confirm) {E-Mail mit Link\\ zur Verifizierung}; + \node[activity, below = of Confirm] (LinkClick) {Link\\ anklicken}; + \node[decision, right = of LinkClick] (LinkTime) {Link älter\\ als 24h?}; + \node[actBox, right = 2cm of LinkTime] (Error2) {Fehler}; + \node[actBox, below = 1.5cm of LinkTime] (Login2) {Login Page}; + \node[end] (End) at (Error2 |- Login2) {}; + \draw ($(Start.north west)+(-2,2)$) rectangle ($(End.south east)+(2,-1)$); + \path ($(Start.north west)+(-2,1.4)$) -- ($(Start -| Error2)+(2,1.4)$) node[midway]{\Large \textbf{Registrieren}}; + + % Verbindungen zwischen den Nodes + \draw[-stealth, thick](Start) -- (Aufruf); + \draw[-stealth, thick](Aufruf) -- (Login1); + \draw[-stealth, thick](Login1) -- (RegOpt); + \draw[-stealth, thick](RegOpt) -- (Registrieren); + \draw[-stealth, thick](Registrieren) -- (EmailTakenEnd); + \draw[-stealth, thick](EmailTakenEnd) -- (Input); + \draw[-stealth, thick](Input) -- (RegPress); + \draw[-stealth, thick](RegPress) -- (EmailTaken); + \draw[-stealth, thick](EmailTaken) -- node[left, near start]{ja} (Error1); + \draw[-stealth, thick](Error1) -- (EmailTakenEnd); + \draw[-stealth, thick](EmailTaken) -- node[left, near start]{nein} (Confirm); + \draw[-stealth, thick](Confirm) -- (LinkClick); + \draw[-stealth, thick](LinkClick) -- (LinkTime); + \draw[-stealth, thick](LinkTime) -- node[above, near start]{ja} (Error2); + \draw[-stealth, thick](LinkTime) -- node[left, near start]{nein} (Login2); + \draw[-stealth, thick](Error2) -- (End); + \draw[-stealth, thick](Login2) -- (End); + +\end{tikzpicture} \ No newline at end of file diff --git a/00-pflichtenheft/sections/TikzPictures/ActivityResetPass.tex b/00-pflichtenheft/sections/TikzPictures/ActivityResetPass.tex new file mode 100644 index 0000000..28a065c --- /dev/null +++ b/00-pflichtenheft/sections/TikzPictures/ActivityResetPass.tex @@ -0,0 +1,70 @@ +\begin{tikzpicture} + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + % Nodeklassen für Aktivitätsdiagramm festlegen: % + %-------------------------------------------------------------------------------% + % start - Schwarzer ausgefüllter Kreis % + % activity - Abgerundetes Rechteck für Aktivitäten des Users % + % actBox - Rechteck für Reaktionen des Systems % + % decision - Karokästchen für Entscheidungen / Abzweigungen % + % end - Zielscheibe für das Ende der Aktivität % + %-------------------------------------------------------------------------------% + % Siehe: https://www-kseta.ttp.kit.edu/fellows/Tanja.Harbaum/tikz_tutorial.pdf % + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + \tikzset{start/.style={circle,minimum width=0.3cm,minimum height=0.3cm,draw,fill}} + \tikzset{activity/.style={rectangle,minimum width=1cm,minimum height=0.5cm,rounded corners=5pt,draw,align=center}} + \tikzset{actBox/.style={rectangle,minimum width=1cm,minimum height=0.5cm,draw,align=center}} + \tikzset{decision/.style={diamond,minimum width=1cm,minimum height=1cm,draw,align=center}} + \tikzset{end/.style={draw,double=white,circle,inner sep=1pt,minimum width=0.3cm,minimum height=0.3cm,draw,fill}} + + % Nodes und deren Position (voneinander abhängig) angeben + \node[start] (Start) {}; + \node[activity, right = of Start] (Aufruf) {Website aufrufen}; + \node[actBox, right = of Aufruf] (Login1) {Login page}; + \node[activity, below = of Login1] (ForgetOpt) {\string"Passwort vergessen\string"\\ Option auswählen}; + \node[actBox, below = of ForgetOpt] (Forget) {\string"Passwort vergessen\string"\\ Fenster}; + \node[decision, below = of Forget] (EmailDecEnd) {}; + \node[activity, below = of EmailDecEnd] (Input1) {E-Mail-Adresse\\ eingeben}; + \node[decision, left = of Input1] (EmailDec) {E-Mail\\ registriert?}; + \path (EmailDec) |- (EmailDecEnd) node[actBox, near end](Error2){Fehler}; + \node[actBox, below = of EmailDec] (SendMail) {E-Mail mit Link\\ zum Zurücksetzen\\ des Passworts}; + \node[activity, below = of SendMail] (LinkClick) {Empfangenen Link\\ öffnen}; + \node[decision, right = of LinkClick] (LinkTime) {Link älter\\ als 24h?}; + \node[actBox, above = of LinkTime] (Error) {Fehler}; + \node[end, right = of Error] (End1) {}; + \node[actBox, below = of LinkTime] (ResetWin) {\string"Passwort zurücksetzen\string"\\ Fenster}; + \node[decision, left = of ResetWin] (PswCorEnd) {}; + \node[activity, left = of PswCorEnd] (Input2) {neues Passwort\\ zweimal eingeben}; + \node[activity, below = 2cm of Input2] (ResetPress) {\string"Passwort zurücksetzen\string"-\\ Button drücken}; + \node[decision, right = of ResetPress] (PswCorrect) {Passwort\\ legal?}; + \node[actBox, right = of PswCorrect] (Login2) {Login page}; + \node[end, right = of Login2] (End2) {}; + \draw ($(Start.north west)+(-3,2)$) rectangle ($(End2.south east)+(1,-2)$); + \path ($(Start.north west)+(-3,1.4)$) -- ($(Start -| End2)+(1,1.4)$) node[midway]{\Large \textbf{Passwort vergessen}}; + + % Verbindungen zwischen den Nodes + \draw[-stealth, thick](Start) -- (Aufruf); + \draw[-stealth, thick](Aufruf) -- (Login1); + \draw[-stealth, thick](Login1) -- (ForgetOpt); + \draw[-stealth, thick](ForgetOpt) -- (Forget); + \draw[-stealth, thick](Forget) -- (EmailDecEnd); + \draw[-stealth, thick](EmailDecEnd) -- (Input1); + \draw[-stealth, thick](Input1) -- (EmailDec); + \draw[-stealth, thick](EmailDec) |- node[left, near start]{nein} (Error2); + \draw[-stealth, thick](Error2) -- (EmailDecEnd); + \draw[-stealth, thick](EmailDec) -- node[left, near start]{ja} (SendMail); + \draw[-stealth, thick](SendMail) -- (LinkClick); + \draw[-stealth, thick](LinkClick) -- (LinkTime); + \draw[-stealth, thick](LinkTime) -- node[left, near start]{ja} (Error); + \draw[-stealth, thick](Error) -- (End1); + \draw[-stealth, thick](LinkTime) -- node[left, near start]{nein} (ResetWin); + \draw[-stealth, thick](ResetWin) -- (PswCorEnd); + \draw[-stealth, thick](PswCorEnd) -- (Input2); + \draw[-stealth, thick](Input2) -- (ResetPress); + \draw[-stealth, thick](ResetPress) -- (PswCorrect); + \draw[-stealth, thick](PswCorrect.north west) -| (PswCorEnd) node[above, near start]{nein}; + \draw[-stealth, thick](PswCorrect) -- (Login2) node[above, near start]{ja}; + \draw[-stealth, thick](Login2) -- (End2); + +\end{tikzpicture} \ No newline at end of file diff --git a/00-pflichtenheft/sections/TikzPictures/SequenceSynchroniseSubscription.tex b/00-pflichtenheft/sections/TikzPictures/SequenceSynchroniseSubscription.tex new file mode 100644 index 0000000..3e68555 --- /dev/null +++ b/00-pflichtenheft/sections/TikzPictures/SequenceSynchroniseSubscription.tex @@ -0,0 +1,79 @@ +%\begin{tikzpicture} +% +% \begin{umlseqdiag} +% +% \umlactor[no ddots]{Benutzer} +% \umlobject[no ddots]{Podcatcher1} +% \umlobject[no ddots]{Podcatcher2} +% \umlobject[no ddots]{Webfrontend} +% \umlobject[no ddots]{Server} +% +% \begin{umlcall}[dt=5, op=abonniere Podcast]{Benutzer}{Podcatcher1} +% \begin{umlcallself}[dt=8, op=füge Podcast zu Abos hinzu, type=synchron]{Podcatcher1} +% \end{umlcallself} +% \begin{umlcall}[op=lade Änderungen hoch]{Podcatcher1}{Server} +% \begin{umlcallself}[dt=8, op=speichere Änderungen]{Server} +% \end{umlcallself} +% \end{umlcall} +% \end{umlcall} +% +% \begin{umlcall}[dt=10, op=Abos anzeigen]{Benutzer}{Webfrontend} +% \begin{umlcall}[dt=5, op=hole Aboliste, return=Aboliste]{Webfrontend}{Server} +% \end{umlcall} +% \end{umlcall} +% +% \begin{umlcall}[dt=10, op=Abos anzeigen]{Benutzer}{Podcatcher2} +% \begin{umlcall}[dt=5, op=hole Aboliste, return=Aboliste]{Podcatcher2}{Server} +% \end{umlcall} +% \end{umlcall} +% +% \end{umlseqdiag} +% +%\end{tikzpicture} + +\begin{sequencediagram} + + \newthread{b}{Benutzer} + \newinst[2]{p1}{Podcatcher1} + \newinst{p2}{Podcatcher2} + \newinst[1]{w}{Webfrontend} + \newinst[1]{s}{Server} + + \begin{call}{b}{abonniere Podcast}{p1}{} + \begin{callself}{p1}{füge Podcasts zu Abos hinzu}{} + \begin{call}{p1}{lade Änderungen hoch}{s}{} + \begin{callself}{s}{\shortstack{speichere\\Änderungen\\persistent}}{} + \end{callself} + \end{call} + \end{callself} + \end{call} + + \begin{call}{b}{Abos anzeigen}{w}{} + \begin{call}{w}{hole Aboliste}{s}{Aboliste} + \end{call} + \end{call} + + \begin{call}{b}{Abos anzeigen}{p2}{} + \begin{call}{p2}{hole Aboliste}{s}{Aboliste} + \end{call} + \end{call} + +\end{sequencediagram} + + + + + + + + + + + + + + + + + + diff --git a/00-pflichtenheft/sections/TikzPictures/UseCaseUML.tex b/00-pflichtenheft/sections/TikzPictures/UseCaseUML.tex new file mode 100644 index 0000000..1bb2e2c --- /dev/null +++ b/00-pflichtenheft/sections/TikzPictures/UseCaseUML.tex @@ -0,0 +1,46 @@ +\begin{tikzpicture}%[trim left = 1cm] + + \begin{umlsystem}[x=5] {<> Podcast Sync Web} + \umlusecase[name=a,width=2.5cm] {Registrieren} + \umlusecase[name=c,y=-4,width=2.5cm] {Aktion ausführen} + \umlusecase[name=d,y=-6,width=2.5cm] {Anmelden} + \umlusecase[name=f,y=-2,width=2.5cm] {Sprache ändern (*)} + \umlusecase[name=g,x=6,y=-1,width=2.5cm] {Anleitung einsehen} + \umlusecase[name=h,x=6,y=-3,width=2.5cm] {Abonnierte Podcasts einsehen} + \umlusecase[name=i,x=6,y=-5,width=2.5cm] {Profil verwalten} + \umlusecase[name=r,x=6,y=-7.25,width=2.5cm] {Zuletzt angehörte\\ Episoden einsehen} + \umlusecase[name=j,x=6,y=-10.5,width=2.5cm] {Mit Google / anderen Anbietern anmelden (*)} + \umlusecase[name=k,x=4,y=-13,width=2.5cm] {Passwort vergessen} + \umlusecase[name=l,y=-15,width=2.5cm] {Admin Aktion ausführen (*)} + \umlusecase[name=m,x=8,y=-15,width=2.5cm] {Statistiken einsehen (*)} + \umlusecase[name=n,x=12,y=-4,width=2.5cm] {Passwort ändern} + \umlusecase[name=o,x=12,y=-2,width=2.5cm] {Gpodder verknüpfen (*)} + \umlusecase[name=p,x=12,y=-6,width=2.5cm] {Personenbezogene Daten abrufen (*)} + \umlusecase[name=q,x=12,y=-8,width=2.5cm] {Account löschen} + \end{umlsystem} + + \umlactor[y=-3] {User} + \umlactor[y=-15] {Admin} + + \umlinherit{Admin}{User} + + \umlassoc{User}{a} + \umlassoc{User}{c} + \umlassoc{User}{d} + \umlassoc{User}{f} + \umlassoc{Admin}{l} + + \umlextend{g}{c} + \umlextend{h}{c} + \umlextend{i}{c} + \umlextend{r}{c} + \umlextend{j}{d} + \umlextend{k}{d} + \umlextend{m}{l} + \umlinclude{c}{d} + \umlinclude{l}{d} + \umlextend{n}{i} + \umlextend{o}{i} + \umlextend{p}{i} + \umlextend{q}{i} +\end{tikzpicture} \ No newline at end of file diff --git a/00-pflichtenheft/sections/anforderungsanalyse.tex b/00-pflichtenheft/sections/anforderungsanalyse.tex new file mode 100644 index 0000000..51be6c3 --- /dev/null +++ b/00-pflichtenheft/sections/anforderungsanalyse.tex @@ -0,0 +1,219 @@ +\newcommand{\newrequirementlist}[1]{ + % https://www.overleaf.com/learn/latex/Lists#Creating_a_new_list_with_enumitem + \newlist{#1list}{enumerate}{1} + \setlist[#1list, 1] + { + before=\leavevmode, + label=\upshape\textlangle #1\arabic*\textrangle, + ref=\upshape\textlangle #1\arabic*\textrangle, + resume=#1list + } + } + +\newrequirementlist{RM} +\newrequirementlist{RS} +\newrequirementlist{RC} +\newrequirementlist{RW} + +\section{Anforderungsanalyse} + +\subsection{ Musskriterien } + +Musskriterien: unabdingbare Leistungen der Software. + +\subsubsection*{ Funktionale Anforderungen } + +\begin{RMlist} + \item\label{r:login} Der Benutzer kann sich im Webfrontend mit einer + E-Mail-Adresse und einem Pass\-wort erstmalig registrieren und bei + abgeschlossener Registrierung fortan mit denselben Informationen + anmelden. + Für eine erfolgreiche Registrierung muss die angegebene + E-Mail-Adresse zuerst verifiziert werden.\\ + \emph{Tests: \ref{t:register}, \ref{t:login}} + \item\label{r:store} Die Liste der \Glspl{abo} eines Benutzers sowie + der zeitliche Fortschritt beim Anhören(/Ansehen) von jeder begonnen + \Gls{episode} werden für jeden Benutzer gespeichert. + \item\label{r:sync} Die Liste der \Glspl{abo} eines Benutzers + sowie der zeitliche Fortschritt beim Anhören(/Ansehen) von jeder + begonnenen \Gls{episode} wird automatisch über alle von einem Benutzer + verknüpften \Gls{podcatcher}n aktualisiert. + Dabei sind der zu synchronisierende Stand der \Glspl{abo} und der + zeitliche Fortschritt jeweils definiert als derjenige Stand, der + zeitlich am kürzesten zurückliegt.\\ + \emph{Tests: \ref{t:sync-sub}, \ref{t:sync-unsub}, \ref{t:sync-episode}} + \item\label{r:ui} Das Webfrontend bietet dem Benutzer eine graphische + Benutzeroberfläche zur Navigation und zur Ansteuerung einzelner + Funktionalitäten.\\ + \emph{Implizit durch alle Testfälle geprüft.} + \item\label{r:reset-pw} Der Benutzer kann sein Passwort ändern und es + gibt eine ,,Passwort vergessen''-Funktion. + So kann ein angemeldeter Benutzer sein Passwort direkt im Webfrontend + ändern. + Im nicht angemeldeten Zustand kann der Benutzer sein Passwort über + die registrierte E-Mail-Adresse zurücksetzen.\\ + \emph{Tests: \ref{t:change-pw}, \ref{t:forgot-pw}} + \item\label{r:show-podcasts} Das Webfrontend bietet dem Benutzer die + Möglichkeit, sich die Liste seiner \Glspl{abo} anzeigen zu + lassen. + Die dabei dargestellten Informationen beinhalten den Namen des + \Glspl{abo} und eine gerundete Angabe darüber, wie lange es her ist, + dass der Benutzer das letzte Mal eine \Gls{episode} dieses \Glspl{abo} + konsumiert hat.\\ + \emph{Tests: \ref{t:sync-sub}, \ref{t:sync-unsub}} +\end{RMlist} + +\subsubsection*{ Nicht-funktionale Anforderungen } + +\begin{RMlist} + \item\label{r:requests} Der Synchronisations-Server kann mindestens + 50 Anfragen pro Sekunde verarbeiten.\\ + \emph{Test: \ref{t:lasttest}} + \item\label{r:desktop-first} Das Webfrontend ist primär für + Desktop-Benutzer ausgelegt. + \item\label{r:gpodder} Der Datenaustausch erfolgt über die Gpodder + \Gls{rest-api} unter Verwendung des Datenformats \Gls{json}. + \item\label{r:persistent-storage} Die Speicherung der Daten eines + Benutzers \ref{r:store} über den Synchronisations-Server erfolgt + persistent in einer \Gls{db}. Diese Daten des Benutzers sind die + Liste seiner \Glspl{abo} und der zeitliche Fortschritt beim Anhören + (/Ansehen) aller begonnenen \Glspl{episode}. + \item\label{r:api-extension} Die \Gls{gpodder} wird um Funktionalitäten + zur verbesserten Kommunikation zwischen Front- und Backend erweitert + (siehe \ref{r:login}).\\ +\end{RMlist} + + +\subsection{ Sollkriterien } + +Sollkriterien: erstrebenswerte Leistungen. + +\subsubsection*{ Funktionale Anforderungen } + +\begin{RSlist} + \item\label{r:man} Das Webfrontend bietet die Möglichkeit eine nicht + ausgefüllte Benutzeranleitung für das Synchronisieren von \Glspl{podcast} + anzuzeigen (Abbildung \ref{fig:help-desktop}).\\ + \emph{Test: \ref{t:man}} + \item\label{r:delete-acc} Der Benutzer kann seinen Account löschen. + Daraufhin werden alle auf diesen Benutzer bezogenen Daten gelöscht.\\ + \emph{Test: \ref{t:delete-acc}} +\end{RSlist} + +\subsubsection*{ Nicht-funktionale Anforderungen } + +\begin{RSlist} +\item\label{r:backend-libs} Das Backend wird in \Gls{java} unter Verwendung des + quelloffenen Frameworks Spring implementiert. Weiter wird für die + \Gls{db} das relationale Open-Source Datenbankverwaltungssystem MariaDB + eingesetzt. + \item\label{r:ui-libs} Die Weboberfläche wird mithilfe der + \Gls{ui-lib} React oder des Webframeworks Vue in JavaScript und + mit dem Frontend-CSS-Framework Bootstrap entwickelt. + \item\label{r:ui-source} + Verwendete \Glspl{ui-lib} werden von einem \Gls{packagemanager}, wie dem Node + Package Manager (npm) bezogen. Ein \Gls{bundler}, wie vite oder Webpack, + stellt ein minimiertes Skript von den Teilen der Bibliotheken zusammen, + die vom Code verwendet werden. Das minimierte Skript wird dann auf einem + eigenen Server für die Weboberfläche bereitgestellt. + \item\label{r:spa} Die Weboberfläche wird als \Gls{spa} + entworfen und aktualisiert dynamisch ihren Inhalt, sobald es eine + Antwort auf eine Anfrage an die \Gls{gpodder} \ref{r:api-compat} erhält. + \item\label{r:parse-metadata} Das Backend kann die Metadaten von + \Gls{podcast} aus + deren \Gls{rss}-Feeds (XML-Dateien) für die Anzeige im Webfrontend + \ref{r:show-podcasts} parsen. + \item\label{r:pw-requirements} Verwendete Passwörter müssen mindestens 8 Zeichen, + ein Sonderzeichen, eine Zahl sowie einen Klein- und einen Großbuchstaben + enthalten. + Diese Anforderungen gelten also insbesondere für über die Funktionen + \ref{r:login} und \ref{r:reset-pw} neu gewählte Passwörter.\\ + \emph{Test: \ref{t:pw-req}} + \item\label{r:save-pw} Passwörter werden sicher mittels \Gls{salt-hash} + gespeichert. + \item\label{r:session} Im Webfrontend angemeldete Benutzer bleiben dort + angemeldet. Hierfür wird ein \Gls{session-token} in einem \Gls{cookie} gespeichert.\\ +\end{RSlist} + +\subsection{ Kannkriterien } + +Kannkriterien: Leistungen, die enthalten sein können. + +\subsubsection*{ Funktionale Anforderungen } + +\begin{RClist} + \item\label{r:subscribe} Ein Benutzer kann über die Weboberfläche einen + abonnierten \Gls{podcast} über einen generierten Link teilen. + Öffnet nun ein anderer Nutzer den Link wird dieser zur Weboberfläche + weitergeleitet und mit einem Pop-up gefragt, ob dieser den + \Gls{podcast} abonnieren + möchte, falls noch nicht geschehen. + Akzeptiert der Nutzer, so wird der \Gls{podcast} zur Liste der + \Glspl{abo} des + Nutzers hinzugefügt. + Gegebenenfalls muss sich der Benutzer dafür zuerst anmelden. + Der Link setzt sich dabei unter anderem aus der URL des Webfrontends oder + einem \Gls{pseudoprotocol} und dem \Gls{podcast}-Link des Providers zusammen. + \item\label{r:unsubscribe} Das Webfrontend bietet dem Benutzer die Möglichkeit, + \Glspl{abo} zu entfernen beziehungsweise \Glspl{podcast} zu deabonnieren. + \item\label{r:import} Das Importieren und Exportieren aller benutzerbezogenen + Daten wird unterstützt (siehe \ref{r:dsgvo}). + \item\label{r:import-other} Das Umsiedeln von anderen Gpodder-Plattformen und + damit insbesondere der damit verbundene Datenimport wird unterstützt. + \item\label{r:api-compat} Die Weboberfläche ist kompatibel mit beliebigen + \Glspl{gpodder}. + \item\label{r:responsive} Die Weboberfläche ist \gls{responsive}. + \item\label{r:admin} Es gibt Administrator Benutzerkonten. Eine angestrebte + Funktionalität dieser privilegierten Konten ist das Einsehen von Statistiken, + wie der Anzahl von Benutzern, und dem Abruf der Metadaten eines + \Glspl{abo}. +\end{RClist} + +\subsubsection*{ Nicht-funktionale Anforderungen } + +\begin{RClist} + \item\label{r:login-provider} Die Anmeldung im Webfrontend kann mit dem + offenen Protokoll \Gls{oauth} 2.0 über Google, Apple oder Facebook erfolgen. + Die bei der Verknüpfung eines \Gls{podcatcher}s mit dem Synchronisationsserver + geforderten Anmeldedaten werden dann automatisch für den betreffenden + Benutzer generiert. + Diese kann er im Webfrontend einsehen. + \item\label{r:live-update} Im Webfrontend angemeldete Benutzer bleiben dort + angemeldet, wenn das Backend ein Update bekommt. + \item\label{r:language} Die Benutzeroberfläche kann in mehreren Sprachen + angezeigt werden, wobei neben der standardmäßig deutschen + Benutzeroberfläche die zusätzliche Bereitstellung einer englischen + Version gegenüber anderen Fremdsprachen priorisiert angestrebt wird. + \item\label{r:dsgvo} Der Umgang mit personenbezogenen Daten ist konform mit + der \\\Gls{dsgvo} der Europäischen Union. + \item\label{r:docker} Die Benutzung von \Gls{docker} vereinfacht das Deployment auf + einen Server, da Abhängigkeiten bereits im \Gls{docker}-Image enthalten sind. + Außerdem bleibt bei einer Kompromittierung der Software das Host-System + durch Virtualisierung der Container sicher.\\ +\end{RClist} + +\subsection{ Abgrenzungskriterien } + +Abgrenzungskriterien: Leistungen, die explizit nicht umgesetzt werden. + +\begin{RWlist} + \item\label{r:playback} Das Webfrontend stellt explizit keine Funktionalität + zum Anhören von \Glspl{podcast} bereit und grenzt sich unter anderem dadurch + von \Gls{podcatcher}-Software ab. + \item\label{r:no-logs} Es werden explizit keine Logdateien für Benutzer-Aktionen + gespeichert. Stattdessen wird nur genau die Liste der \Glspl{abo} eines + Benutzers und der zeitliche Fortschritt jeder \Gls{episode} aktuell gehalten. + \item\label{r:no-devices} Die benutzerdefinierte Synchronisation über + verschiedene, für die Synchronisation differenzierte Geräte wird nicht + unterstützt. + Das heißt, die gespeicherten Daten eines Benutzers werden über alle + mit seinem Account verbundenen \Gls{podcatcher} unabhängig vom Gerät auf den + gleichen, letzten Stand synchronisiert. + \item\label{r:discovery} Das Webfrontend bietet keine Funktionalität zum + Suchen von \Glspl{podcast} (\Gls{discovery}) an. + \item\label{r:no-man} Im Webfrontend wird lediglich die Möglichkeit geboten, + eine zunächst leere Benutzeranleitung \ref{r:man} anzuzeigen. Eine + inhaltlich vollständig ausgeschriebene Benutzeranleitung wird + ausdrücklich nicht bereitgestellt. +\end{RWlist} + diff --git a/00-pflichtenheft/sections/benutzeroberflaeche.tex b/00-pflichtenheft/sections/benutzeroberflaeche.tex new file mode 100644 index 0000000..536ed04 --- /dev/null +++ b/00-pflichtenheft/sections/benutzeroberflaeche.tex @@ -0,0 +1,183 @@ +\section{Benutzeroberfläche} + +Die Synchronisation der \Glspl{podcast} soll über eine Weboberfläche verwalten werden. +Dazu wird eine \Gls{spa} erstellt. Diese kann durch ein +\Gls{responsive}-Design für Desktop- und Mobilgeräte nutzerfreundlich angezeigt +werden. + +Die Navigation erfolgt über eine Navigationsleiste, welche auf jeder +nutzerbezogenen Seite zu sehen ist. Über die Navigationsleiste können über +Knöpfe alle anderen nutzerbezogenen Seiten aufgerufen werden. Über ein +Dropdown-Menu werden Accounteinstellungen und ein Logout-Knopf sichtbar (siehe +Abbildung \ref{fig:listening-progress-account-dropdown}). Ein weiters +Dropdown-Menu erlaubt das Wechseln der Sprache (siehe Abbildung +\ref{fig:podcast-desktop-change-language}). Der ,,Hilfe''-Eintrag öffnet ein +Fenster mit Hilfestellungen (siehe Abbildung \ref{fig:help-desktop}). Durch das +\Gls{responsive}-Design kann die Navigations-Leiste auf Mobilgeräten sich zu +einem Burger-Menu zusammenklappen. + +Die Komponenten der Weboberfläche werden von einem Still-Framework, wie Bootstrap, +benutzt. + +% Übersichtlichkeit wichtig? + +% Webanwendung +% Single-Page-Application +% Responsive +% Navigationsleiste +% Bootstrap + +\subsection{Login} + +Bei der erstmaligen Nutzung des Services wird der Nutzer aufgefordert, sich neu +für die \Gls{podcast}-Synchronisation zu registrieren. Dabei gibt der Nutzer seine +E-Mail und ein Passwort ein. Daraufhin wird dem Nutzer eine E-Mail mit einem +Bestätigungslink an seine E-Mail-Adresse versandt. Erst wenn der Link angeklickt +wird, ist der Account freigeschaltet und der Nutzer wird zur Anmeldeseite +weitergeleitet (siehe Abbildung \ref{fig:login-mobile}). + +Alternativ kann der Nutzer sich auch mit einem Identifikationsbereitsteller, wie +Google oder Facebook registrieren und anmelden. + +Zusätzlich kann der Nutzer beim Anmelden über eine Checkbox einstellen, dass +dieser angemeldet bleiben möchte, sodass sich dieser beim nächsten Aufruf der +Weboberfläche nicht erneut anmelden muss. + +Nach dem Anmelden wird der Nutzer zur \Gls{podcast}-Liste weitergeleitet. + +% Registrieren +% E-Mail und Passwort, E-Mail bestätigung + +% anmelden mit E-Mail und Passwort oder es kann sich auch mit einem +% Identifikations-Bereitsteller (wie Google, Facebook, GitHub) angemeldet +% werden. + +% Angemeldet bleiben + +% Anschließlich auf der Startseite + +\subsection{Podcast-Liste} + +Die Seite der \Gls{podcast}-Liste zeigt eine Liste aller vom Nutzer abonnierten +\Glspl{podcast}. Diese sind danach sortiert, welcher \Gls{podcast} zuletzt +gehört wurde (siehe Abbildung \ref{fig:podcast-desktop-change-language}). + +\Glspl{podcast} können aufgeklappt werden, um \Glspl{episode} inklusive deren Hörfortschritt +anzuzeigen. Die \Glspl{episode} werden nach Veröffentlichungszeitpunkt sortiert. + +% Navigatinsleiste +% Zeigt eine Liste aller vom Nutzer abonnierten Podcasts sortiert nach welcher +% Podcast zuletzt gehört wurde. +% Podcasts können aufgeklappt werden, um Episoden inklusive deren Hörfortschritt +% anzuzueigen. Die Episoden werden nach Veröffentlichungszeitpunkt sortiert. + +\subsection{Zuletzt gehört} + +Die Seite ,,Zuletzt gehört'' zeigt eine Liste der vom Nutzer gehörten +\Glspl{episode} inklusive Hörfortschritt (siehe Abbildung +\ref{fig:listening-progress-account-dropdown}). Die \Glspl{episode} sind danach +sortiert wann sie zuletzt gehört wurden. + +Zusätzlich können die beendeten und angefangenen \Glspl{episode} über ein +Dropdown-Menu auch auf- und absteigend lexikografisch oder nach Hörfortschritt +sortiert werden. + +% \newpage + +\subsection{Einstellungen} + +Auf der Einstellungsseite darf der Nutzer sein Passwort ändern und seinen +Account löschen (siehe Abbildung \ref{fig:settings-mobile-1}). + +Der Nutzer kann seine personenbezogenen Daten, wie abonnierte \Glspl{podcast} und +Hörfortschritte, in eine Datei exportieren, welche der Nutzer herunterladen +kann. Außerdem kann der Nutzer zuvor heruntergeladene Exports wieder +importieren, um zum Beispiel alte abonnierte \Glspl{podcast} wiederherzustellen. + +Zusätzlich kann der Nutzer seine Daten von einer anderen \Gls{gpodder} importieren, +indem er sich in den Einstellungen auf der anderen \Gls{gpodder} anmeldet. + +% Passwort ändern +% Account löschen +% Daten importieren/exportieren +% Daten anderer Gpodder-API importieren + +% \vspace{1cm} + +%===================================================================================% +% https://tex.stackexchange.com/questions/55337/how-to-use-figure-inside-a-minipage % +%===================================================================================% +\hspace{-.5cm} +\begin{minipage}[H]{\linewidth} + %\centering + \begin{minipage}{0.32\linewidth} + \begin{figure}[H] + \fbox{\includegraphics[width=.9\linewidth]{assets/ui/login-mobile.png}} + \setcapindent*{1em} + \caption{Anmeldeseite mit Identitätsbereitsteller\\} + \label{fig:login-mobile} + \end{figure} + \end{minipage} +% \hspace{0.05\linewidth} + \begin{minipage}{0.32\linewidth} + \begin{figure}[H] + \fbox{\includegraphics[width=.9\linewidth]{assets/ui/podcasts-mobile.png}} + \setcapindent*{1em} + \caption{Mobile \Glspl{podcast}-Seite mit Burger-Menu\\} + \label{fig:podcasts-mobile} + \end{figure} + \end{minipage} +% \hspace{0.05\linewidth} + \begin{minipage}{0.32\linewidth} + \begin{figure}[H] + \fbox{\includegraphics[width=.9\linewidth]{assets/ui/listening-progress-mobile.png}} + \setcapindent*{1em} + \caption{Mobile ,,Zuletzt gehört''-Seite mit offenem Burger-Menu} + \label{fig:listening-progress-mobile} + \end{figure} + \end{minipage} +\end{minipage} + +\begin{figure}[H] + \centering + \fbox{\includegraphics[width=.9\linewidth]{assets/ui/podcasts-desktop-change-language.png}} + \caption{\Glspl{podcast}-Seite mit \Glspl{episode} und Sprachauswahl} + \label{fig:podcast-desktop-change-language} +\end{figure} + +\begin{figure}[H] + \centering + \fbox{\includegraphics[width=.9\linewidth]{assets/ui/listening-progess-account-dropdown.png}} + \caption{,,Zuletzt gehört''-Seite mit Fortschrittsbalken und Account-Dropdown} + \label{fig:listening-progress-account-dropdown} +\end{figure} + +\begin{figure}[H] + \centering + \includegraphics[width=.87\linewidth]{assets/ui/help-desktop.png} + \caption{Hilfe-Fenster, welches mit dem Menu-Eintrag ,,Hilfe'' geöffnet wird} + \label{fig:help-desktop} +\end{figure} + +\begin{minipage}[H]{.9\linewidth} + \centering + \begin{minipage}{.4\linewidth} + \begin{figure}[H] + \centering + \fbox{\includegraphics[width=.8\linewidth]{assets/ui/settings-mobile-1.png}} + \setcapindent*{1em} + \caption{Einstellungen mit Ändern des Passworts und Gpodder-Verknüpfung} + \label{fig:settings-mobile-1} + \end{figure}% + \end{minipage} + \hspace{.5cm} + \begin{minipage}{.4\linewidth} + \begin{figure}[H] + \fbox{\includegraphics[width=.8\linewidth]{assets/ui/settings-mobile-2.png}} + \setcapindent*{1em} + \caption{Einstellungen mit Datenexport und Accountlöschung\\} + \label{fig:settings-mobile-2} + \end{figure} + \end{minipage} +\end{minipage} + diff --git a/00-pflichtenheft/sections/einleitung.tex b/00-pflichtenheft/sections/einleitung.tex new file mode 100644 index 0000000..67b6208 --- /dev/null +++ b/00-pflichtenheft/sections/einleitung.tex @@ -0,0 +1,34 @@ +\section{Einleitung} + +Dieses Softwareprojekt setzt sich zum Ziel, einen im Vergleich zu anderen Synchronisationsservern +möglichst schlanken und performanten Synchronisationsserver für \Glspl{podcast} +bereitzustellen, welcher von sogenannten \Glspl{podcatcher} verwendet werden soll. + +Ein \Gls{podcatcher} ist eine Anwendung, mit der \Glspl{podcast} abonniert, +heruntergeladen und angehört werden können. +Durch diesen Synchronisationsserver ermöglichen Betreiber von \Glspl{podcatcher} den Nutzern +eine einfache Möglichkeit zum Synchronisieren von \Glspl{podcast} über verschiedene +Geräte hinweg. +Im Gegensatz zu anderen Synchronisationsservern für \Glspl{podcast} versucht dieses Produkt +möglichst einfach und unkompliziert in der Handhabung zu sein. Deshalb wird nur eine +grundlegende Synchronisationsfunktion ohne weitere +Anpassungsmöglichkeit bereitgestellt. +Außerden werden Funktionalitäten die bereits von einem \Gls{podcatcher} ermöglicht werden, wie +zum Beispiel das Anhören und Verwalten von \Glspl{podcast}, nicht bereitgestellt. +Dies sorgt zusätzlich für ein schlankes Backend. + +Es werden hierbei sowohl die \Glspl{podcast} an sich, als auch die aktuellen +Hörfortschritte synchronisiert. +Das bedeutet, dass alle vorgenommenen Änderungen auf den Server gespiegelt +werden. +Die Änderungen werden danach von jedem mit dem Account verbundenen +\Gls{podcatcher} heruntergeladen. +Dazu werden die \Glspl{podcast} und die Hörfortschritte eines jeden Nutzers in einer +\Gls{db} gespeichert. +Auf die \Gls{db} können die \Gls{podcatcher} mit einer API in Verbindung eines +Nutzeraccounts lesend und schreibend zugreifen. +Um eine Kompatibilität mit bestehenden Podcatchern +sicherzustellen, wird die weitverbreitete \Gls{gpodder} verwendet. +Zusätzlich soll es Nutzern durch ein Webinterface ermöglicht werden, ihre +synchronisierten \Glspl{podcast} einsehen zu können und ihren Nutzeraccount verwalten zu können. + diff --git a/00-pflichtenheft/sections/glossar.tex b/00-pflichtenheft/sections/glossar.tex new file mode 100644 index 0000000..8dce712 --- /dev/null +++ b/00-pflichtenheft/sections/glossar.tex @@ -0,0 +1,272 @@ +\makeglossaries + +\newglossaryentry{spa} +{ + name=Single-Page-Application, + description={ + ist ein Webseiten-Modell, bei welchem dem Nutzer nur ein Webdokument + bereitgestellt wird. Mit einem Skript wird der Inhalt der Seite + dynamisch mit Daten einer API befüllt. Außerdem verwaltet die Seite + (nicht der Server), welcher Inhalt bei welchem Pfad angezeigt wird. Dies + erzeugt geringere Serverlast und eine bessere Nutzererfahrung, da die + Seitenstruktur beim Laden von neuen Inhalten erhalten bleibt} +} + +\newglossaryentry{packagemanager} +{ + name=Paketmanager, + description={ + ist ein Programm, welches Pakete und dessen Abhängigkeiten verwaltet, + installiert, entfernt und aktualisiert. Pakete können andere Programme, + Plugins oder Software-Bibliotheken sein} +} + +\newglossaryentry{bundler} +{ + name=Bundler, + description={ + ist ein Programm, welches genutzte Teile von Abhängigkeiten eines + Software-Projekts in passender Reihenfolge zusammensucht und daraus + Dateien erstellt, die für den Nutzer bereitgestellt werden können. Dabei + kann der Bundler mit zusätzlichen Modulen Dateien erzeugen, die + rückwärtskompatibel oder für den Nutzer schwerer einsehbar sind} +} + +\newglossaryentry{java} +{ + name=Java, + description={ + ist eine objekt-orientierte interpretierte kompilierte + Programmiersprache, welche plattformunabhängig auf einer virtuellen + Maschine ausgeführt wird} +} + +\newglossaryentry{db} +{ + name=Datenbank, + description={ + ist ein System um Daten persistent zu speichern und effizient zu + verwalten. Am meisten verbreitet sind relationale Datenbanken, welche + Daten in Tabellen mit Referenzen zu Einträgen anderer Tabellen + speichern. Programme können dann über eine Anfragesprache (Structured + Query Language - SQL) komplexe Operationen auf den Daten ausführen} +} + +\newglossaryentry{docker} +{ + name=Docker, + description={ + ist ein Programm, das virtualisierte Container ausführt. Ein Programm in + so einem Container läuft in seiner eigenen virtuellen Umgebung, wodurch + das Host-System sicher bleibt. Zudem lassen sich die Container leicht + auf andere Systeme verteilen} +} + +% RESTfull-API, JSON, RSS-Feed, Salting and Hasing, OAuth, Cookie, Garbage +% Collection, DSGVO, Podcast, Podcatcher, Episode, Gpodder, + +\newglossaryentry{podcatcher} +{ + name=Podcatcher, + plural=Podcatchern, + description={ + ist ein Programm, über welches man Podcasts entdecken, abonnieren und + Episoden von Podcasts hören kann. Mit einem Account auf einer Plattform, + welche eine Gpodder-API zur Verfügung stellt, können Ereignisse, die von + einem Nutzer ausgehen, auf anderen Podcatchern des Nutzers + synchronisiert werden} +} + +\newglossaryentry{podcast} +{ + name=Podcast, + description={ + ist ein RSS-Feed, dessen Einträge die Episoden darstellen} +} + +\newglossaryentry{episode} +{ + name=Episode, + plural=Episoden, + description={ + ist ein Eintrag in einem Podcast. Eine URL in dem Eintrag zeigt auf eine + Medien-Datei, welche vom Podcatcher abgespielt werden kann} +} + +\newglossaryentry{rest-api} +{ + name=RESTful-API, + description={ + ist ein Schnittstellenentwurf über das Hypertext Transfer Protocol + (HTTP), bei dem die Schnittstellen strukturiert als Pfad an einem + Endpunkt erreichbar sind. Mittels verschiedener HTTP-Methoden können an + der Schnittstelle Daten abgefragt (GET), gesendet (PUT), gelöscht + (DELETE) oder geändert (POST) werden. Die Daten, die über die + Schnittstelle gesendet werden liegen meist im JSON-Format vor} +} + + +\newglossaryentry{gpodder} +{ + name=Gpodder-API, + description={ + wird von gpodder.net benutzt und entwickelt. Die API wird als + Schnittstelle zwischen Podcatchern und Podcast Synchronisationsservern + verwendet. Weitere Details sind unter + "https://gpoddernet.readthedocs.io/en/latest/api/" zu finden} +} + +\newglossaryentry{json} +{ + name=JSON, + description={ + (JavaScript Object Notation) ist ein Datenformat und wird zur + Übertragung von Strukturen und Daten eingesetzt. JSON besteht dabei aus + grundlegenden Datentypen sowie Objekten mit Schlüssel-Wert Paaren und + Listen} +} + +\newglossaryentry{oauth} +{ + name=OAuth, + description={ + (Open Authorization) ist ein offenes Protokoll, welches es Nutzern + ermöglicht, sich mit bereits bestehenden Accounts bei anderen Diensten + zu registrieren. Dabei werden benötigte Daten für die Registrierung über + die bereitgestellte Schnittstelle zur Verfügung gestellt} +} + +\newglossaryentry{garbage-collection} +{ + name=Garbage Collection, + description={ + ist eine automatische Speicherbereinigung, welche nicht mehr benötigten + Speicherplatz wieder freigibt. Die Bereinigung kann dabei in determinierten + Zeitintervallen erfolgen oder durch bestimmte Ereignisse ausgelöst + werden} +} + +\newglossaryentry{salt-hash} +{ + name=Salting und Hashing, + description={ + ist eine Methode um Passwörter so zu kodieren, dass sie nicht als + Klartext gespeichert werden und auch sicher vor Hash-Wörterbüchern sind. + Dafür wird dem Passwort ein bekanntes Wort, der Salt, angefügt, bevor + aus dem kompletten Wort eine Prüfsumme, ein Hash, generiert wird. Beim + Anmelden wird die Prüfsumme der Anmeldung mit der bekannten + Prüfsumme des Passworts verglichen} +} + +\newglossaryentry{rss} +{ + name=RSS, + description={ + (Really Simple Syndication) zeigt strukturiert Listen von Nachrichten + an. Die Änderungen werden im XML-Format in sogenannte RSS-Dateien + geschrieben, welche über einen Link abgerufen werden können} +} + +\newglossaryentry{dsgvo} +{ + name=Datenschutz-Grundverordnung, + description={ + (DSGVO) ist eine im europäischen Wirtschaftsraum + geltende Verordnung. Sie sorgt für eine Reglementierung bei der + Verarbeitung personenbezogener Daten. Unter anderem muss einsehbar sein, + welche Daten von Nutzern erhoben werden. Außerdem muss für einen Nutzer + die Möglichkeit bestehen, seine erhobenen Daten abrufen zu können} +} + +\newglossaryentry{push-pull} +{ + name=Push und Pull, + description={ + sind Methoden, um Daten auszutauschen. Bei der Pull-Methode + stellt Akteur A einem Akteur B eine Anfrage auf Daten und erhält diese + als Antwort. Damit Akteur A und B immer auf dem selben Stand sind, muss + Akteur A chronisch Anfragen an Akteur B stellen. Im Gegensatz dazu steht + die Push-Methode, bei der Akteur B den Akteuren mitteilt, dass er neue + Änderungen hat. Dafür muss Akteur B allerdings wissen mit welchen + anderen Akteuren er in Verbindung steht und diese Verbindung aufrecht + erhalten} +} + +\newglossaryentry{ui-lib} +{ + name=UI-Bibliothek, + plural=UI-Bibliotheken, + description={ + kümmert sich um das Layout einer Webseite. Dabei unterscheidet man + zwischen Design-Bibliotheken (wie Bootstrap), welche fertige + UI-Komponenten bereitstellen, und Layout-Bibliotheken (wie Vue oder + React.js), welche die Komponenten basierend auf Daten dynamisch + anzeigen} +} + +\newglossaryentry{responsive} +{ + name=Responsive, + description={ + Design ist ein Design-Prinzip für Webseiten, bei dem die selbe Webseite ihre + Komponenten dynamisch der Bildschirmbreite anpasst} +} + +\newglossaryentry{pseudoprotocol} +{ + name=Pseudoprotokoll, + description={ + ist ein URL-Schema, auf das Webseiten hören können, wenn sie sich das + URL-Schema im Browser anmelden. Bekannt Pseudoprotokolle sind: + ,,mailto:'', ,,tel:'' oder ,,irc:''} +} + +\newglossaryentry{dashboard} +{ + name=Dashboard, + description={ + ist die erste Seite auf der man landet, wenn man angemeldet ist} +} + +\newglossaryentry{abo} +{ + name=Abonnement, + description={ + ist ein abonnierter Podcast} +} + +\newglossaryentry{discovery} +{ + name=Discovery, + description={ + ist ein Feature der Gpodder-API, welches dem Nutzer eine Reihe von + Podcasts zum abonnieren anbietet} +} + +\newglossaryentry{session-token} +{ + name=Session-Token, + description={ + ist ein Wort, dass vom Client gespeichert wird solange der Nutzer + eingeloggt ist und bei jeder Anfrage an den Server mitgeschickt wird. + Der Server kann den Session-Token einem Nutzer zuordnen und so mit + nutzerspezifischen Daten antworten} +} + +\newglossaryentry{cookie} +{ + name=Cookie, + description={ + ist ein kleiner webseitenspezifischer Speicher im Browser, welcher vom + Server und von der Webseite gesetzt werden kann und bei jeder weiteren + Anfrage an den Server mitgesendet wird. Cookies bleiben entweder + temporär im Browserspeicher, bis der Browser geschlossen wird oder + permanent, bis ein optionales Verfallsdatum erreicht ist} +} + +% UI-Framework (React.js/Vue/Bootstrap) +% Responsive Design +% Abonnement +% Pseudoprotokoll +% Dashboard + diff --git a/00-pflichtenheft/sections/produktdaten.tex b/00-pflichtenheft/sections/produktdaten.tex new file mode 100644 index 0000000..c74e3fb --- /dev/null +++ b/00-pflichtenheft/sections/produktdaten.tex @@ -0,0 +1,83 @@ +\section{Produktdaten} + +Das Projekt erhebt an unterschiedlichen Stellen Daten des Benutzers oder anderen +Plattformen. Diese Daten können temporär gespeichert werden. Dabei bleiben die +Daten nur für eine kurze Zeit gespeichert, zum Beispiel so lange wie der Nutzer +eingeloggt ist. Andere Daten können auch persistent gespeichert werden. Dabei +bleiben die Daten solange gespeichert, bis sie manuell oder durch +\Gls{garbage-collection} gelöscht werden. + +\subsection{Weboberfläche} + +Die Weboberfläche bezieht direkt Daten vom Benutzer. Die Weboberfläche erhält +E-Mail-Adresse und Passwort vom Benutzer, speichert diese aber nur solange, bis +diese an das Backend zur Validierung geschickt werden. Daraufhin erhält die +Weboberfläche einen \Gls{session-token} welcher entweder persistent oder temporär als +\Gls{cookie} gespeichert bleibt, je nachdem, ob der Nutzer angemeldet bleiben möchte. + +Zusätzlich erhält die Weboberfläche die abonnierten \Glspl{podcast} und gehörten +\Glspl{episode} inklusive Metadaten vom Backend. Diese Daten werden nur temporär, +solange die Webseite offen ist, gespeichert. + +\subsubsection*{Persistent} + +\begin{itemize} + \item \Gls{session-token} (falls der Nutzer angemeldet bleiben möchte) +\end{itemize} + +\subsubsection*{Temporär} + +\begin{itemize} + \item Abonnierte \Glspl{podcast} + \item Gehörte \Glspl{episode} + \item \Gls{session-token} +\end{itemize} + +\subsection{Backend} + +Das Backend interagiert nicht direkt mit dem Benutzer, sondern bekommt Daten von +der Weboberfläche und \Glspl{podcatcher}. Wenn sich ein Nutzer über die Weboberfläche +oder im \Gls{podcatcher} anmeldet oder registriert speichert der Server temporär die E-Mail-Adresse und +das Passwort des Nutzers im Klartext, bevor es die Daten gegen die gehashten und +gesalzenen persistenten Daten der \Gls{db} abgleicht beziehungsweise speichert. + +Außerdem speichert das Backend abonnierte \Glspl{podcast} und gehörte +\Glspl{episode}, welche +es vom \Gls{podcatcher} bekommt, im persistenten Speicher. Diese Daten bleiben +gespeichert bis sie durch neue Daten obsolet werden (\Gls{garbage-collection}), +vom Nutzer manuell gelöscht werden oder der Account gelöscht wird. + +Wenn die Weboberfläche \Glspl{episode} über \Glspl{podcast} abfragt, welche noch nicht vom +Nutzer gehört wurden, holt der Server Metadaten über den \Glspl{podcast} und speichert +Informationen und Bilder des \Glspl{podcast} temporär, solange der Nutzer die +Weboberfläche geöffnet hat. + +\subsubsection*{Persistent} +\begin{itemize} +% Email (gehasht und gesalzen) / Betreuer fragen weil evtl Mails gesendet werden +% müssen, falls Datenschutzbedinungen geändert werden +\item E-Mail-Adresse gehasht und gesalzen +\item Passwort gehasht und gesalzen +\item Abonnierte \Glspl{podcast} +\item Gehörte \Glspl{episode} (Hörfortschritte) +\end{itemize} + +\subsubsection*{Temporär} +\begin{itemize} + \item Metadaten von Feeds (\Glspl{episode}, Bilder) +\end{itemize} + +\subsection{Podcatcher} + +Der \Gls{podcatcher} speichert die E-Mail-Adresse, das Passwort und die URL der +Gpodder-Instanz im persistenten Speicher. Zusätzlich speichert der +\Gls{podcatcher} +abonnierte \Glspl{podcast} und gehörte \Glspl{episode} im persistenten Speicher. + +\subsubsection*{Persistent} +\begin{itemize} +\item E-Mail-Adresse, Passwort und URL der Gpodder-Instanz +\item Abonnierte \Glspl{podcast} +\item Gehörte \Glspl{episode} +\end{itemize} + diff --git a/00-pflichtenheft/sections/produktfunktionen.tex b/00-pflichtenheft/sections/produktfunktionen.tex new file mode 100644 index 0000000..12c5b56 --- /dev/null +++ b/00-pflichtenheft/sections/produktfunktionen.tex @@ -0,0 +1,215 @@ +\section{ Produktfunktionen } + +\subsection{ Registrierung } +\label{f:registrierung} + +\begin{description} + \item[Anwendungsfall:] Der Nutzer möchte sich registrieren. + \item[Anforderungen:] \ref{r:login} + \item[Test:] \ref{t:register}, \ref{t:pw-req} + \item[Ziel:] Der Nutzer hat einen funktionierenden Account. + \item[Vorbedingung:] - + \item[Nachbedingung Erfolg:] Erfolgreiche Registrierung. + \item[Nachbedingung Fehlschlag:] Registrierung/Verbindungsaufbau zum Server schlägt + fehl. + \item[Akteure:] Nutzer, Server + \item[Auslösendes Ereignis:] Drücken des \enquote{Registrieren}-Buttons. + \item[Beschreibung:] \mbox{} + \begin{enumerate} + \item Öffnen der Webseite. + \item Drücken auf \enquote{Registrieren}-Option. + \item E-Mail-Adresse und zwei Mal neues Passwort eingeben. + \item \enquote{Registrieren}-Button drücken. + \item Bestätigungs-E-Mail wird an angegebene E-Mail-Adresse gesendet. + \item Nutzer öffnet Bestätigungs-Link. + \item Nutzer wird auf Login-Seite weitergeleitet. Der Bestätigungs-Link verfällt. + \end{enumerate} + \item[Erweiterung:] Wenn der Link nach 24 Stunden noch nicht angeklickt wurde, verfällt \\ + dieser. +\end{description} + +\newpage + +\subsection{ Anmelden } +\label{f:anmelden} + +\begin{description} + \item[Anwendungsfall:] Der Nutzer möchte sich anmelden + \item[Anforderungen:] \ref{r:login}, \ref{r:login-provider} + \item[Test:] \ref{t:login} + \item[Ziel:] Der Nutzer ist eingeloggt. + \item[Vorbedingung:] Der Nutzer hat einen funktionierenden Account. + \item[Nachbedingung Erfolg:] Erfolgreiche Anmeldung + \item[Nachbedingung Fehlschlag:] Anmeldung/Verbindungsaufbau zum Server schlägt + fehl. + \item[Akteure:] Nutzer, Server + \item[Auslösendes Ereignis:] Drücken des \enquote{Anmelden}-Buttons. + \item[Beschreibung:] \mbox{} + \begin{enumerate} + \item Öffnen der Webseite. + \item E-Mail-Adresse und Passwort eingeben. + \item Option \enquote{Passwort merken} aus-/abwählen. + \item \enquote{Anmelden}-Button drücken. + \item Nutzer wird zu seinem \Gls{dashboard} weitergeleitet. + \end{enumerate} + \item[Erweiterung:] Wenn die Option \enquote{Passwort merken} aktiviert ist, bleibt der Nutzer \\ + sitzungsübergreifend in seinem Account eingeloggt. + \item[Alternativen:] \mbox{} + \begin{enumerate} + \item Falls sich der Browser einen vorherigen Login gemerkt hat, + wird der Nutzer beim Aufruf der Seite direkt zu seinem + \Gls{dashboard} weitergeleitet. + \item Der Nutzer meldet sich mit einem \Gls{oauth} Dienst an. + \end{enumerate} +\end{description} + +\newpage + +\subsection{ Passwort vergessen } +\label{f:pwreset} + +\begin{description} + \item[Anwendungsfall:] Der Nutzer hat sein Passwort vergessen und möchte es zurücksetzen. + \item[Anforderungen:] \ref{r:reset-pw} + \item[Test:] \ref{t:pw-req}, \ref{t:forgot-pw} + \item[Ziel:] Der Nutzer hat ein neues Passwort. + \item[Vorbedingung:] Der Nutzer hat einen bestehenden Account und Zugriff auf seine E-Mail-Adresse + \item[Nachbedingung Erfolg:] Erfolgreiche Passwort Zurücksetzung. + \item[Nachbedingung Fehlschlag:] Zurücksetzung des Passworts / Verbindungsaufbau zum Server + schlägt fehl. + \item[Akteure:] Nutzer, Server + \item[Auslösendes Ereignis:] Drücken der \enquote{Passwort-Vergessen} Option auf der Login-Seite. + \item[Beschreibung:] \mbox{} + \begin{enumerate} + \item Öffnen der Webseite. + \item \enquote{Passwort-Vergessen} Option auswählen. + \item Weiterleitung auf \enquote{Passwort zurücksetzen} Seite. + \item E-Mail-Adresse des Accounts eingeben. Falls kein Account mit dieser + Adresse existiert, wird dieser Schritt wiederholt. + \item Bestätigungs-E-Mail mit Zurücksetzungs-Link wird an die angegebene + E-Mail-Adresse gesendet. + \item Der Nutzer öffnet den Zurücksetzungs-Link. + \item Ein Neues Passwort zweimal eingeben. + \item Auf den \enquote{Passwort-Zurücksetzen}-Button klicken. + \item Weiterleitung auf Login-Seite. + \end{enumerate} +\end{description} + +\newpage + +\subsection{ Passwort ändern } +\label{f:pwchange} + +\begin{description} + \item[Anwendungsfall:] Der Nutzer möchte sein Passwort ändern. + \item[Anforderungen:] \ref{r:reset-pw} + \item[Test:] \ref{t:pw-req}, \ref{t:change-pw} + \item[Ziel:] Der Nutzer hat ein neues Passwort. + \item[Vorbedingung:] Der Nutzer ist in seinem Account angemeldet. + \item[Nachbedingung Erfolg:] Erfolgreiche Passwort Änderung. + \item[Nachbedingung Fehlschlag:] Änderung des Passworts / Verbindungsaufbau zum Server + schlägt fehl. + \item[Akteure:] Nutzer, Server + \item[Auslösendes Ereignis:] Betätigen des \enquote{Passwort-Ändern}-Buttons. + \item[Beschreibung:] \mbox{} + \begin{enumerate} + \item Öffnen der Webseite. + \item Anmelden + \item Auf Account-Einstellungen gehen. + \item Bisheriges Passwort und zweimal neues Passwort in dafür vorgesehene Felder + eintippen. + \item \enquote{Passwort-Ändern}-Button betätigen. + \end{enumerate} +\end{description} + +\newpage + +\subsection{ Hörfortschritt synchronisieren } +\label{f:hörfortschrittSync} + +\begin{description} + \item[Anwendungsfall:] Der Nutzer hört mit einem verknüpften + \Gls{podcatcher} eine \Gls{episode} bis zu + einem gewissen Zeitpunkt. + Der Hörfortschritt soll mit dem Server und allen anderen Geräten synchronisiert werden. + \item[Anforderungen:] \ref{r:sync}, \ref{r:store}, \ref{r:persistent-storage}, \ref{r:gpodder} + \item[Test:] \ref{t:sync-episode} + \item[Ziel:] Der Hörfortschritt wird auf den Server und alle verknüpften Geräte übertragen. + \item[Vorbedingung:] Der Nutzer hat seinen Account mit einem \Gls{podcatcher} verknüpft und verwendet + diesen im Folgenden. + \item[Nachbedingung Erfolg:] Erfolgreiche Synchronisation des Hörfortschritts. + \item[Nachbedingung Fehlschlag:] Verbindungsaufbau zum Server schlägt + fehl. +\item[Akteure:] Nutzer, \Gls{podcatcher}, Server +\item[Auslösendes Ereignis:] Anhören einer \Gls{episode} bis zu einem gewissen Zeitpunkt. + \item[Beschreibung:] \mbox{} + \begin{enumerate} + \item Der Nutzer hört innerhalb eines \Gls{podcatcher}s eine + \Gls{episode}. + \item Der Hörfortschritt wird dem Server über die \Gls{gpodder} mitgeteilt. + \item Der Server aktualisiert den Hörfortschritt im entsprechenden Datensatz + des Nutzeraccounts zur \Gls{episode}. + \end{enumerate} + \item[Erweiterung:] Wenn sich ein weiterer \Gls{podcatcher} aktualisiert, ruft dieser + die neuen Hörfortschritte des Nutzers vom Server über die \Gls{gpodder} ab und wendet diese an. +\end{description} + +\newpage + +\subsection{ Abonnements synchronisieren} +\label{f:abonnentsSync} + +\begin{description} + \item[Anwendungsfall:] Der Nutzer fügt auf einem verknüpften + \Gls{podcatcher} ein \Gls{abo} hinzu bzw. löscht ein \Gls{abo}. + Dieses soll mit dem Server und allen anderen Geräten synchronisiert werden. + \item[Anforderungen:] \ref{r:sync}, \ref{r:store}, \ref{r:persistent-storage}, \ref{r:gpodder} + \item[Test:] \ref{t:sync-sub}, \ref{t:sync-unsub} + \item[Ziel:] Das (De-)\Gls{abo} wird auf den Server und alle verknüpften Geräte übertragen. + \item[Vorbedingung:] Der Nutzer hat seinen Account mit einem \Gls{podcatcher} verknüpft und verwendet + diesen im Folgenden. +\item[Nachbedingung Erfolg:] Erfolgreiche Synchronisation des \Glspl{abo}. + \item[Nachbedingung Fehlschlag:] Verbindungsaufbau zum Server schlägt + fehl. +\item[Akteure:] Nutzer, \Gls{podcatcher}, Server +\item[Auslösendes Ereignis:] (De-)Abonnieren eines \Glspl{podcast} innerhalb + eines \Gls{podcatcher}. + \item[Beschreibung:] \mbox{} + \begin{enumerate} + \item Der Nutzer (de-)abonniert innerhalb eines \Gls{podcatcher}s einen + \Gls{podcast}. + \item Die Änderung wird dem Server über die \Gls{gpodder} mitgeteilt. + \item Der Server fügt das neue \Gls{abo} persistent zum Datensatz des Nutzers + auf dem Server hinzu / löscht das \Gls{abo} vom Datensatz auf + dem Server. + \end{enumerate} + \item[Erweiterung:] Wenn sich ein weiterer \Gls{podcatcher} aktualisiert, holt dieser + die aktuelle Liste der \Glspl{abo} des Nutzers vom Server über die + \Gls{gpodder} (Client Pull). +\end{description} + +\newpage + +\subsection{ Account löschen} +\label{f:deleteAccount} + +\begin{description} + \item[Anwendungsfall:] Der Nutzer möchte seinen Account löschen. + \item[Anforderungen:] \ref{r:delete-acc} + \item[Test:] \ref{t:delete-acc} + \item[Ziel:] Der Account und alle Nutzerdaten werden vom Server gelöscht. + \item[Vorbedingung:] Der Nutzer ist angemeldet und befindet sich auf der Einstellungsseite. + \item[Nachbedingung Erfolg:] Der Account wurde gelöscht. + \item[Nachbedingung Fehlschlag:] Verbindungsaufbau zum Server schlägt + fehl. + \item[Akteure:] Nutzer, Server + \item[Auslösendes Ereignis:] Der Nutzer drückt auf den \enquote{Account löschen} Knopf. + \item[Beschreibung:] \mbox{} + \begin{enumerate} + \item Der Nutzer drückt auf den \enquote{Account löschen}-Knopf. + \item Der Nutzer wird dazu aufgefordert sein Passwort als Bestätigung der Löschung + einzugeben. + \item Der Server löscht den Account und alle dazugehörigen Daten. + \item Der Nutzer wird auf die Login-Seite weitergeleitet. + \end{enumerate} +\end{description} diff --git a/00-pflichtenheft/sections/produktuebersicht.tex b/00-pflichtenheft/sections/produktuebersicht.tex new file mode 100644 index 0000000..37e0789 --- /dev/null +++ b/00-pflichtenheft/sections/produktuebersicht.tex @@ -0,0 +1,184 @@ +\section{Produktübersicht} + +In diesem Kapitel wird der Aufbau der Synchronisationsserver-Webseite in einem Use-Case-Diagramm, welches sich in Abbildung +\ref{fig:UseCase} befindet, visualisiert. + +Hierbei wird in Aktivitätsdiagrammen näher auf die Aktionen \enquote{Anmelden} (Abbildung \ref{fig:ActLogin}), +\enquote{Registrieren} (Abbildung \ref{fig:ActRegister}) und \enquote{Passwort vergessen} (Abbildung \ref{fig:ActResetPass}) +eingegangen. + +Weiter beschreibt ein abstrahiertes Sequenzdiagramm (Abbildung +\ref{fig:SeqSynchSubs}) den Nachrichtenaustausch bei der Synchronisation der +gespeicherten Daten eines Benutzers. Dies erfolgt am Beispiel der Tätigung eines +neuen \Glspl{abo} über einen \Gls{podcatcher}. + +\subsection{Der Aufbau der Webseite} + +In dem Use-Case-Diagramm in Abbildung \ref{fig:UseCase} erkennt man den Aufbau der Webseite des Synchronisationsservers. +Besucht man die Seite ohne eingeloggt zu sein, so hat man die Möglichkeit die Sprache zu ändern, sich anzumelden, sich zu registrieren oder sein Passwort über die \enquote{Passwort vergessen}-Funktion \ref{f:pwreset} zurückzusetzen. +Dabei gibt es die Möglichkeit sich mit seinem Google-Konto oder dem Konto eines anderen Anbieters zu registrieren / anzumelden. + +Ist man angemeldet, wird man auf ein \Gls{dashboard} weitergeleitet. +Dort hat man die Möglichkeit eine Anleitung einzusehen und die bisher +abonnierten \Glspl{podcast} einzusehen. +Dabei erhält man zu jedem \Gls{podcast} einen Überblick über dessen +\Glspl{episode} und Details darüber, bis zu welchem Zeitpunkt man die +\Gls{episode} angehört hat. +Auch gibt es die Möglichkeit einen Verlauf und den korrespondierenden +Fortschritt aller zuletzt gehörten \Glspl{episode} einzusehen. + +Des Weiteren kann man das eigene Profil verwalten. +Dies beinhaltet das Ändern des eigenen Passworts, das Importieren der Daten einer Gpodder-Instanz über eine Anmeldung oder +dem Importieren als Datei. +Auch können in der Profilverwaltung personenbezogene Daten als Datei importiert und exportiert werden. +Schließlich hat man in der Profilverwaltung die Möglichkeit den eigenen Account permanent zu löschen. + +Ist man ein Administrator, so hat man zusätzlich die Möglichkeit Statistiken einzusehen. + +\newpage + +% UML Use-Case Diagramm +\begin{figure}[H] + \centering + \hspace*{-2.5cm} + \input{sections/TikzPictures/UseCaseUML} + \caption{Use-Case-Diagramm der Webseite\\(*) - Optionale Funktionen, deren Implementierung nicht feststeht} + \label{fig:UseCase} +\end{figure} +\newpage + +\subsection{Das Einloggen} + +Im Aktivitätsdiagramm \enquote{Anmeldung} in Abbildung \ref{fig:ActLogin} möchte der Nutzer sich auf der Webseite anmelden. +Hierzu ruft der Nutzer zunächst die Webseite auf und landet auf der Login Seite. + +Hat der Nutzer sich bereits zuvor auf dem Computer auf der Seite angemeldet und dabei ausgewählt eingeloggt zu bleiben, +so wird der Nutzer über Session-\Glspl{cookie} automatisch auf das \Gls{dashboard} weitergeleitet. +Ist das nicht der Fall, so muss der Nutzer seine Anmeldedaten eingeben. + +Nach dem Eintragen der Daten hat der Nutzer die Möglichkeit für das nächste Mal, wenn der Nutzer die Webseite +am selben Computer betritt, angemeldet zu bleiben. +Daraufhin drückt der Nutzer den \enquote{Anmelden}-Button. + +Wurde die E-Mail oder das Passwort falsch eingegeben, so zeigt die Seite eine Fehlermeldung an und der Nutzer wird aufgefordert seine Eingabe zu korrigieren. +Wurden die E-Mail und das Passwort korrekt eingegeben, so landet der Nutzer +angemeldet auf dem \Gls{dashboard}, von wo aus er dann alle Aktionen ausführen kann, für die man angemeldet sein muss. + +\newpage + +% UML Activity Diagramm - Login +\begin{figure}[H] + \centering + \input{sections/TikzPictures/ActivityLogin} + \caption{Aktivitätsdiagramm - Anmelden} + \label{fig:ActLogin} +\end{figure} +\newpage + +\subsection{Das Registrieren} + +Möchte sich der Nutzer einen neuen Account erstellen, so läuft dieser Vorgang +entsprechend dem Aktivitätsdiagramm \enquote{Registrieren} in Abbildung \ref{fig:ActRegister} ab. +Der Nutzer ruft zunächst die Webseite auf und landet auf der Login Seite. +Dort wählt der Nutzer die \enquote{Registrieren}-Option aus und wird zur Registrierungsseite weitergeleitet. + +Auf der Registrierungsseite gibt der Nutzer in den jeweils dafür zugeordneten Eingabefeldern seine E-Mail-Adresse und zur Kontrolle +zwei Mal das gleiche Passwort ein, welches dem Account zugeordnet werden soll. +Daraufhin drückt der Nutzer zum Abschluss den \enquote{Registrieren}-Button. +Der Server überprüft, ob die E-Mail-Adresse bereits vergeben ist und ob das Passwort die Passwortmindestanforderungen \ref{r:pw-requirements} erfüllt. + +Ist die E-Mail schon vergeben, so wird dem Nutzer eine entsprechende Meldung angezeigt und der Nutzer muss die eingegebene E-Mail-Adresse korrigieren. +Erfüllt das Passwort nicht die Mindestvoraussetzungen, so wird dem Nutzer auch hier eine entsprechende Meldung angezeigt und der Nutzer muss die Eingabe korrigieren. + +Erfüllen die Eingaben die Voraussetzungen, so wird an die angegebene E-Mail-Adresse eine E-Mail mit einem Link zur Verifizierung +der E-Mail-Adresse geschickt. +Der Nutzer muss diesen Link innerhalb der nächsten 24 Stunden zur Aktivierung seines Kontos öffnen, da ansonsten der Link +abläuft und der Nutzer den Prozess erneut beginnen muss. + +Wurde der Link innerhalb von 24 Stunden geöffnet, so wird der Account aktiviert und der Nutzer wird über den Link zur Login Seite +weitergeleitet, von wo aus er sich mit seinem neu erstellten Account nun einloggen kann. + +\newpage + +% UML Activity Diagram - Register +\begin{figure}[H] + \centering + \input{sections/TikzPictures/ActivityRegister} + \caption{Aktivitätsdiagramm - Registrieren} + \label{fig:ActRegister} +\end{figure} +\newpage + +\subsection{Das Vergessen des Passwortes} + +Hat der Nutzer einmal sein Passwort vergessen, so gibt es die Möglichkeit das Passwort zurückzusetzen. +Wie dieser Vorgang aussieht, wird im Aktivitätsdiagramm \enquote{Passwort zurücksetzen} in Abbildung \ref{fig:ActResetPass} +dargestellt. +Zunächst ruft der Nutzer die Webseite auf und landet auf der Login Seite. +Über die Option \enquote{Passwort vergessen} wird der Nutzer auf die \enquote{Passwort vergessen}-Seite weitergeleitet. + +Hier gibt der Nutzer in einem Eingabefeld die E-Mail-Adresse des Accounts an, dessen Passwort der Nutzer vergessen hat. +Nach der Bestätigung der Eingabe überprüft der Server, ob unter der angegebenen E-Mail-Adresse ein Account angelegt ist. +Ist dies nicht der Fall, so wird der Nutzer erneut darum gebeten die E-Mail-Adresse anzugeben. +Existiert ein Account unter der angegeben E-Mail-Adresse, so wird eine E-Mail an die angegebene Adresse geschickt, die einen +automatisch generierten Link enthält, der es dem Nutzer erlaubt das Passwort für den jeweiligen Account zurückzusetzen. + +Der Nutzer muss den Link innerhalb der nächsten 24 Stunden öffnen, da dieser ansonsten abläuft und zu einer entsprechenden +Fehlermeldung führt. +Öffnet der Nutzer den Link innerhalb von 24 Stunden, so wird der Nutzer auf eine Seite weitergeleitet auf der der Nutzer +zwei Mal das neue Passwort eingeben muss. +Das Passwort muss dabei die Passwortmindestanforderungen \ref{r:pw-requirements} erfüllen. + +Ist das neue Passwort eingegeben, so muss der Nutzer den \enquote{Passwort zurücksetzen}-Button drücken und das eingegebene Passwort wird überprüft. +Erfüllt das Passwort die Mindestanforderungen nicht, so wird dem Nutzer eine entsprechende Fehlermeldung angezeigt und der Nutzer muss sich ein neues Passwort ausdenken und zwei Mal eingeben. +Erfüllt das Passwort die Mindestanforderungen, so wird das neue Passwort für den Account gespeichert und der Nutzer wird auf die Login Seite weitergeleitet, von wo aus er sich mit dem neuen Passwort anmelden kann. + +\newpage + +% UML Activity Diagram - Reset Password +\begin{figure}[H] + \centering + \input{sections/TikzPictures/ActivityResetPass} + \caption{Aktivitätsdiagramm - Passwort vergessen} + \label{fig:ActResetPass} +\end{figure} +\newpage + +\subsection{Das Synchronisieren am Beispiel eines Abonnements} + +Der Benutzer in Abbildung \ref{fig:SeqSynchSubs} hört \Glspl{podcast} über die +verschiedenen \Gls{podcatcher}-Applika\-tionen \enquote{Podcatcher1} und \enquote{Podcatcher2}. +Dabei spielt es im Folgenden keine Rolle ob er beide Applikationen auf demselben Gerät, beide auf jeweils unterschiedlichen Geräten oder die gleiche Applikation auf unterschiedlichen Geräten verwendet. + +Der Benutzer tätigt ein neues \Gls{abo} über die \Gls{podcatcher}-Applikation \enquote{Podcatcher1}. +Dies bewirkt zunächst eine lokale Änderung der Liste der \Glspl{abo}. +Im Zuge dessen wird auch der Server über die vorgenommene Änderung benachrichtigt. +Dieser speichert dann die kommunizierte Änderung persistent in der \Gls{db}. + +Im Folgenden lässt sich der Benutzer die Liste seiner \Glspl{abo} im Webfrontend anzeigen. +Dafür benachrichtigt das Webfrontend den Server darüber, dass es die Liste der +\Glspl{abo} des entsprechenden Nutzers benötigt (\enquote{Client Pull}). +Das Webfrontend erhält die aktuelle Liste, welche insbesondere das zuvor +getätigte \Gls{abo} enthält, und zeigt diese dem Nutzer an. + +Der Benutzer lässt sich nun auch in der \Gls{podcatcher}-Applikation +\enquote{Podcatcher2} die Liste seiner \Glspl{abo} anzeigen. +Die Anwendung \enquote{Podcatcher2} benachrichtigt hierfür nach kurzer Zeit oder +nach manueller Anforderung den Server darüber, dass sie die Liste der +\Glspl{abo} des entsprechenden Nutzers benötigt. + +Die Anwendung erhält die aktuelle Liste, übernimmt diese lokal und zeigt sie dem Nutzer an. +So wird der aktuelle Stand der Liste der \Glspl{abo} (analog der Fortschritt +beim Anhören/Ansehen von \Glspl{episode}) über alle verbundenen \Gls{podcatcher} (und entsprechend alle verbundenen Geräte) synchronisiert. + +\newpage + +% UML Sequence Diagram - Synchronise Subscription +\begin{figure}[H] + \centering + \raisebox{3cm}{ + \input{sections/TikzPictures/SequenceSynchroniseSubscription}} + \caption{Sequenzdiagramm - Synchronisation am Beispiel Abonnieren} + \label{fig:SeqSynchSubs} +\end{figure} +\newpage diff --git a/00-pflichtenheft/sections/tests.tex b/00-pflichtenheft/sections/tests.tex new file mode 100644 index 0000000..9d29aa1 --- /dev/null +++ b/00-pflichtenheft/sections/tests.tex @@ -0,0 +1,605 @@ + +\section{Tests} + +\subsection{Registrierung}\label{t:register} +\vspace{0.3cm} + +\begin{enumerate} + +\item +\begin{description} + \item [Stand] Ein Fenster ist im Browser geöffnet. + \item [Aktion] Der Benutzer gibt die URL der Weboberfläche in die Kopfzeile ein und drückt Enter. + \item [Reaktion] Der Browser wechselt zur Anmeldeseite der Weboberfläche. +\end{description} + +\item +\begin{description} + \item [Stand] Die Anmeldeseite ist im Browser geladen. + \item [Aktion] Der Benutzer klickt auf den Knopf \enquote{Registrieren}. + \item [Reaktion] Der Browser wechselt zur Registrierungsseite. +\end{description} + +\item +\begin{enumerate} + +\item +\begin{description} + \item [Stand] Die Registrierungsseite mit Registrierungsmöglichkeit in der Mitte des Fensters ist geladen. + \item [Aktion] Der Benutzer gibt \enquote{pseIstToll@test.com} als E-Mail und \enquote{Test123!?} als Passwort ein. Weiter gibt er \enquote{test123!?} in das \enquote{Wiederholen} Feld ein. Dann klickt der Benutzer auf den Knopf \enquote{Registrieren}. + \item [Reaktion] Die Registrierungsseite bleibt geladen. Im E-Mail-Feld steht \enquote{pseIstToll@test.com}, im Feld \enquote{Passwort} steht \enquote{Test123!?} und im Feld \enquote{Wiederholen} steht \enquote{test123!?}. Der Benutzer wird aufgefordert in die Felder \enquote{Passwort} und \enquote{Wiederholen} das gleiche Passwort einzugeben. +\end{description} + +\item +\begin{description} + \item [Stand] Die Registrierungsseite ist geladen. Als E-Mail ist \enquote{pseIstToll@test\-.com}, als Passwort \enquote{Test123!?} und \enquote{test123!?} in das Feld \enquote{Wiederholen} eingetragen. + \item [Aktion] Der Benutzer gibt \enquote{Test123!?} in das Feld \enquote{Wiederholen} ein. Dann klickt der Benutzer auf den Knopf \enquote{Registrieren}. + \item [Reaktion] Dem Benutzer wird angezeigt, dass an die angegebene E-Mail-Adres\-se eine E-Mail mit einem Bestätigungslink versendet wurde. Der Benutzer wird aufgefordert diesen zu Bestätigen um die Registrierung abzuschließen. +\end{description} + +\end{enumerate} + +\item +\begin{description} + \item [Stand] Der Benutzer hat die Registrierung begonnen und die E-Mail mit dem Bestä\-tigungs-Link erhalten. Der Benutzer hat noch nicht auf den Bestätigungs-Link geklickt. + \item [Aktion] Der Benutzer klickt innerhalb von 24 Stunden nach Versendung auf den Bestätigungs-Link. + \item [Reaktion] Der Account wird erstellt und der Benutzer wird auf die Anmeldeseite weitergeleitet. +\end{description} + +\item +\begin{description} + \item [Stand] Der Benutzer hat die Registrierung bereits abgeschlossen. + \item [Aktion] Der Benutzer klickt auf den Bestätigungs-Link. + \item [Reaktion] Der Benutzer erhält eine Fehlermeldung, dass der Link abgelaufen ist. +\end{description} + +\item +\begin{description} + \item [Stand] Der Benutzer hat die Registrierung begonnen und die E-Mail mit dem Bestä\-tigungs-Link erhalten. Die E-Mail wurde vor mehr als 24 Stunden versendet. Der Benutzer hat den Bestätigungs-Link noch nicht angeklickt. + \item [Aktion] Der Benutzer klickt auf den Bestätigungs-Link. + \item [Reaktion] Der Benutzer erhält eine Fehlermeldung, dass der Link abgelaufen ist. Der Account wird nicht erstellt. Der Benutzer wird auf die Anmeldeseite weitergeleitet. +\end{description} + +\item +\begin{description} + \item [Stand] Die Registrierungsseite ist geladen. Es ist bereits ein Account mit der E-Mail-Adresse \enquote{pseIstToll@test.com} registriert. + \item [Aktion] Der Benutzer gibt \enquote{pseIstToll@test.com} als E-Mail-Adresse und \enquote{Test123!?} als Passwort ein. Weiter gibt er das gleiche Passwort in das Feld \enquote{Wiederholen} ein. Dann klickt der Benutzer auf den Knopf \enquote{Registrieren}. + \item [Reaktion] Die Registrierungsseite bleibt geladen. Es wird eine Fehlermeldung ausgegeben, dass diese E-Mail-Adresse bereits vergeben ist.\\ +\end{description} + +\end{enumerate} + +\newpage + + +\subsection{Passwort Anforderungen}\label{t:pw-req} +\vspace{0.3cm} + +\begin{description} + \item [Anmerkung] Die Testfälle sind beispielhaft für den Vorgang der Registrierung entworfen worden. Es wird analoges Verhalten bei den Vorgängen \enquote{Passwort ändern} \ref{t:change-pw} und \enquote{Passwort vergessen} \ref{t:forgot-pw} erwartet. +\end{description} + +\begin{enumerate} + +\item +\begin{description} + \item [Stand] Die Registrierungsseite ist geladen. Als E-Mail-Adresse ist \enquote{pseIstToll@test\-.com} eingetragen. + \item [Aktion] Der Benutzer gibt \enquote{test} in die Felder \enquote{Passwort} und \enquote{Wiederholen} ein. Dann klickt der Benutzer auf den Knopf \enquote{Registrieren}. + \item [Reaktion] Die Registrierungsseite bleibt geladen. Dem Benutzer wird angezeigt das sein Passwort nicht die Passwort-Mindestanforderungen erfüllt. Weiter wird farblich angezeigt, dass sein Passwort nicht: + \color{red} + \begin{itemize} + \item mindestens 8 Zeichen lang ist. + \item mindestens einen Großbuchstaben enthält. + \item mindestens eine Zahl enthält. + \item mindestens ein Sonderzeichen enthält. + \end{itemize} + \color{black} + Dem Benutzer wird farblich angezeigt dass sein Passwort: + \color{ForestGreen} + \begin{itemize} + \item mindestens einen Kleinbuchstaben enthält. + \end{itemize} + \color{black} +\end{description} + +\item +\begin{description} + \item [Stand] Die Registrierungsseite ist geladen. Als E-Mail ist \enquote{pseIstToll@test.com} eingetragen. + \item [Aktion] Der Benutzer gibt \enquote{Test123} in die Felder \enquote{Passwort} und \enquote{Wiederholen} ein. Dann klickt der Benutzer auf den Knopf \enquote{Registrieren}. + \item [Reaktion] Die Registrierungsseite bleibt geladen. Dem Benutzer wird angezeigt das sein Passwort nicht die Passwort-Mindestanforderungen erfüllt. Weiter wird farblich angezeigt, dass sein Passwort nicht: + \color{red} + \begin{itemize} + \item mindestens 8 Zeichen lang ist. + \item mindestens ein Sonderzeichen enthält. + \end{itemize} + \color{black} + Dem Benutzer wird farblich angezeigt dass sein Passwort: + \color{ForestGreen} + \begin{itemize} + \item mindestens einen Kleinbuchstaben enthält. + \item mindestens einen Großbuchstaben enthält. + \item mindestens eine Zahl enthält. + \end{itemize} + \color{black} +\end{description} + +\newpage + +\item +\begin{description} + \item [Stand] Die Registrierungsseite ist geladen. Als E-Mail-Adresse ist \enquote{pseIstToll@test\-.com} eingetragen. + \item [Aktion] Der Benutzer gibt \enquote{TEST123?} in die Felder \enquote{Passwort} und \enquote{Wiederholen} ein. Dann klickt der Benutzer auf den Knopf \enquote{Registrieren}. + \item [Reaktion] Die Registrierungsseite bleibt geladen. Dem Benutzer wird angezeigt das sein Passwort nicht die Passwort-Mindestanforderungen erfüllt. Weiter wird farblich angezeigt, dass sein Passwort nicht: + \color{red} + \begin{itemize} + \item mindestens einen Kleinbuchstaben enthält. + \end{itemize} + \color{black} + Dem Benutzer wird farblich angezeigt dass sein Passwort: + \color{ForestGreen} + \begin{itemize} + \item mindestens 8 Zeichen lang ist. + \item mindestens einen Großbuchstaben enthält. + \item mindestens eine Zahl enthält. + \item mindestens ein Sonderzeichen enthält. + \end{itemize} + \color{black} +\end{description} + +\item +\begin{description} + \item [Stand] Die Registrierungsseite ist geladen. Als E-Mail ist \enquote{pseIstToll@test.com} eingetragen. + \item [Aktion] Der Benutzer gibt \enquote{Test123!?} in die Felder \enquote{Passwort} und \enquote{Wiederholen} ein. Dann klickt der Benutzer auf den Knopf \enquote{Registrieren}. + \item [Reaktion] Dem Benutzer wird angezeigt, dass an die angegebene E-Mail-Adresse eine E-Mail mit einem Bestätigungslink versendet wurde. Der Benutzer wird aufgefordert diesen zu bestätigen um die Registrierung abzuschließen.\\ +\end{description} + +\end{enumerate} + +\newpage + + +\subsection{Anmeldung}\label{t:login} +\vspace{0.3cm} + +\begin{description} + \item [Vorbedingung] Es ist nur ein Benutzer registriert. Dieser hat die E-Mail \enquote{pseIstToll@test\-.com} und das Passwort \enquote{Test123!?}. Die Option \enquote{Angemeldet bleiben} wurde nicht verwendet. +\end{description} + +\begin{enumerate} + +\item +\begin{description} + \item [Stand] Ein Fenster ist im Browser geöffnet. + \item [Aktion] Der Benutzer gibt die URL der Weboberfläche in die Kopfzeile ein und drückt Enter. + \item [Reaktion] Der Browser wechselt zur Anmeldeseite der Weboberfläche. +\end{description} + +\item +\begin{description} + \item [Stand] Die Anmeldeseite mit Anmeldemöglichkeit in der Mitte des Fensters ist im Browser geladen. + \item [Aktion] Der Benutzer macht keine Eingaben und klickt auf den Knopf \enquote{Anmelden}. + \item [Reaktion] Die Anmeldeseite bleibt geladen. Der Benutzer wird aufgefordert die Felder \enquote{E-Mail-Adresse} und \enquote{Passwort} auszufüllen. +\end{description} + +\item +\begin{description} + \item [Stand] Die Anmeldeseite ist im Browser geladen. + \item [Aktion] Der Benutzer gibt als E-Mail-Adresse \enquote{pseIstToll@test.com} ein aber gibt kein Passwort ein. Dann klickt der Benutzer auf den Knopf \enquote{Anmelden}. + \item [Reaktion] Die Anmeldeseite bleibt geladen. Im E-Mail-Feld ist \enquote{pseIstToll@test\-.com} eingetragen. Der Benutzer wird aufgefordert das Passwort-Feld auszufüllen. +\end{description} + +\item +\begin{description} + \item [Stand] Die Anmeldeseite ist im Browser geladen. + \item [Aktion] Der Benutzer gibt in das \enquote{E-Mail}-Feld \enquote{pseIstSuperToll@test.com} ein und gibt \enquote{Test123!?} als Passwort ein. Dann klickt der Benutzer auf den Knopf \enquote{Anmelden}. + \item [Reaktion] Die Anmeldeseite bleibt geladen. Als E-Mail ist \enquote{pseIstSuperToll@test\-.com} und als Passwort \enquote{Test123!?} eingetragen. Dem Benutzer wird eine Fehlermeldung ausgegeben: \enquote{E-Mail-Adresse oder Passwort ist ungültig!} +\end{description} + +\item +\begin{description} + \item [Stand] Die Anmeldeseite ist im Browser geladen. + \item [Aktion] Der Benutzer gibt in das \enquote{E-Mail}-Feld \enquote{pseIstToll@test.com} ein und gibt \enquote{test} als Passwort ein. Der Benutzer wählt die Option \enquote{Angemeldet bleiben} aus. Dann klickt der Benutzer auf den Knopf \enquote{Anmelden}. + \item [Reaktion] Die Anmeldeseite bleibt geladen. Als E-Mail ist \enquote{pseIstToll@test.com} und als Passwort \enquote{test} eingetragen. Die Option \enquote{Angemeldet bleiben} ist ausgewählt. Dem Benutzer wird eine Fehlermeldung ausgegeben: \enquote{E-Mail-Adresse oder Passwort ist ungültig!} +\end{description} + +\newpage + +\item +\begin{description} + \item [Stand] Die Anmeldeseite ist im Browser geladen. Als E-Mail ist \enquote{pseIstToll@test\-.com} und als Passwort \enquote{test} eingetragen. Die Option \enquote{Angemeldet bleiben} ist ausgewählt. + \item [Aktion] Der Benutzer gibt \enquote{Test123!?} als Passwort ein. Dann klickt der Benutzer auf den Knopf \enquote{Anmelden}. + \item [Reaktion] Der Browser wechselt zum \Gls{dashboard}. +\end{description} + +\item +\begin{description} + \item [Stand] Der Benutzer hat sich erfolgreich angemeldet. Bei der Anmeldung wurde die Option \enquote{Angemeldet bleiben} ausgewählt. + \item [Aktion] Der Benutzer startet seinen Browser neu. Dann gibt er die URL der Weboberfläche in der Kopfzeile ein und drückt Enter. + \item [Reaktion] Der Benutzer wird automatisch angemeldet und das + \Gls{dashboard} geladen. +\end{description} + +\item +\begin{description} + \item [Stand] Der Benutzer hat sich erfolgreich angemeldet. Bei der Anmeldung wurde die nicht Option \enquote{Angemeldet bleiben} ausgewählt. + \item [Aktion] Der Benutzer startet seinen Browser neu. Dann gibt er die URL der Weboberfläche in der Kopfzeile ein und drückt Enter. + \item [Reaktion] Der Browser wechselt zur Anmeldeseite. Der Benutzer wird nicht automatisch angemeldet.\\ +\end{description} + +\end{enumerate} + +\newpage + + +\subsection{Passwort ändern}\label{t:change-pw} +\vspace{0.3cm} + +\begin{description} + \item [Vorbedingung] Es gibt einen Benutzer mit der E-Mail \enquote{pseIstToll@test.com} und dem Passwort \enquote{Test123!?}. +\end{description} + +\begin{enumerate} + +\item +\begin{description} + \item [Stand] Der Benutzer ist angemeldet. Das \Gls{dashboard} ist im Browser geladen. + \item [Aktion] Der Benutzer klickt auf den Profil-Knopf oben rechts (Pfeil nach unten neben dem Männchen). Dann klickt er in dem geöffneten Menu oben rechts auf den Knopf \enquote{Einstellungen}. + \item [Reaktion] Der Browser wechselt zur \enquote{Einstellungs}-Oberfläche. +\end{description} + +\item +\begin{description} + \item [Stand] Der Benutzer ist angemeldet. Die \enquote{Einstellungs}-Oberfläche ist im Browser geladen. + \item [Aktion] Der Benutzer macht keine Eingaben und klickt auf den Knopf \enquote{Passwort ändern}. + \item [Reaktion] Die \enquote{Einstellungs}-Oberfläche bleibt geladen. Der Benutzer wird aufgefordert die Felder \enquote{Altes Passwort}, \enquote{Neues Passwort} und \enquote{Wiederholen} auszufüllen. +\end{description} + +\item +\begin{description} + \item [Stand] Der Benutzer ist angemeldet. Die \enquote{Einstellungs}-Oberfläche ist im Browser geladen. + \item [Aktion] Der Benutzer gibt \enquote{test} als altes Passwort ein. Weiter gibt er in die Felder \enquote{Neues Passwort} und \enquote{Wiederholen} \enquote{NeuerTest123!?} ein. Dann klickt er auf den Knopf \enquote{Passwort ändern}. + \item [Reaktion] Die \enquote{Einstellungs}-Oberfläche bleibt geladen. Der Benutzer bekommt eine Fehlermeldung, dass das alte Passwort falsch ist. In den Feldern \enquote{Neues Passwort} und \enquote{Wiederholen} steht weiter \enquote{NeuerTest123!?}. +\end{description} + +\item +\begin{description} + \item [Stand] Der Benutzer ist angemeldet. Die \enquote{Einstellungs}-Oberfläche ist im Browser geladen. + \item [Aktion] Der Benutzer gibt \enquote{Test123!?} als altes Passwort ein. Weiter gibt er in die Felder \enquote{Neues Passwort} und \enquote{Wiederholen} \enquote{neuerTest} ein. Dann klickt er auf den Knopf \enquote{Passwort ändern}. + \item [Reaktion] Die \enquote{Einstellungs}-Oberfläche bleibt geladen. Dem Benutzer wird angezeigt das sein neues Passwort nicht die Passwort-Mindestanforderungen erfüllt. Weiter wird farblich angezeigt, dass sein Passwort nicht: + \color{red} + \begin{itemize} + \item mindestens eine Zahl enthält. + \item mindestens ein Sonderzeichen enthält. + \end{itemize} + \color{black} + Dem Benutzer wird farblich angezeigt, dass sein Passwort: + \color{ForestGreen} + \begin{itemize} + \item mindestens 8 Zeichen lang ist. + \item mindestens einen Kleinbuchstaben enthält. + \item mindestens einen Großbuchstaben enthält. + \end{itemize} + \color{black} +\end{description} + +\item +\begin{description} + \item [Stand] Der Benutzer ist angemeldet. Die \enquote{Einstellungs}-Oberfläche ist im Browser geladen. + \item [Aktion] Der Benutzer gibt in das \enquote{Altes Passwort}-Feld \enquote{Test123!?} ein. Er gibt in das \enquote{Neues Passwort}-Feld und in das \enquote{Wiederholen}-Feld \enquote{NeuerTest123!?} ein. Dann klickt er auf den Knopf \enquote{Passwort ändern}. + \item [Reaktion] Die \enquote{Einstellungs}-Oberfläche bleibt geladen. Die Felder \enquote{Altes Passwort}, \enquote{Neues Passwort} und \enquote{Wiederholen} werden geleert. Dem Benutzer wird angezeigt, dass das Passwort erfolgreich geändert wurde. +\end{description} + +\item +\begin{description} + \item [Stand] Der Benutzer ist angemeldet. Die \enquote{Einstellungs}-Oberfläche ist im Browser geladen. Sein Passwort wurde zu \enquote{NeuerTest123!?} geändert. + \item [Aktion] Der Benutzer meldet sich ab. + \item [Reaktion] Der Browser wechselt zur Anmeldeseite. Der Benutzer ist abgemeldet. +\end{description} + +\item +\begin{description} + \item [Stand] Der Benutzer ist abgemeldet. Die Anmeldeseite ist geladen. + \item [Aktion] Der Benutzer gibt als E-Mail \enquote{pseIstToll@test.com} und \enquote{Test123!?} als Passwort ein. Dann klickt der Benutzer auf den Knopf \enquote{Anmelden}. + \item [Reaktion] Die Anmeldeseite bleibt geladen. Als E-Mail ist \enquote{pseIstToll@test.com} eingetragen und als Passwort ist \enquote{Test123!?} eingetragen. Dem Benutzer wird per Fehlermeldung ausgegeben, dass E-Mail-Adresse oder Passwort ungültig sind. +\end{description} + +\item +\begin{description} + \item [Stand] Der Benutzer ist abgemeldet. Die Anmeldeseite ist geladen. + \item [Aktion] Der Benutzer gibt als E-Mail \enquote{pseIstToll@test.com} und \enquote{NeuerTest123!?} als Passwort ein. Dann klickt der Benutzer auf den Knopf \enquote{Anmelden}. + \item [Reaktion] Der Browser wechselt zum \Gls{dashboard}.\\ +\end{description} + +\end{enumerate} + +\newpage + + +\subsection{Passwort vergessen}\label{t:forgot-pw} +\vspace{0.3cm} + +\begin{description} + \item [Vorbedingung] Es gibt nur einen registrierten Benutzer. Dieser hat die E-Mail \enquote{pseIstToll@test.com} und \enquote{Test123!?} als Passwort. +\end{description} + +\begin{enumerate} + +\item +\begin{description} + \item [Stand] Die Anmeldeseite ist im Browser geladen. + \item [Aktion] Der Benutzer klickt auf den Knopf \enquote{Passwort vergessen?}. + \item [Reaktion] Der Browser wechselt zur \enquote{Passwort vergessen}-Seite. +\end{description} + +\item +\begin{description} + \item [Stand] Die Passwort-Vergessen-Seite ist im Browser geladen. + \item [Aktion] Der Benutzer gibt in das \enquote{E-Mail}-Feld \enquote{pseIstSuperToll@test.com} ein. Dann klickt der Benutzer auf den Knopf \enquote{Bestätigen}. + \item [Reaktion] Die \enquote{Passwort zurücksetzen}-Seite bleibt geladen. Dem Benutzer wird eine Fehlermeldung angezeigt, dass diese E-Mail-Adresse nicht vergeben ist. +\end{description} + +\item +\begin{description} + \item [Stand] Die Passwort-Vergessen-Seite ist im Browser geladen. + \item [Aktion] Der Benutzer gibt in das \enquote{E-Mail}-Feld \enquote{pseIstToll@test.com} ein. Der Benutzer klickt auf den Knopf \enquote{Bestätigen}. + \item [Reaktion] Der Benutzer wird darüber benachrichtigt, dass an die angegebene E-Mail-Adresse eine E-Mail mit einem Link zum Zurücksetzen des Passworts versendet wurde. +\end{description} + +\item +\begin{description} + \item [Stand] Der Benutzer hat die E-Mail mit dem Link zum Zurücksetzen des Passworts erhalten und sein Passwort noch nicht zurückgesetzt. + \item [Aktion] Der Benutzer klickt innerhalb von 24 Stunden nach Versendung auf den Link zum Passwort Zurücksetzen. + \item [Reaktion] Der Browser wechselt zur \enquote{Passwort zurücksetzen}-Seite. +\end{description} + +\item +\begin{description} + \item [Stand] Im Browser ist die \enquote{Passwort zurücksetzen}-Seite geladen. + \item [Aktion] Der Benutzer gibt in die Felder \enquote{Neues Passwort} und \enquote{Wiederholen} \enquote{neuerTest} ein. Dann klickt er auf den Knopf \enquote{Passwort zurücksetzen}. + \item [Reaktion] Die \enquote{Passwort zurücksetzen}-Seite bleibt geladen. Dem Benutzer wird angezeigt das sein neues Passwort nicht die Passwort-Mindestanforderungen erfüllt. Weiter wird farblich angezeigt, dass sein Passwort nicht: + \color{red} + \begin{itemize} + \item mindestens eine Zahl enthält. + \item mindestens ein Sonderzeichen enthält. + \end{itemize} + \color{black} + Dem Benutzer wird farblich angezeigt, dass sein Passwort: + \color{ForestGreen} + \begin{itemize} + \item mindestens 8 Zeichen lang ist. + \item mindestens einen Kleinbuchstaben enthält. + \item mindestens einen Großbuchstaben enthält. + \end{itemize} + \color{black} +\end{description} + +\newpage + +\item +\begin{description} + \item [Stand] Im Browser ist die \enquote{Passwort zurücksetzen}-Seite geladen. + \item [Aktion] Der Benutzer gibt in das \enquote{Neues Passwort}-Feld und in das \enquote{Wiederholen}-Feld \enquote{NeuerTest123!?} ein. Dann klickt der Benutzer auf den Knopf \enquote{Passwort zurücksetzen}. + \item [Reaktion] Der Browser wechselt zur Anmeldeseite. Der verwendete Link zum zurücksetzen des Passworts wird ungültig. +\end{description} + +\item +\begin{description} + \item [Stand] Im Browser ist die Anmeldeseite geladen. + \item [Aktion] Der Benutzer gibt in das \enquote{E-Mail}-Feld \enquote{pseIstToll@test.com} und \enquote{Test\-123!?} als Passwort ein. + \item [Reaktion] Die Anmeldeseite bleibt geladen. Der Benutzer bekommt eine Fehlermeldung, dass die E-Mail oder das Passwort ungültig ist. +\end{description} + +\item +\begin{description} + \item [Stand] Im Browser ist die Anmeldeseite geladen. + \item [Aktion] Der Benutzer gibt in das \enquote{E-Mail}-Feld \enquote{pseIstToll@test.com} und \enquote{NeuerTest123!?} als Passwort ein. + \item [Reaktion] Der Browser wechselt zum \Gls{dashboard}. +\end{description} + +\item +\begin{description} + \item [Stand] Der Benutzer hat die E-Mail mit dem Link zum Zurücksetzen des Passworts erhalten und sein Passwort noch nicht zurückgesetzt. Die E-Mail wurde vor mehr als 24 Stunden versendet. + \item [Aktion] Der Benutzer klickt auf den Link zum Passwort Zurücksetzen. + \item [Reaktion] Der Benutzer bekommt eine Fehlermeldung, dass der Link abgelaufen ist. Das Passwort wird nicht zurückgesetzt. Der Benutzer wird auf die Anmeldeseite weitergeleitet. +\end{description} + +\item +\begin{description} + \item [Stand] Der Benutzer hat die E-Mail mit dem Link zum Zurücksetzen des Passworts erhalten und sein Passwort bereits über diesen Link zurückgesetzt. + \item [Aktion] Der Benutzer klickt erneut auf den Link zum Passwort Zurücksetzen. + \item [Reaktion] Der Benutzer bekommt eine Fehlermeldung, dass der Link abgelaufen ist. Das Passwort wird nicht zurückgesetzt. Der Benutzer wird auf die Anmeldeseite weitergeleitet.\\ +\end{description} + +\end{enumerate} + +\newpage + + +\subsection{Account Löschen}\label{t:delete-acc} +\vspace{0.3cm} + +\begin{description} + \item [Vorbedingung] Es gibt den registrierten Benutzer mit der E-Mail \enquote{pseIstToll@test.com} und \enquote{Test123!?} als Passwort. +\end{description} + +\begin{enumerate} + +\item +\begin{description} + \item [Stand] Der Benutzer ist angemeldet. Im Browser ist die Einstellungsseite geladen. Möglichkeit zum Löschen des Accounts unten links. + \item [Aktion] Der Benutzer klickt auf den Knopf \enquote{Account löschen}. + \item [Reaktion] Der Browser wechselt zur Account-Löschen-Seite. Auf dieser wird der Benutzer aufgefordert sein Passwort einzugeben um den Vorgang des Löschens zu Bestätigen. +\end{description} + +\item +\begin{description} + \item [Stand] Die Account-Löschen-Seite ist im Browser geladen. + \item [Aktion] Der Benutzer macht keine Eingaben und klickt auf den Knopf \enquote{Account löschen bestätigen}. + \item [Reaktion] Die Account-Löschen-Seite bleibt geladen. Dem Benutzer wird eine Fehlermeldung angezeigt, dass er um die Account Löschung zu bestätigen sein Passwort eingeben muss. +\end{description} + +\item +\begin{description} + \item [Stand] Die Account-Löschen-Seite ist im Browser geladen. + \item [Aktion] Der Benutzer gibt \enquote{test123!?} als Passwort ein und klickt auf den Knopf \enquote{Account löschen bestätigen}. + \item [Reaktion] Es bleibt die Account-Löschen-Seite geladen. Dem Benutzer wird eine Fehlermeldung angezeigt, dass das Passwort falsch ist. +\end{description} + +\item +\begin{description} + \item [Stand] Die Account-Löschen-Seite ist im Browser geladen. + \item [Aktion] Der Benutzer gibt \enquote{Test123!?} als Passwort ein und klickt auf den Knopf \enquote{Account löschen bestätigen}. + \item [Reaktion] Der Account des Benutzers wird zusammen mit allen dazugehörigen gespeicherten Daten gelöscht. Der Browser wechselt zur Anmeldeseite. +\end{description} + +\item +\begin{description} + \item [Stand] Die Anmeldeseite ist im Browser geladen. + \item [Aktion] Der Benutzer gibt als E-Mail \enquote{pseIstToll@test.com} und \enquote{Test123!?} als Passwort ein. Dann klickt er auf den \enquote{Anmelden} Knopf. + \item [Reaktion] Die Anmeldeseite bleibt geladen und dem Benutzer wird eine Fehlermeldung angezeigt: \enquote{E-Mail-Adresse oder Passwort ist ungültig!} +\end{description} + +\item +\begin{description} + \item [Stand] Die Account-Löschen-Seite ist im Browser geladen. + \item [Aktion] Der Benutzer klickt auf den \enquote{Abbrechen} Knopf. + \item [Reaktion] Der Account wird nicht gelöscht. Der Browser wechselt zur Einstellungsseite. +\end{description} + +\end{enumerate} + +\newpage + + +\subsection{Synchronisation: Podcast abonnieren}\label{t:sync-sub} +\vspace{0.3cm} + +\begin{enumerate} + +\item +\begin{description} + \item [Stand] Der Benutzer ist in einem synchronisierten \Gls{podcatcher} angemeldet. + \item [Aktion] Der Benutzer fügt den \Gls{podcast} \enquote{Der tagesschau + Zukunfts-Podcast: mal angenommen} zu seinen \Glspl{abo} hinzu. + \item [Reaktion] Der \Gls{podcatcher} synchronisiert das neue \Gls{abo}. +\end{description} + +\item +\begin{description} + \item [Stand] Dem Benutzer wird die \enquote{Zuletzt gehört}-Oberfläche angezeigt. + \item [Aktion] Der Benutzer klickt auf den Reiter \enquote{Podcasts}. + \item [Reaktion] Der Browser wechselt zur \enquote{Podcasts}-Oberfläche. + Diese zeigt eine Liste aller abonnierten \Glspl{podcast}. Der oberste + Eintrag ist der \Gls{podcast} \enquote{Der tagesschau Zukunfts-Podcast: + mal angenommen}. +\end{description} + +\item +\begin{description} + \item [Stand] Der Benutzer meldet sich über ein anderes Gerät in einem + synchronisierten \Gls{podcatcher} an. + \item [Aktion] Der Benutzer lässt sich im \Gls{podcatcher} die Liste seiner + \Glspl{abo} anzeigen. + \item [Reaktion] Nach kurzer Zeit oder durch manuelles Synchronisieren wird + dem Benutzer dort ebenfalls die aktualisierte Liste der \Glspl{abo} + angezeigt. Diese enthält insbesondere den Eintrag mit dem \Gls{podcast} \enquote{Der tagesschau Zukunfts-Podcast: mal angenommen}.\\ +\end{description} + +\end{enumerate} + + +\subsection{Synchronisation: Podcast deabonnieren}\label{t:sync-unsub} +\vspace{0.3cm} + +\begin{description} + \item [Vorbedingung] Der Benutzer hat den \Gls{podcast} \enquote{Der tagesschau Zukunfts-Podcast: mal angenommen} abonniert. +\end{description} + +\begin{enumerate} + +\item +\begin{description} + \item [Stand] Der Benutzer ist in einem synchronisierten \Gls{podcatcher} angemeldet. + \item [Aktion] Der Benutzer deabonniert den \Gls{podcast} \enquote{Der tagesschau Zukunfts-Podcast: mal angenommen}. + \item [Reaktion] Der \Gls{podcatcher} synchronisiert die reduzierte Liste + der \Glspl{abo}. +\end{description} + +\item +\begin{description} + \item [Stand] Die \enquote{Zuletzt gehört}-Oberfläche ist im Browser geladen. + \item [Aktion] Der Benutzer klickt auf den Reiter \enquote{Podcasts}. + \item [Reaktion] Der Browser wechselt zur \enquote{Podcasts}-Oberfläche. + Diese zeigt die aktuelle Liste aller abonnierten \Glspl{podcast}. Der Eintrag \enquote{Der tagesschau Zukunfts-Podcast: mal angenommen} ist nicht mehr in der Liste enthalten.\\ +\end{description} + +\end{enumerate} + +\newpage + +\subsection{Synchronisation: \Gls{episode} anhören}\label{t:sync-episode} +\vspace{0.3cm} + +\begin{enumerate} + +\item +\begin{description} + \item [Stand] Der Benutzer ist in einem synchronisierten \Gls{podcatcher} angemeldet. + \item [Aktion] Der Benutzer hört sich die \Gls{episode} \enquote{Kein Handel mit + China? Was dann?} des \Glspl{podcast} \enquote{Der tagesschau Zukunfts-Podcast: mal angenommen} bis zum Zeitpunkt 7 Minuten 19 Sekunden an. Dann beendet er die Wiedergabe. + \item [Reaktion] Der \Gls{podcatcher} synchronisiert den Fortschritt beim + Anhören der \Gls{episode}. +\end{description} + +\item +\begin{description} + \item [Stand] Die \enquote{Podcasts}-Oberfläche ist im Browser geladen. + \item [Aktion] Der Benutzer klickt auf den Reiter \enquote{Zuletzt gehört}. + \item [Reaktion] Der Browser wechselt zur \enquote{Zuletzt + gehört}-Oberfläche. Diese zeigt eine Liste aller angefangenen, aber + nicht beendeten \Glspl{episode} mit korrespondierendem Hörfortschritt + an. Die Liste ist nach der Aktualität des Anhörens oder Ansehens einer + \Gls{episode} sortiert. Der oberste Eintrag ist die \Gls{episode} \enquote{Kein Handel mit China? Was dann?} mit dem Fortschritt 7 Minuten 19 Sekunden. +\end{description} + +\item +\begin{description} + \item [Stand] Der Benutzer meldet sich über ein anderes Gerät in einem + synchronisierten \Gls{podcatcher} an. + \item [Aktion] Der Benutzer hört sich die \Gls{episode} \enquote{Kein Handel mit + China? Was dann?} des \Glspl{podcast} \enquote{Der tagesschau Zukunfts-Podcast: mal angenommen} bis zum Zeitpunkt 4 Minuten 30 Sekunden an. Dann beendet er die Wiedergabe. + \item [Reaktion] Der \Gls{podcatcher} synchronisiert den Fortschritt beim + Anhören der \Gls{episode}. +\end{description} + +\item +\begin{description} + \item [Stand] Die \enquote{Podcasts}-Oberfläche ist im Browser geladen. + \item [Aktion] Der Benutzer klickt auf den Reiter \enquote{Zuletzt gehört}. + \item [Reaktion] Der Browser wechselt zur \enquote{Zuletzt + gehört}-Oberfläche. Der oberste Eintrag der \enquote{Zuletzt + gehört}-Liste ist die \Gls{episode} \enquote{Kein Handel mit China? Was dann?} mit dem Fortschritt 4 Minuten 30 Sekunden.\\ +\end{description} + +\end{enumerate} + + +\subsection{Benutzeranleitung anzeigen}\label{t:man} +\vspace{0.3cm} + +\begin{description} + \item [Stand] Der Benutzer ist im Webfrontend angemeldet. + \item [Aktion] Der Benutzer klickt auf den \enquote{Hilfe}-Button oben rechts (Abbildung \ref{fig:help-desktop}). + \item [Reaktion] Dem Benutzer wird ein leeres \enquote{Hilfe}-Fenster + angezeigt. (Dieses kann nachträglich um Hilfestellungen zum + Synchronisieren von \Glspl{podcast} ergänzt werden. \ref{r:no-man})\\ +\end{description} + + +\subsection{Lasttest}\label{t:lasttest} +\vspace{0.3cm} +Es werden Lasttests zur Überprüfung der Leitungsfähigkeit des Synchronisa\-tions-Server Systems durchgeführt. +Dabei müssen über einen längeren Zeitraum mindestens 50 Anfragen pro Sekunde verarbeitet werden. +Die Antwortzeit pro Anfrage darf dabei nicht mehr als 500 Millisekunden betragen. +Der Test gilt als bestanden, wenn diese Anforderungen erfüllt sind. + diff --git a/00-pflichtenheft/tikz-uml.sty b/00-pflichtenheft/tikz-uml.sty new file mode 100644 index 0000000..c6e8e0d --- /dev/null +++ b/00-pflichtenheft/tikz-uml.sty @@ -0,0 +1,5377 @@ +%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% Start of tikz-uml.sty +% +% Some macros for UML Diagrams. +% Home page of project : +% Author: Nicolas Kielbasiewicz +% Style from : +% Fixed by Nicolas Kielbasiewicz (nicolas.kielbasiewicz@ensta-paristech.fr) in march 2016 to compile with pgf 3.00 +%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +\NeedsTeXFormat{LaTeX2e}[1995/12/01]% +\ProvidesPackage{tikz-uml}[2011/01/26]% +% +\RequirePackage{etoolbox}% +\RequirePackage{ifthen}% +\RequirePackage{tikz}% +\RequirePackage{xstring}% +\RequirePackage{calc}% +\RequirePackage{pgfopts}% +\usetikzlibrary{backgrounds,arrows,shapes,fit,shadows,decorations.markings}% +% +\def\tikzumlPackageLayersNum{3}% +\pgfkeys{/tikzuml/options/.cd, packageLayers/.initial=3}% +\pgfkeys{/tikzuml/options/.cd, packageLayers/.store in=\tikzumlPackageLayersNum}% +\def\tikzumlStateLayersNum{3}% +\pgfkeys{/tikzuml/options/.cd, stateLayers/.initial=3}% +\pgfkeys{/tikzuml/options/.cd, stateLayers/.store in=\tikzumlStateLayersNum}% +\def\tikzumlFragmentLayersNum{3}% +\pgfkeys{/tikzuml/options/.cd, fragmentLayers/.initial=3}% +\pgfkeys{/tikzuml/options/.cd, fragmentLayers/.store in=\tikzumlFragmentLayersNum}% +\def\tikzumlComponentLayersNum{3}% +\pgfkeys{/tikzuml/options/.cd, componentLayers/.initial=3}% +\pgfkeys{/tikzuml/options/.cd, componentLayers/.store in=\tikzumlComponentLayersNum}% +% +\ProcessPgfOptions{/tikzuml/options}% +% +\def\pgfsetlayersArg{background}% +\pgfdeclarelayer{background}% +\newcounter{tikzumlPackageLayers}% +\loop \pgfdeclarelayer{package\thetikzumlPackageLayers}% + \xdef\pgfsetlayersArg{\pgfsetlayersArg,package\thetikzumlPackageLayers}% + \ifnum\tikzumlPackageLayersNum>\thetikzumlPackageLayers% + \stepcounter{tikzumlPackageLayers}% +\repeat% +% +\newcounter{tikzumlFragmentLayers}% +\loop \pgfdeclarelayer{fragment\thetikzumlFragmentLayers}% + \xdef\pgfsetlayersArg{\pgfsetlayersArg,fragment\thetikzumlFragmentLayers}% + \ifnum\tikzumlFragmentLayersNum>\thetikzumlFragmentLayers% + \stepcounter{tikzumlFragmentLayers}% +\repeat% +% +\newcounter{tikzumlStateLayers}% +\loop \pgfdeclarelayer{state\thetikzumlStateLayers}% + \xdef\pgfsetlayersArg{\pgfsetlayersArg,state\thetikzumlStateLayers}% + \ifnum\tikzumlStateLayersNum>\thetikzumlStateLayers% + \stepcounter{tikzumlStateLayers}% +\repeat% +% +\newcounter{tikzumlComponentLayers}% +\loop \pgfdeclarelayer{component\thetikzumlComponentLayers}% + \xdef\pgfsetlayersArg{\pgfsetlayersArg,component\thetikzumlComponentLayers}% + \ifnum\tikzumlComponentLayersNum>\thetikzumlComponentLayers% + \stepcounter{tikzumlComponentLayers}% +\repeat% +% +\pgfdeclarelayer{lifelines}% +\pgfdeclarelayer{activity}% +\pgfdeclarelayer{connections}% +\xdef\pgfsetlayersArg{\pgfsetlayersArg,lifelines,activity,connections,main}% +\pgfsetlayers{\pgfsetlayersArg}% +% +\pgfkeys{/tikzuml/.cd, text/.initial=black, draw/.initial=black, font/.initial=\small,% + x/.initial=0, y/.initial=0,% + package type/.initial=tikzumlEmpty, fill package/.initial=blue!20,% + class width/.initial=10ex, simple interface width/.initial=4ex, class type/.initial=class, fill class/.initial=yellow!20, fill template/.initial=yellow!2,% + narynode width/.initial=6ex,% + relation geometry/.initial=--, relation angle1/.initial=-30, relation angle2/.initial=30, relation loopsize/.initial=3em, relation weight/.initial=0.5, relation pos1/.initial=0.2, relation pos2/.initial=0.8, relation pos stereo/.initial=0.5,% + note width/.initial=3cm, fill note/.initial=green!20,% + fill system/.initial=white,% + fill usecase/.initial=blue!20,% + actor below/.initial=0.5cm,% + state join width/.initial=3ex,% + state decision width/.initial=3ex,% + state initial width/.initial=5ex,% + state final width/.initial=5.5ex,% + state enter width/.initial=5ex,% + state exit width/.initial=5ex,% + state end width/.initial=5ex,% + state history width/.initial=5ex,% + state deep history width/.initial=5ex,% + state width/.initial=8ex, fill state/.initial=yellow!20,% + object stereo/.initial=object, fill object/.initial=yellow!20,% + call dt/.initial=tikzumlEmpty, call padding/.initial=2, call type/.initial=synchron, fill call/.initial=white,% + fragment type/.initial=opt, fragment inner xsep/.initial=1, fragment inner ysep/.initial=1, fill fragment/.initial= none,% + create call dt/.initial=4,% + component width/.initial=8ex, fill component/.initial= yellow!20,% + required interface distance/.initial=2.5cm, required interface width/.initial=1em, required interface padding/.initial=1cm,% + provided interface distance/.initial=3cm, provided interface width/.initial=1em, provided interface padding/.initial=1cm,% + port width/.initial=1ex, fill port/.initial= yellow!20,% + fill assembly connector/.initial= white,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \errmessage{TIKZUML ERROR : in tikzuml global, invalid option \keyname}% + }% +}% +% +\pgfkeys{/tikzuml/.cd, text/.get=\tikzumlDefaultTextColor, draw/.get=\tikzumlDefaultDrawColor, font/.get=\tikzumlDefaultFont,% + x/.get=\tikzumlDefaultX, y/.get=\tikzumlDefaultY,% + package type/.get=\tikzumlPackageDefaultType, fill package/.get=\tikzumlPackageDefaultFillColor,% + class width/.get=\tikzumlClassDefaultWidth, simple interface width/.get=\tikzumlSimpleInterfaceDefaultWidth, class type/.get=\tikzumlClassDefaultType, fill class/.get=\tikzumlClassDefaultFillColor, fill template/.get=\tikzumlClassTemplateFillColorDefaultFillColor,% + narynode width/.get=\tikzumlNaryNodeDefaultWidth,% + relation geometry/.get=\tikzumlRelationDefaultGeometry, relation angle1/.get=\tikzumlRelationDefaultAngleO, relation angle2/.get=\tikzumlRelationDefaultAngleT, relation loopsize/.get=\tikzumlRelationDefaultLoopSize, relation weight/.get=\tikzumlRelationDefaultWeight, relation pos1/.get=\tikzumlRelationDefaultPosO, relation pos2/.get=\tikzumlRelationDefaultPosT, relation pos stereo/.get=\tikzumlRelationDefaultPosStereo,% + note width/.get=\tikzumlNoteDefaultWidth, fill note/.get=\tikzumlNoteDefaultFillColor,% + fill system/.get=\tikzumlSystemDefaultFillColor,% + fill usecase/.get=\tikzumlUseCaseDefaultFillColor,% + actor below/.get=\tikzumlActorDefaultBelow,% + state join width/.get=\tikzumlStateJoinDefaultWidth,% + state decision width/.get=\tikzumlStateDecisionDefaultWidth,% + state initial width/.get=\tikzumlStateInitialDefaultWidth,% + state final width/.get=\tikzumlStateFinalDefaultWidth,% + state enter width/.get=\tikzumlStateEnterDefaultWidth,% + state exit width/.get=\tikzumlStateExitDefaultWidth,% + state end width/.get=\tikzumlStateEndDefaultWidth,% + state history width/.get=\tikzumlStateHistoryDefaultWidth,% + state deep history width/.get=\tikzumlStateDeepHistoryDefaultWidth,% + state width/.get=\tikzumlStateDefaultWidth, fill state/.get=\tikzumlStateDefaultFillColor,% + object stereo/.get=\tikzumlObjectDefaultStereo, fill object/.get=\tikzumlObjectDefaultFillColor,% + call dt/.get=\tikzumlCallDefaultDT, call padding/.get=\tikzumlCallDefaultPadding, call type/.get=\tikzumlCallDefaultType, fill call/.get=\tikzumlCallDefaultFillColor,% + fragment type/.get=\tikzumlFragmentDefaultType, fragment inner xsep/.get=\tikzumlFragmentDefaultXSep, fragment inner ysep/.get=\tikzumlFragmentDefaultYSep, fill fragment/.get=\tikzumlFragmentDefaultFillColor,% + create call dt/.get=\tikzumlCreateCallDefaultDT,% + component width/.get=\tikzumlComponentDefaultWidth, fill component/.get=\tikzumlComponentDefaultFillColor,% + required interface distance/.get=\tikzumlRequiredInterfaceDefaultDistance, required interface width/.get=\tikzumlRequiredInterfaceDefaultWidth, required interface padding/.get=\tikzumlRequiredInterfaceDefaultPadding,% + provided interface distance/.get=\tikzumlProvidedInterfaceDefaultDistance, provided interface width/.get=\tikzumlProvidedInterfaceDefaultWidth, provided interface padding/.get=\tikzumlProvidedInterfaceDefaultPadding,% + port width/.get=\tikzumlPortDefaultWidth, fill port/.get=\tikzumlPortDefaultFillColor,% + fill assembly connector/.get=\tikzumlAssemblyConnectorDefaultFillColor% +}% +% +% utility : change default colors +\newcommand{\tikzumlset}[1]{% + \pgfkeys{/tikzuml/.cd,#1}% + \pgfkeys{/tikzuml/.cd, text/.get=\tikzumlDefaultTextColor, draw/.get=\tikzumlDefaultDrawColor, font/.get=\tikzumlDefaultFont,% + x/.get=\tikzumlDefaultX, y/.get=\tikzumlDefaultY,% + package type/.get=\tikzumlPackageDefaultType, fill package/.get=\tikzumlPackageDefaultFillColor,% + class width/.get=\tikzumlClassDefaultWidth, simple interface width/.get=\tikzumlSimpleInterfaceDefaultWidth, class type/.get=\tikzumlClassDefaultType, fill class/.get=\tikzumlClassDefaultFillColor, fill template/.get=\tikzumlClassTemplateFillColorDefaultFillColor,% + narynode width/.get=\tikzumlNaryNodeWidth,% + relation geometry/.get=\tikzumlRelationDefaultGeometry, relation angle1/.get=\tikzumlRelationDefaultAngleO, relation angle2/.get=\tikzumlRelationDefaultAngleT, relation loopsize/.get=\tikzumlRelationDefaultLoopSize, relation weight/.get=\tikzumlRelationDefaultWeight, relation pos1/.get=\tikzumlRelationDefaultPosO, relation pos2/.get=\tikzumlRelationDefaultPosT, relation pos stereo/.get=\tikzumlRelationDefaultPosStereo,% + note width/.get=\tikzumlNoteDefaultWidth, fill note/.get=\tikzumlNoteDefaultFillColor,% + fill system/.get=\tikzumlSystemDefaultFillColor,% + fill usecase/.get=\tikzumlUseCaseDefaultFillColor,% + actor below/.get=\tikzumlActorDefaultBelow,% + state join width/.get=\tikzumlStateJoinDefaultWidth,% + state decision width/.get=\tikzumlStateDecisionDefaultWidth,% + state initial width/.get=\tikzumlStateInitialDefaultWidth,% + state final width/.get=\tikzumlStateFinalDefaultWidth,% + state enter width/.get=\tikzumlStateEnterDefaultWidth,% + state exit width/.get=\tikzumlStateExitDefaultWidth,% + state end width/.get=\tikzumlStateEndDefaultWidth,% + state history width/.get=\tikzumlStateHistoryDefaultWidth,% + state deep history width/.get=\tikzumlStateDeepHistoryDefaultWidth,% + state width/.get=\tikzumlStateDefaultWidth, fill state/.get=\tikzumlStateDefaultFillColor,% + object stereo/.get=\tikzumlObjectDefaultStereo, fill object/.get=\tikzumlObjectDefaultFillColor,% + call dt/.get=\tikzumlCallDefaultDT, call padding/.get=\tikzumlCallDefaultPadding, call type/.get=\tikzumlCallDefaultType, fill call/.get=\tikzumlCallDefaultFillColor,% + fragment type/.get=\tikzumlFragmentDefaultType, fragment inner xsep/.get=\tikzumlFragmentDefaultXSep, fragment inner ysep/.get=\tikzumlFragmentDefaultYSep, fill fragment/.get=\tikzumlFragmentDefaultFillColor,% + create call dt/.get=\tikzumlCreateCallDT,% + component width/.get=\tikzumlComponentDefaultWidth, fill component/.get=\tikzumlComponentDefaultFillColor,% + required interface distance/.get=\tikzumlRequiredInterfaceDefaultDistance, required interface width/.get=\tikzumlRequiredInterfaceDefaultWidth, required interface padding/.get=\tikzumlRequiredInterfaceDefaultPadding,% + provided interface distance/.get=\tikzumlProvidedInterfaceDefaultDistance, provided interface width/.get=\tikzumlProvidedInterfaceDefaultWidth, provided interface padding/.get=\tikzumlProvidedInterfaceDefaultPadding,% + port width/.get=\tikzumlPortDefaultWidth, fill port/.get=\tikzumlPortDefaultFillColor,% + fill assembly connector/.get=\tikzumlAssemblyConnectorDefaultFillColor% + }% +}% +% +% define a point +% arg : node/coordinates of the point +\newcommand{\umlpoint}[1]{% + \begin{pgfonlayer}{connections}% + \node[tikzuml control nodes style] at (#1) {};% + \end{pgfonlayer}% +}% +% +\newcommand{\tikzumlskipescape}[3][_]{% +\begingroup% + \def\_{#1}\edef\x{\endgroup% + \def\noexpand\csname #3\endcsname{#2}}\x% +}% +% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% class diagrams % +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +\pgfkeys{/tikzuml/relation/.cd, attr1/.style args={#1|#2}{arg1=#1, mult1=#2},% + attr2/.style args={#1|#2}{arg2=#1, mult2=#2},% + attr/.style args={#1|#2}{arg=#1, mult=#2},% + recursive/.style args={#1|#2|#3}{angle1=#1, angle2=#2, loopsize=#3},% + anchors/.style args={#1 and #2}{anchor1=#1, anchor2=#2},% + recursive direction/.style args={#1 to #2}{recursive direction start=#1, recursive direction end=#2}% +}% +% +\pgfkeys{/tikzuml/note/.cd, anchors/.style args={#1 and #2}{anchor1=#1, anchor2=#2}}% +% +\tikzstyle{tikzuml simpleclass style}=[rectangle, minimum height=2em, node distance=2em]% +\tikzstyle{tikzuml simpleinterface style}=[circle, minimum height=1em, node distance=1em]% +\tikzstyle{tikzuml class style}=[rectangle split, rectangle split parts=3, rectangle split part align={center, left, left}, minimum height=2em, node distance=2em]% +\tikzstyle{tikzuml narynode style}=[diamond]% +\tikzstyle{tikzuml template style}=[dashed, inner ysep=0.5em, inner xsep=1ex]% +\tikzstyle{tikzuml control nodes style}=[fill=black, inner sep=1.5pt, circle]% +% +\tikzstyle{tikzuml association style}=[color=\tikzumlDefaultDrawColor, -]% +\tikzstyle{tikzuml bidirectional association style}=[color=\tikzumlDefaultDrawColor, angle45-angle45]% +\tikzstyle{tikzuml unidirectional association style}=[color=\tikzumlDefaultDrawColor, -angle 45]% +\tikzstyle{tikzuml aggregation style}=[color=\tikzumlDefaultDrawColor, open diamond-]% +\tikzstyle{tikzuml unidirectional aggregation style}=[color=\tikzumlDefaultDrawColor, open diamond-angle 45]% +\tikzstyle{tikzuml composition style}=[color=\tikzumlDefaultDrawColor, diamond-]% +\tikzstyle{tikzuml unidirectional composition style}=[color=\tikzumlDefaultDrawColor, diamond-angle 45]% +\tikzstyle{tikzuml nesting style}=[color=\tikzumlDefaultDrawColor]% +\tikzstyle{tikzuml dependency style}=[color=\tikzumlDefaultDrawColor, -angle 45, dashed]% +\tikzstyle{tikzuml import style}=[color=\tikzumlDefaultDrawColor, -angle 45, dashed]% +\tikzstyle{tikzuml inherit style}=[color=\tikzumlDefaultDrawColor, -open triangle 45]% +\tikzstyle{tikzuml implements style}=[color=\tikzumlDefaultDrawColor, -open triangle 45, dashed]% +% +\pgfkeys{/tikzuml/assemblyconnectorrelation/.cd, anchors/.style args={#1 and #2}{anchor1=#1, anchor2=#2}}% +% +\newcounter{tikzumlPackageClassNum}% +\newcounter{tikzumlPackageSubPackageNum}% +\newcounter{tikzumlRelationNum}% +\setcounter{tikzumlRelationNum}{1}% +\newcounter{tikzumlNoteNum}% +\setcounter{tikzumlNoteNum}{1}% +% +\newcounter{pos}% +\newcounter{posT}% +\newcounter{posStereo}% +% +\newcounter{tikzumlPackageLevel}% +\setcounter{tikzumlPackageLevel}{0}% +% +\newif\iftikzumlpackageSimpleStyle% +\newif\iftikzumlclassSimpleStyle% +\newif\iftikzumlclassCircleShape% +\newif\iftikzumlpackageWithoutCoords% +\newif\iftikzumlclassWithoutCoords% +\newif\iftikzumlassocclassWithoutCoords% +\newif\iftikzumlnoteWithoutCoords% +% +% define a uml package +% arg : package name +% optional : x, y: coordinates of the package +% type: stereotype of the package +% name: name of the package node +% draw, fill, text: colors +% style: to manage every default TikZ option +% no coords: to tell that the package position is defined relatively +% to another node (automatically used with TikZ options above, below, left, right, below left, ...) +\newenvironment{umlpackage}[2][]{% + \pgfkeys{/tikzuml/package/.cd, x/.initial=\tikzumlDefaultX, y/.initial=\tikzumlDefaultX, style/.style={},% + name/.initial=tikzumlEmpty, type/.initial=\tikzumlPackageDefaultType, draw/.initial=\tikzumlDefaultDrawColor,% + fill/.initial=\tikzumlPackageDefaultFillColor, text/.initial=\tikzumlDefaultTextColor,% + no coords/.is if=tikzumlpackageWithoutCoords,% + no coords=false,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{above}\OR% + \equal{\keyname}{left}\OR% + \equal{\keyname}{below}\OR% + \equal{\keyname}{right}\OR% + \equal{\keyname}{above left}\OR% + \equal{\keyname}{above right}\OR% + \equal{\keyname}{below left}\OR% + \equal{\keyname}{below right}}{% + \IfSubStr{\keyvalue}{ of }{% + \pgfkeys{/tikzuml/package/.cd, no coords}% + }{}% + }{}% + \ifx\keyvalue\pgfkeysnovalue% + \pgfkeys{/tikzuml/package/.cd, style/.append style/.expand once={\keyname}}% + \else% + \pgfkeys{/tikzuml/package/.cd, style/.append style/.expand twice={\expandafter\keyname\expandafter=\keyvalue}}% + \fi% + %\errmessage{TIKZUML ERROR : in umlpackage, invalid option \keyname}% + }% + }% + \pgfkeys{/tikzuml/package/.cd, #1}% + \pgfkeys{/tikzuml/package/.cd, x/.get=\tikzumlPackageXShift, y/.get=\tikzumlPackageYShift, name/.get=\tikzumlPackageName, type/.get=\tikzumlPackageTypeTmp,% + draw/.get=\tikzumlPackageDrawColor, fill/.get=\tikzumlPackageFillColor,% + text/.get=\tikzumlPackageTextColor% + }% + % + + % + \ifthenelse{\equal{\tikzumlPackageTypeTmp}{tikzumlEmpty}}{% + \def\tikzumlPackageType{}% + }{% + \expandafter\def\expandafter\tikzumlPackageType\expandafter{$\ll$\tikzumlPackageTypeTmp$\gg$ \\}% + }% + % + \ifnum\thetikzumlPackageLevel>0% + \let\tikzumlPackage@nameold\tikzumlPackage@fitname% + \def\tikzumlPackage@name{#2}% + \begingroup% + \def\_{@}\edef\x{\endgroup% + \def\noexpand\tikzumlPackage@fitname{\tikzumlPackage@name}}\x% + \let\tikzumlPackage@parentold\tikzumlPackage@parent% + \edef\tikzumlPackage@parent{\tikzumlPackage@parentold @@\tikzumlPackage@nameold}% + \else% + \def\tikzumlPackage@parent{}% + \def\tikzumlPackage@name{#2}% + \begingroup% + \def\_{@}\edef\x{\endgroup% + \def\noexpand\tikzumlPackage@fitname{\tikzumlPackage@name}}\x% + \fi% + % + \let\tikzumlPackage@nodeNameold\tikzumlPackage@nodeName% + % + \begingroup% + \def\_{_}\edef\x{\endgroup% + \def\noexpand\tikzumlPackage@nodeName{\tikzumlPackage@name}}\x% + % + \ifthenelse{\equal{\tikzumlPackageName}{tikzumlEmpty}}{}{% + \def\tikzumlPackage@nodeName{\tikzumlPackageName}% + }% + % + \StrSubstitute{\tikzumlPackage@nodeName}{.}{@POINT@}{\tikzumlPackage@nodeName}% + % + \expandafter\gdef\csname tikzumlPackageFit\tikzumlPackage@parent @@\tikzumlPackage@fitname\endcsname{}% + % + \setcounter{tikzumlPackageClassNum}{0}% + \setcounter{tikzumlPackageSubPackageNum}{0}% + \stepcounter{tikzumlPackageLevel}% + % + \begin{scope}[xshift=\tikzumlPackageXShift cm, yshift=\tikzumlPackageYShift cm]% +}{% + \addtocounter{tikzumlPackageLevel}{-1}% + \begin{pgfonlayer}{package\thetikzumlPackageLevel}% + % + % if contains no class, and not simple, one define a fictive node to enable the fit option + \ifnum\c@tikzumlPackageClassNum=0% + \ifnum\c@tikzumlPackageSubPackageNum=0% + \iftikzumlpackageWithoutCoords% + \node[inner sep=1.5ex, /tikzuml/package/style] (\tikzumlPackage@nodeName-root) {\phantom{\tikzumlPackage@nodeName}};% + \else% + \node[inner sep=1.5ex, /tikzuml/package/style] (\tikzumlPackage@nodeName-root) at (0,0) {\phantom{\tikzumlPackage@nodeName}};% + \fi% + \expandafter\xdef\csname tikzumlPackageFit\tikzumlPackage@parent @@\tikzumlPackage@fitname\endcsname{(\tikzumlPackage@nodeName-root)}% + \fi% + \fi% + % + \ifnum\c@tikzumlPackageLevel>0% + \def\tikzumlPackageFitTmp{\csname tikzumlPackageFit\tikzumlPackage@parent\endcsname}% + \expandafter\xdef\csname tikzumlPackageFit\tikzumlPackage@parent\endcsname{\tikzumlPackageFitTmp (\tikzumlPackage@nodeName) (\tikzumlPackage@nodeName-caption)}% + \stepcounter{tikzumlPackageSubPackageNum}% + \fi% + % + \node[draw=\tikzumlPackageDrawColor, fill=\tikzumlPackageFillColor, text=\tikzumlPackageTextColor, font=\tikzumlDefaultFont, inner sep=1.5ex, /tikzuml/package/style, fit = \csname tikzumlPackageFit\tikzumlPackage@parent @@\tikzumlPackage@fitname\endcsname] (\tikzumlPackage@nodeName) {};% + \node[draw=\tikzumlPackageDrawColor, fill=\tikzumlPackageFillColor, text=\tikzumlPackageTextColor, font=\tikzumlDefaultFont, minimum height=1.5em, outer ysep=-0.3, anchor=south west] (\tikzumlPackage@nodeName-caption) at (\tikzumlPackage@nodeName.north west) {\begin{tabular}{c} \tikzumlPackageType \textbf{\tikzumlPackage@name}\end{tabular}};% + \end{pgfonlayer}% + \end{scope}% +}% +% +% shortcut to define an empty package +\newcommand{\umlemptypackage}[2][]{\begin{umlpackage}[#1]{#2} \end{umlpackage}}% +% +% define a uml class +% args : name of the class +% attributes of the class +% operations of the class +% optional : x, y: coordinates of the class +% width: of the class node +% type: type of class (class, interface, typedef, enum) +% tags: tagged values of class +% template: template parameters +% simple: if used, class is empty and drawn with a rectangle +% circle: if used with simple, class is empty and drawn with a circle +% draw, fill, fill template, and text: colors +% style: to manage every default TikZ option +% no coords: to tell that the class position is defined relatively +% to another node (automatically used with TikZ options above, below, left, right, below left, ...) +\newcommand{\umlclass}[4][]{% + \pgfkeys{/tikzuml/class/.cd, x/.initial=\tikzumlDefaultX, y/.initial=\tikzumlDefaultX, width/.initial=\tikzumlClassDefaultWidth, type/.initial=\tikzumlClassDefaultType,% + tags/.initial={}, style/.style={},% + template/.initial={}, name/.initial=tikzumlEmpty,% + draw/.initial=\tikzumlDefaultDrawColor,% + fill template/.initial=\tikzumlClassTemplateFillColorDefaultFillColor,% + fill/.initial=\tikzumlClassDefaultFillColor,% + text/.initial=\tikzumlDefaultTextColor,% + simple/.is if=tikzumlclassSimpleStyle, circle/.is if=tikzumlclassCircleShape, no coords/.is if=tikzumlclassWithoutCoords,% + simple=false, circle=false, no coords=false,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{above}\OR% + \equal{\keyname}{left}\OR% + \equal{\keyname}{below}\OR% + \equal{\keyname}{right}\OR% + \equal{\keyname}{above left}\OR% + \equal{\keyname}{above right}\OR% + \equal{\keyname}{below left}\OR% + \equal{\keyname}{below right}}{% + \IfSubStr{\keyvalue}{ of }{% + \pgfkeys{/tikzuml/class/.cd, no coords}% + }{}% + }{}% + \ifx\keyvalue\pgfkeysnovalue% + \pgfkeys{/tikzuml/class/.cd, style/.append style/.expand once={\keyname}}% + \else% + \pgfkeys{/tikzuml/class/.cd, style/.append style/.expand twice={\expandafter\keyname\expandafter=\keyvalue}}% + \fi% + %\errmessage{TIKZUML ERROR : in umlclass, invalid option \keyname}% + }% + }% + \pgfkeys{/tikzuml/class/.cd,#1}% + % + \iftikzumlclassSimpleStyle% + \iftikzumlclassCircleShape% + \pgfkeys{/tikzuml/class/.cd, width/.initial=\tikzumlSimpleInterfaceDefaultWidth}% + \fi% + \fi% + % + \pgfkeys{/tikzuml/class/.cd, x/.get=\tikzumlClassX, y/.get=\tikzumlClassY, width/.get=\tikzumlClassMinimumWidth,% + type/.get=\tikzumlClassTypeTmp, tags/.get=\tikzumlClassTagsTmp, template/.get=\tikzumlClassTemplateFillColorParam,% + name/.get=\tikzumlClassName,% + draw/.get=\tikzumlClassDrawColor, fill/.get=\tikzumlClassFillColor,% + text/.get=\tikzumlClassTextColor, fill template/.get=\tikzumlClassTemplateFillColor% + }% + % + \ifthenelse{\equal{\tikzumlClassTypeTmp}{class}\OR\equal{\tikzumlClassTypeTmp}{abstract}}{% + \def\tikzumlClassType{}% + }{% + \expandafter\def\expandafter\tikzumlClassType\expandafter{$\ll$\tikzumlClassTypeTmp$\gg$ \\}% + }% + % + \ifthenelse{\equal{\tikzumlClassTagsTmp}{}}{% + \def\tikzumlClassTags{}% + }{% + \def\tikzumlClassTags{\\ \{\tikzumlClassTagsTmp\}}% + }% + % + \ifthenelse{\equal{\tikzumlClassTemplateFillColorParam}{}}{% + \def\tikzumlClassVPadding{}% + \def\tikzumlClassHPadding{}% + }{% + \def\tikzumlClassVPadding{\vspace{0.1em} \\}% + \def\tikzumlClassHPadding{\hspace{0.5ex} $ $}% + }% + % + \def\tikzumlClassName{#2}% + % + \begingroup% + \def\_{_}\edef\x{\endgroup% + \def\noexpand\tikzumlClassNodeName{\tikzumlClassName}}\x% + % + \ifthenelse{\equal{\tikzumlClassName}{tikzumlEmpty}}{}{% + \def\tikzumlClassNodeName{\tikzumlClassName}% + }% + % + \StrSubstitute{\tikzumlClassNodeName}{:}{@COLON@}[\tikzumlClassNodeName]% + \StrSubstitute{\tikzumlClassNodeName}{\_}{@UNDERSCORE@}[\tikzumlClassNodeName]% + % + \ifthenelse{\equal{\tikzumlClassTypeTmp}{abstract}}{% + \let\tikzumlClassNameOld\tikzumlClassName% + \def\tikzumlClassName{{\it \tikzumlClassNameOld}}% + }{}% + % + \def\tikzumlClassPos{\tikzumlClassX,\tikzumlClassY}% + \def\tikzumlClassAttributes{#3}% + \def\tikzumlClassOperations{#4}% + % + \iftikzumlclassSimpleStyle% + \iftikzumlclassWithoutCoords% + \iftikzumlclassCircleShape% + \node[tikzuml simpleinterface style, draw=\tikzumlClassDrawColor, fill=\tikzumlClassFillColor, text=\tikzumlClassTextColor, font=\tikzumlDefaultFont, minimum width=\tikzumlClassMinimumWidth, /tikzuml/class/style] (\tikzumlClassNodeName) {};% + \node[anchor=south] (\tikzumlClassNodeName-label) at (\tikzumlClassNodeName.north) {\begin{tabular}{c}\tikzumlClassVPadding \tikzumlClassType \tikzumlClassHPadding \textbf{\tikzumlClassName} \tikzumlClassHPadding \tikzumlClassTags \end{tabular}% + };% + \else% + \node[tikzuml simpleclass style, draw=\tikzumlClassDrawColor, fill=\tikzumlClassFillColor, text=\tikzumlClassTextColor, font=\tikzumlDefaultFont, minimum width=\tikzumlClassMinimumWidth, /tikzuml/class/style] (\tikzumlClassNodeName) {\begin{tabular}{c}\tikzumlClassVPadding \tikzumlClassType \tikzumlClassHPadding \textbf{\tikzumlClassName} \tikzumlClassHPadding \tikzumlClassTags \end{tabular}% + };% + \fi% + \else% + \iftikzumlclassCircleShape% + \node[tikzuml simpleinterface style, draw=\tikzumlClassDrawColor, fill=\tikzumlClassFillColor, text=\tikzumlClassTextColor, font=\tikzumlDefaultFont, minimum width=\tikzumlClassMinimumWidth, /tikzuml/class/style] (\tikzumlClassNodeName) at (\tikzumlClassPos) {}; + \node[anchor=south] (\tikzumlClassNodeName-label) at (\tikzumlClassNodeName.north){\begin{tabular}{c}\tikzumlClassVPadding \tikzumlClassType \tikzumlClassHPadding \textbf{\tikzumlClassName} \tikzumlClassHPadding \tikzumlClassTags \end{tabular}% + };% + \else% + \node[tikzuml simpleclass style, draw=\tikzumlClassDrawColor, fill=\tikzumlClassFillColor, text=\tikzumlClassTextColor, font=\tikzumlDefaultFont, minimum width=\tikzumlClassMinimumWidth, /tikzuml/class/style] (\tikzumlClassNodeName) at (\tikzumlClassPos) {\begin{tabular}{c}\tikzumlClassVPadding \tikzumlClassType \tikzumlClassHPadding \textbf{\tikzumlClassName} \tikzumlClassHPadding \tikzumlClassTags \end{tabular}% + };% + \fi% + \fi% + \else% + \iftikzumlclassWithoutCoords% + \node[tikzuml class style, draw=\tikzumlClassDrawColor, fill=\tikzumlClassFillColor, text=\tikzumlClassTextColor, font=\tikzumlDefaultFont, minimum width=\tikzumlClassMinimumWidth, /tikzuml/class/style] (\tikzumlClassNodeName) {\begin{tabular}{c}\tikzumlClassVPadding \tikzumlClassType \tikzumlClassHPadding \textbf{\tikzumlClassName} \tikzumlClassHPadding \tikzumlClassTags \end{tabular}% + \nodepart{second}% + \begin{tabular}{l}% + \tikzumlClassAttributes% + \end{tabular}% + \nodepart{third}% + \begin{tabular}{l}% + \tikzumlClassOperations% + \end{tabular}% + };% + \else% + \node[tikzuml class style, draw=\tikzumlClassDrawColor, fill=\tikzumlClassFillColor, text=\tikzumlClassTextColor, font=\tikzumlDefaultFont, minimum width=\tikzumlClassMinimumWidth, /tikzuml/class/style] (\tikzumlClassNodeName) at (\tikzumlClassPos) {\begin{tabular}{c}\tikzumlClassVPadding \tikzumlClassType \tikzumlClassHPadding \textbf{\tikzumlClassName} \tikzumlClassHPadding \tikzumlClassTags \end{tabular}% + \nodepart{second}% + \begin{tabular}{l}% + \tikzumlClassAttributes% + \end{tabular}% + \nodepart{third}% + \begin{tabular}{l}% + \tikzumlClassOperations% + \end{tabular}% + };% + \fi% + \fi% + % + \ifthenelse{\equal{\tikzumlClassTemplateFillColorParam}{}}{}{% + \draw (\tikzumlClassNodeName.north east) node[tikzuml template style, name=\tikzumlClassNodeName-template, draw=\tikzumlClassDrawColor, fill=\tikzumlClassTemplateFillColor, text=\tikzumlClassTextColor, font=\tikzumlDefaultFont] {\tikzumlClassTemplateFillColorParam};% + }% + % + % add to fit + \ifnum\c@tikzumlPackageLevel>0% + \edef\tikzumlPackageFitOld{\csname tikzumlPackageFit\tikzumlPackage@parent @@\tikzumlPackage@fitname\endcsname}% + \ifthenelse{\equal{\tikzumlClassTemplateFillColorParam}{}}{% + \expandafter\xdef\csname tikzumlPackageFit\tikzumlPackage@parent @@\tikzumlPackage@fitname\endcsname{\tikzumlPackageFitOld (\tikzumlClassNodeName)}% + }{% + \expandafter\xdef\csname tikzumlPackageFit\tikzumlPackage@parent @@\tikzumlPackage@fitname\endcsname{\tikzumlPackageFitOld (\tikzumlClassNodeName) (\tikzumlClassNodeName-template)}% + }% + \stepcounter{tikzumlPackageClassNum}% + \fi% + \ifnum\c@tikzumlComponentLevel>0% + \def\tikzumlComponentFitTmp{\csname tikzumlComponentFit\tikzumlComponent@parent @@\tikzumlComponent@fitname\endcsname}% + \ifthenelse{\equal{\tikzumlClassTemplateFillColorParam}{}}{% + \expandafter\xdef\csname tikzumlComponentFit\tikzumlComponent@parent @@\tikzumlComponent@fitname\endcsname{\tikzumlComponentFitTmp (\tikzumlClassNodeName)}% + }{% + \expandafter\xdef\csname tikzumlComponentFit\tikzumlComponent@parent @@\tikzumlComponent@fitname\endcsname{\tikzumlComponentFitTmp (\tikzumlClassNodeName) (\tikzumlClassNodeName-template)}% + }% + \stepcounter{tikzumlComponentSubComponentNum}% + \fi% +}% +% +% shortcuts for interface, enum and typedef environments +\newcommand{\umlabstract}[4][]{% + \pgfkeys{/tikzuml/class/.cd, .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{type}}{% + \errmessage{TIKZUML ERROR : in umlabstract, forbidden option type}% + }{}% + }% + }% + \pgfkeys{/tikzuml/class/.cd, #1}% + \umlclass[type=abstract,#1]{#2}{#3}{#4}% +}% +\newcommand{\umlinterface}[4][]{% + \pgfkeys{/tikzuml/class/.cd, .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{type}}{% + \errmessage{TIKZUML ERROR : in umlinterface, forbidden option type}% + }{}% + }% + }% + \pgfkeys{/tikzuml/class/.cd, #1}% + \umlclass[type=interface,#1]{#2}{#3}{#4}% +}% +\newcommand{\umltypedef}[4][]{% + \pgfkeys{/tikzuml/class/.cd, .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{type}}{% + \errmessage{TIKZUML ERROR : in umltypedef, forbidden option type}% + }{}% + }% + }% + \pgfkeys{/tikzuml/class/.cd, #1}% + \umlclass[type=typedef,#1]{#2}{#3}{#4}% +}% +\newcommand{\umlenum}[4][]{% + \pgfkeys{/tikzuml/class/.cd, .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{type}}{% + \errmessage{TIKZUML ERROR : in umlenum, forbidden option type}% + }{}% + }% + }% + \pgfkeys{/tikzuml/class/.cd, #1}% + \umlclass[type=enum,#1]{#2}{#3}{#4} +}% +% +% shortcut to define an empty class +\newcommand{\umlemptyclass}[2][]{\umlclass[#1]{#2}{}{}}% +\newcommand{\umlsimpleclass}[2][]{% + \pgfkeys{/tikzuml/class/.cd, .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{simple}}{% + \errmessage{TIKZUML ERROR : in umlsimpleclass, forbidden option simple}% + }{}% + }% + }% + \pgfkeys{/tikzuml/class/.cd, #1}% + \umlemptyclass[simple, #1]{#2}% +}% +% +\newcommand{\umlsimpleinterface}[2][]{% + \pgfkeys{/tikzuml/class/.cd, .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{simple}}{% + \errmessage{TIKZUML ERROR : in umlsimpleinterface, forbidden option simple}% + }{% + \ifthenelse{\equal{\keyname}{circle}}{% + \errmessage{TIKZUML ERROR : in umlsimpleinterface, forbidden option circle}% + }{}% + }% + }% + }% + \pgfkeys{/tikzuml/class/.cd, #1}% + \umlsimpleclass[circle, #1]{#2}% +}% +% underline the text for static arg +\newcommand{\umlstatic}[1]{\underline{#1}}% +\newcommand{\umlvirt}[1]{\textit{#1}}% +% +% define node for n-ary association +\newcommand{\umlNarynode}[2][]{% + \def\tikzumlNaryNodeAnchor{.north} + \def\tikzumlNaryNodeLabelPos{above} + \pgfkeys{/tikzuml/narynode/.cd, x/.initial=\tikzumlDefaultX, y/.initial=\tikzumlDefaultX, width/.initial=\tikzumlNaryNodeDefaultWidth, name/.initial=tikzumlEmpty,% + draw/.initial=\tikzumlDefaultDrawColor, fill/.initial=\tikzumlClassDefaultFillColor,% + text/.initial=\tikzumlDefaultTextColor, style/.style={},% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{above}}{% + \def\tikzumlNaryNodeAnchor{.north}% + \def\tikzumlNaryNodeLabelPos{above}% + }{% + \ifthenelse{\equal{\keyname}{above left}}{% + \def\tikzumlNaryNodeAnchor{.north west}% + \def\tikzumlNaryNodeLabelPos{above left}% + }{% + \ifthenelse{\equal{\keyname}{left}}{% + \def\tikzumlNaryNodeAnchor{.west}% + \def\tikzumlNaryNodeLabelPos{left}% + }{% + \ifthenelse{\equal{\keyname}{below left}}{% + \def\tikzumlNaryNodeAnchor{.south west}% + \def\tikzumlNaryNodeLabelPos{below left}% + }{% + \ifthenelse{\equal{\keyname}{below}}{% + \def\tikzumlNaryNodeAnchor{.south}% + \def\tikzumlNaryNodeLabelPos{below}% + }{% + \ifthenelse{\equal{\keyname}{below right}}{% + \def\tikzumlNaryNodeAnchor{.south east}% + \def\tikzumlNaryNodeLabelPos{below right}% + }{% + \ifthenelse{\equal{\keyname}{right}}{% + \def\tikzumlNaryNodeAnchor{.east}% + \def\tikzumlNaryNodeLabelPos{right}% + }{% + \ifthenelse{\equal{\keyname}{above right}}{% + \def\tikzumlNaryNodeAnchor{.north east}% + \def\tikzumlNaryNodeLabelPos{above right}% + }{% + \ifx\keyvalue\pgfkeysnovalue% + \pgfkeys{/tikzuml/narynode/.cd, style/.append style/.expand once={\keyname}}% + \else% + \pgfkeys{/tikzuml/narynode/.cd, style/.append style/.expand twice={\expandafter\keyname\expandafter=\keyvalue}}% + \fi% + %\errmessage{TIKZUML ERROR : in umlNarynode, invalid option \keyname}% + }% + }% + }% + }% + }% + }% + }% + }% + }% + }% + \pgfkeys{/tikzuml/narynode/.cd,#1}% + \pgfkeys{/tikzuml/narynode/.cd, x/.get=\tikzumlNaryNodeX, y/.get=\tikzumlNaryNodeY, width/.get=\tikzumlNaryNodeMinimumWidth,% + name/.get=\tikzumlNaryNodeName,% + draw/.get=\tikzumlNaryNodeDrawColor, fill/.get=\tikzumlNaryNodeFillColor,% + text/.get=\tikzumlNaryNodeTextColor% + }% + % + \def\tikzumlNaryName{#2}% + % + \ifthenelse{\equal{\tikzumlNaryNodeName}{tikzumlEmpty}}{% + \edef\tikzumlNaryNodeName{\tikzumlNaryName}% + }{% + \edef\tikzumlNaryNodeName{\tikzumlNaryNodeName}% + }% + % + \begingroup% + \def\_{_}\edef\x{\endgroup% + \def\noexpand\tikzumlNaryNode@nodeName{\tikzumlNaryNodeName}}\x% + % + \StrSubstitute{\tikzumlNaryNode@nodeName}{:}{@COLON@}[\tikzumlNaryNode@nodeName]% + \StrSubstitute{\tikzumlNaryNode@nodeName}{\_}{@UNDERSCORE@}[\tikzumlNaryNode@nodeName]% + % + \def\tikzumlNarynodePos{\tikzumlNaryNodeX,\tikzumlNaryNodeY}% + % + \node[tikzuml narynode style, draw=\tikzumlNaryNodeDrawColor, fill=\tikzumlNaryNodeFillColor, text=\tikzumlNaryNodeTextColor, font=\tikzumlDefaultFont, minimum width=\tikzumlNaryNodeMinimumWidth, minimum height=\tikzumlNaryNodeMinimumWidth, /tikzuml/narynode/style] (\tikzumlNaryNode@nodeName) at (\tikzumlNarynodePos) {};% + \draw (\tikzumlNaryNode@nodeName\tikzumlNaryNodeAnchor) node[\tikzumlNaryNodeLabelPos] {\tikzumlNaryName};% + % + % add to fit + \ifnum\c@tikzumlPackageLevel>0% + \edef\tikzumlPackageFitOld{\csname tikzumlPackageFit\tikzumlPackage@parent @@\tikzumlPackage@fitname\endcsname}% + \expandafter\xdef\csname tikzumlPackageFit\tikzumlPackage@parent @@\tikzumlPackage@fitname\endcsname{\tikzumlPackageFitOld (\tikzumlNaryNode@nodeName)}% + \stepcounter{tikzumlPackageClassNum}% + \fi% +}% +% +\newdimen\tikzumlNestingSymbolSize% +% +% main command to define a relation between two classes +% args : src class +% dest class +% optional : geometry: geometry of the line +% weight: barycentric weight of the middle part when geometry is a 3-line +% arm1, arm2: lengths of first or last part when geometry id a 3-line +% arg1, arg2, arg: name of the src/dest/dest side class type attribute defined by the relation +% mult1, mult2, mult: multiplicity of the src/dest/dest side class type attribute defined by the relation +% pos1, pos2, pos: position of the src/dest/dest side class type attribute defined by the relation +% align1, align2, align: text justification of the src/dest/dest side class type attribute defined by the relation +% anchor1, anchor2: src/dest anchors on linked classes +% angle1, angle2, loopsize: start angle, end angle and size of the relation (only if recursive) +% stereo: stereotype of the relation +% pos stereo: position of the stereotype on the relation +% style: style of the relation (association, aggregation, composition, inherit, ...) +% name: rootname used for naming nodes of the relation +% recursive mode: type of recursive arrow (transition for state diagrams, or default) +% recursive direction start/end: when transition relation, start/end directions of the relation arrow +\newcommand{\umlrelation}[3][]{% + \pgfkeys{/tikzuml/relation/.cd, geometry/.initial=\tikzumlRelationDefaultGeometry, weight/.initial=\tikzumlRelationDefaultWeight,% + arm1/.initial=auto, arm2/.initial=auto,% + arg1/.initial={}, arg2/.initial={}, arg/.initial={},% + mult1/.initial={}, mult2/.initial={}, mult/.initial={},% + pos1/.initial=\tikzumlRelationDefaultPosO, pos2/.initial=\tikzumlRelationDefaultPosT, pos/.initial=tikzumlEmpty,% + align1/.initial={}, align2/.initial={}, align/.initial={},% + anchor1/.initial=tikzumlEmpty, anchor2/.initial=tikzumlEmpty,% + angle1/.initial=\tikzumlRelationDefaultAngleO, angle2/.initial=\tikzumlRelationDefaultAngleT, loopsize/.initial=\tikzumlRelationDefaultLoopSize,% + stereo/.initial={}, pos stereo/.initial=\tikzumlRelationDefaultPosStereo,% + style/.initial=->, style2/.style={}, name/.initial=relation-\thetikzumlRelationNum,% + recursive mode/.initial=default, recursive direction start/.initial=right,% + recursive direction end/.initial=bottom,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{with port}% + \OR\equal{\keyname}{interface}% + \OR\equal{\keyname}{padding}% + \OR\equal{\keyname}{width}% + \OR\equal{\keyname}{first arm}% + \OR\equal{\keyname}{second arm}% + \OR\equal{\keyname}{middle arm}% + \OR\equal{\keyname}{last arm}% + \OR\equal{\keyname}{distance}}{}{% + \ifx\keyvalue\pgfkeysnovalue% + \pgfkeys{/tikzuml/relation/.cd, style2/.append style/.expand once={\keyname}}% + \else% + \pgfkeys{/tikzuml/relation/.cd, style2/.append style/.expand twice={\expandafter\keyname\expandafter=\keyvalue}}% + \fi% + %\errmessage{TIKZUML ERROR : in umlrelation, invalid option \keyname}% + }% + }% + }% + \pgfkeys{/tikzuml/relation/.cd,#1}% + \pgfkeys{/tikzuml/relation/.cd, geometry/.get=\tikzumlRelationGeometry, weight/.get=\tikzumlRelationWeight,% + arm1/.get=\tikzumlRelationArmO, arm2/.get=\tikzumlRelationArmT,% + arg1/.get=\tikzumlRelationAttrName, arg2/.get=\tikzumlRelationAttrNameTO, arg/.get=\tikzumlRelationAttrNameTT,% + mult1/.get=\tikzumlRelationMultiplicity, mult2/.get=\tikzumlRelationMultiplicityTO, mult/.get=\tikzumlRelationMultiplicityTT,% + pos1/.get=\tikzumlRelationPosition, pos2/.get=\tikzumlRelationPositionTO, pos/.get=\tikzumlRelationPositionTT,% + align1/.get=\tikzumlRelationAlign, align2/.get=\tikzumlRelationAlignTO, align/.get=\tikzumlRelationAlignTT,% + anchor1/.get=\tikzumlRelationSrcAnchor, anchor2/.get=\tikzumlRelationDestAnchor,% + angle1/.get=\tikzumlRelationStartAngle, angle2/.get=\tikzumlRelationEndAngle, loopsize/.get=\tikzumlRelationLoopSize,% + stereo/.get=\tikzumlRelationStereoType, pos stereo/.get=\tikzumlRelationPositionStereotype,% + style/.get=\tikzumlRelationStyle, name/.get=\tikzumlRelationName,% + recursive mode/.get=\tikzumlRelationRecursiveMode,% + recursive direction start/.get=\tikzumlRelationRecursiveDirectionStart,% + recursive direction end/.get=\tikzumlRelationRecursiveDirectionEnd% + }% + % + \def\tikzumlSrcClassName{#2}% + % + % managing \_ in class names for node names + \begingroup% + \def\_{_}\edef\x{\endgroup% + \def\noexpand\tikzumlSrcClassNodeName{\tikzumlSrcClassName}}\x% + % + \StrSubstitute{\tikzumlSrcClassNodeName}{:}{@COLON@}[\tikzumlSrcClassNodeName]% + \StrSubstitute{\tikzumlSrcClassNodeName}{\_}{@UNDERSCORE@}[\tikzumlSrcClassNodeName]% + % + \def\tikzumlDestClassName{#3}% + \begingroup% + \def\_{_}\edef\x{\endgroup% + \def\noexpand\tikzumlDestClassNodeName{\tikzumlDestClassName}}\x% + % + \StrSubstitute{\tikzumlDestClassNodeName}{:}{@COLON@}[\tikzumlDestClassNodeName]% + \StrSubstitute{\tikzumlDestClassNodeName}{\_}{@UNDERSCORE@}[\tikzumlDestClassNodeName]% + % + % managing alias keys + \def\tikzumlRelationAttrNameT{\tikzumlRelationAttrNameTO\tikzumlRelationAttrNameTT}% + \def\tikzumlRelationMultiplicityT{\tikzumlRelationMultiplicityTO\tikzumlRelationMultiplicityTT}% + \def\tikzumlRelationAlignT{\tikzumlRelationAlignTO\tikzumlRelationAlignTT}% + \def\posAttrName{}% + \def\posMultiplicity{}% + \def\posAttrNameT{}% + \def\posMultiplicityT{}% + % + \ifthenelse{\equal{\tikzumlRelationPositionTT}{tikzumlEmpty}}{% + \def\tikzumlRelationPositionT{\tikzumlRelationPositionTO}% + }{% + \def\tikzumlRelationPositionT{\tikzumlRelationPositionTT}% + }% + % + \def\attrAlign{}% + \def\multAlign{}% + \def\attrAlignT{}% + \def\multAlignT{}% + % + \ifthenelse{\equal{\tikzumlRelationAlign}{left}}{% + \def\attrAlign{above right}% + \def\multAlign{below right}% + }{% + \ifthenelse{\equal{\tikzumlRelationAlign}{right}}{% + \def\attrAlign{above left}% + \def\multAlign{below left}% + }{}% + }% + % + \ifthenelse{\equal{\tikzumlRelationAlignT}{left}}{% + \def\attrAlignT{above right}% + \def\multAlignT{below right}% + }{% + \ifthenelse{\equal{\tikzumlRelationAlignT}{right}}{% + \def\attrAlignT{above left}% + \def\multAlignT{below left}% + }{}% + }% + % + % def stereotype + \ifthenelse{\equal{\tikzumlRelationStereoType}{}}{% + \def\stereotype{}% + }{% + \def\stereotype{$\ll$\tikzumlRelationStereoType$\gg$}% + }% + + % def anchors macros + \ifthenelse{\equal{\tikzumlRelationSrcAnchor}{tikzumlEmpty}}{% + \def\tikzumlRelationSrcAnchor{}% + }{% + \let\tikzumlRelationSrcAnchorold\tikzumlRelationSrcAnchor% + \def\tikzumlRelationSrcAnchor{.\tikzumlRelationSrcAnchorold}% + }% + % + \ifthenelse{\equal{\tikzumlRelationDestAnchor}{tikzumlEmpty}}{% + \def\tikzumlRelationDestAnchor{}% + }{% + \let\tikzumlRelationDestAnchorold\tikzumlRelationDestAnchor% + \def\tikzumlRelationDestAnchor{.\tikzumlRelationDestAnchorold}% + }% + % + \setcounter{pos}{100*\real{\tikzumlRelationPosition}}% + \setcounter{posT}{100*\real{\tikzumlRelationPositionT}}% + \setcounter{posStereo}{100*\real{\tikzumlRelationPositionStereotype}}% + % + \pgfmathsetmacro{\tikzumlRelationWeightT}{1.0-\tikzumlRelationWeight}% + % + %\newcounter{tikzumlControlNodesNum}% + %\setcounter{tikzumlControlNodesNum}{0}% + % + \node[inner sep=0] (\tikzumlRelationName-middle) at (barycentric cs:\tikzumlSrcClassNodeName=\tikzumlRelationWeightT,\tikzumlDestClassNodeName=\tikzumlRelationWeight) {};% + % + % straight line + \ifthenelse{\equal{\tikzumlRelationGeometry}{--}}% + {% + \ifthenelse{\equal{\tikzumlSrcClassNodeName}{\tikzumlDestClassNodeName}}{% + \def\arcNum{1}% + \def\arcNumT{1}% + % + \ifthenelse{\equal{\tikzumlRelationRecursiveMode}{default}}{% + \xdef\tikzumlLastArc{node[pos=\tikzumlRelationPosition, \posAttrName, \attrAlign] {\tikzumlRelationAttrName}% + node[pos=\tikzumlRelationPosition, swap, \posMultiplicity, \multAlign] {\tikzumlRelationMultiplicity}% + node[pos=\tikzumlRelationPositionT, \posAttrNameT, \attrAlignT] {\tikzumlRelationAttrNameT}% + node[pos=\tikzumlRelationPositionT, swap, \posMultiplicityT, \multAlignT] {\tikzumlRelationMultiplicityT}% + node[pos=\tikzumlRelationPositionStereotype, anchor=center] {\stereotype} }% + \xdef\tikzumlPath{(\tikzumlSrcClassNodeName) edge[in=\tikzumlRelationEndAngle, out=\tikzumlRelationStartAngle, distance=\tikzumlRelationLoopSize] \tikzumlLastArc% + node[midway, inner sep=0, name=\tikzumlRelationName-1, anchor=center] {} (\tikzumlDestClassNodeName) }% + }{% + \ifthenelse{\equal{\tikzumlRelationRecursiveMode}{transition}}{% + \xdef\tikzumlFirstArc{node[midway, inner sep=0, name=\tikzumlRelationName-1, anchor=center] {}}% + \xdef\tikzumlMidOneArc{node[midway, inner sep=0, name=\tikzumlRelationName-3, anchor=center] {}}% + % + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionStart}{\tikzumlRelationRecursiveDirectionEnd}}{% + \def\numArcs{3}% + \xdef\tikzumlLastArc{node[midway, inner sep=0, name=\tikzumlRelationName-5, anchor=center] {}}% + % + \begin{pgfonlayer}{connections}% + \draw (\tikzumlSrcClassNodeName) edge[in=\tikzumlRelationEndAngle, out=\tikzumlRelationStartAngle, distance=\tikzumlRelationLoopSize, draw=none] % + node[midway, inner sep=0, name=\tikzumlRelationName-tmp, anchor=center] {} (\tikzumlDestClassNodeName);% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionStart}{right}\OR\equal{\tikzumlRelationRecursiveDirectionStart}{left}}{% + \node[inner sep=0, name=\tikzumlRelationName-2] at (\tikzumlSrcClassNodeName.\tikzumlRelationStartAngle -| \tikzumlRelationName-tmp) {};% + \node[inner sep=0, name=\tikzumlRelationName-4] at (\tikzumlDestClassNodeName.\tikzumlRelationEndAngle -| \tikzumlRelationName-tmp) {};% + }{% + \node[inner sep=0, name=\tikzumlRelationName-2] at (\tikzumlSrcClassNodeName.\tikzumlRelationStartAngle |- \tikzumlRelationName-tmp) {};% + \node[inner sep=0, name=\tikzumlRelationName-4] at (\tikzumlDestClassNodeName.\tikzumlRelationEndAngle |- \tikzumlRelationName-tmp) {};% + }% + \end{pgfonlayer}% + }{% + \def\numArcs{4}% + \xdef\tikzumlMidTwoArc{node[midway, inner sep=0, name=\tikzumlRelationName-5, anchor=center] {}}% + \xdef\tikzumlLastArc{node[midway, inner sep=0, name=\tikzumlRelationName-7, anchor=center] {}}% + % + \begin{pgfonlayer}{connections}% + \draw (\tikzumlSrcClassNodeName) edge[in=\tikzumlRelationEndAngle, out=\tikzumlRelationStartAngle, distance=\tikzumlRelationLoopSize, draw=none] % + node[midway, name=\tikzumlRelationName-4, anchor=center] {} (\tikzumlDestClassNodeName);% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionStart}{right}\OR\equal{\tikzumlRelationRecursiveDirectionStart}{left}}{% + \node[inner sep=0, name=\tikzumlRelationName-2] at (\tikzumlSrcClassNodeName.\tikzumlRelationStartAngle -| \tikzumlRelationName-4) {};% + \node[inner sep=0, name=\tikzumlRelationName-6] at (\tikzumlDestClassNodeName.\tikzumlRelationEndAngle |- \tikzumlRelationName-4) {};% + }{% + \node[inner sep=0, name=\tikzumlRelationName-2] at (\tikzumlSrcClassNodeName.\tikzumlRelationStartAngle |- \tikzumlRelationName-4) {};% + \node[inner sep=0, name=\tikzumlRelationName-6] at (\tikzumlDestClassNodeName.\tikzumlRelationEndAngle -| \tikzumlRelationName-4) {};% + }% + \end{pgfonlayer}% + }% + % + \ifnum\numArcs=4% + \ifnum\theposStereo>300% + \pgfmathsetmacro{\tikzumlRelationPositionStereotype}{(\theposStereo-300)/100}% + \xdef\tikzumlLastArc{\tikzumlLastArc node[pos=\tikzumlRelationPositionStereotype, anchor=center] {\stereotype}}% + \else% + \ifnum\theposStereo<100% + \xdef\tikzumlFirstArc{\tikzumlFirstArc node[pos=\tikzumlRelationPositionStereotype, anchor=center] {\stereotype}}% + \else% + \ifnum\theposStereo>200% + \pgfmathsetmacro{\tikzumlRelationPositionStereotype}{(\theposStereo-200)/100}% + \xdef\tikzumlMidTwoArc{\tikzumlMidTwoArc node[pos=\tikzumlRelationPositionStereotype, anchor=center] {\stereotype}}% + \else% + \pgfmathsetmacro{\tikzumlRelationPositionStereotype}{(\theposStereo-100)/100}% + \xdef\tikzumlMidOneArc{\tikzumlMidOneArc node[pos=\tikzumlRelationPositionStereotype, anchor=center] {\stereotype}}% + \fi% + \fi% + \fi% + % + \ifthenelse{\thepos=300\OR\thepos=100}{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionStart}{right}}{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionEnd}{bottom}}{% + \def\posAttrName{above right}% + \def\posMultiplicity{below left}% + }{% + \def\posAttrName{above left}% + \def\posMultiplicity{below right}% + }% + }{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionStart}{left}}{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionEnd}{bottom}}{% + \def\posAttrName{above left}% + \def\posMultiplicity{below right}% + }{% + \def\posAttrName{above right}% + \def\posMultiplicity{below left}% + }% + }{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionStart}{top}}{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionEnd}{left}}{% + \def\posAttrName{above right}% + \def\posMultiplicity{below left}% + }{% + \def\posAttrName{above left}% + \def\posMultiplicity{below right}% + }% + }{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionEnd}{left}}{% + \def\posAttrName{above left}% + \def\posMultiplicity{below right}% + }{% + \def\posAttrName{above right}% + \def\posMultiplicity{below left}% + }% + }% + }% + }% + }{}% + % + \ifthenelse{\thepos=200}{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionStart}{right}}{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionEnd}{bottom}}{% + \def\posAttrName{above left}% + \def\posMultiplicity{below right}% + }{% + \def\posAttrName{above right}% + \def\posMultiplicity{below left}% + }% + }{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionStart}{left}}{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionEnd}{bottom}}{% + \def\posAttrName{above right}% + \def\posMultiplicity{below left}% + }{% + \def\posAttrName{above left}% + \def\posMultiplicity{below right}% + }% + }{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionStart}{top}}{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionEnd}{left}}{% + \def\posAttrName{above left}% + \def\posMultiplicity{below right}% + }{% + \def\posAttrName{above right}% + \def\posMultiplicity{below left}% + }% + }{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionEnd}{left}}{% + \def\posAttrName{above right}% + \def\posMultiplicity{below left}% + }{% + \def\posAttrName{above left}% + \def\posMultiplicity{below right}% + }% + }% + }% + }% + }{}% + % + \ifnum\thepos>300% + \pgfmathsetmacro{\tikzumlRelationPosition}{(\thepos-300)/100}% + \xdef\tikzumlLastArc{\tikzumlLastArc node[pos=\tikzumlRelationPosition, \posAttrName, \attrAlign] {\tikzumlRelationAttrName}% + node[pos=\tikzumlRelationPosition, swap, \posMultiplicity, \multAlign] {\tikzumlRelationMultiplicity}% + }% + \else% + \ifnum\thepos<100% + \xdef\tikzumlFirstArc{\tikzumlFirstArc node[pos=\tikzumlRelationPosition, \posAttrName, \attrAlign] {\tikzumlRelationAttrName}% + node[pos=\tikzumlRelationPosition, swap, \posMultiplicity, \multAlign] {\tikzumlRelationMultiplicity}% + }% + \else% + \ifnum\thepos>200% + \pgfmathsetmacro{\tikzumlRelationPosition}{(\thepos-200)/100}% + \xdef\tikzumlMidTwoArc{\tikzumlMidTwoArc node[pos=\tikzumlRelationPosition, \posAttrName, \attrAlign] {\tikzumlRelationAttrName}% + node[pos=\tikzumlRelationPosition, swap, \posMultiplicity, \multAlign] {\tikzumlRelationMultiplicity}% + }% + \else% + \pgfmathsetmacro{\tikzumlRelationPosition}{(\thepos-100)/100}% + \xdef\tikzumlMidOneArc{\tikzumlMidOneArc node[pos=\tikzumlRelationPosition, \posAttrName, \attrAlign] {\tikzumlRelationAttrName}% + node[pos=\tikzumlRelationPosition, swap, \posMultiplicity, \multAlign] {\tikzumlRelationMultiplicity}% + }% + \fi% + \fi% + \fi% + % + \ifthenelse{\theposT=300\OR\theposT=100}{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionStart}{right}}{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionEnd}{bottom}}{% + \def\posAttrNameT{above right}% + \def\posMultiplicityT{below left}% + }{% + \def\posAttrNameT{above left}% + \def\posMultiplicityT{below right}% + }% + }{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionStart}{left}}{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionEnd}{bottom}}{% + \def\posAttrNameT{above left}% + \def\posMultiplicityT{below right}% + }{% + \def\posAttrNameT{above right}% + \def\posMultiplicityT{below left}% + }% + }{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionStart}{top}}{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionEnd}{left}}{% + \def\posAttrNameT{above right}% + \def\posMultiplicityT{below left}% + }{% + \def\posAttrNameT{above left}% + \def\posMultiplicityT{below right}% + }% + }{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionEnd}{left}}{% + \def\posAttrNameT{above left}% + \def\posMultiplicityT{below right}% + }{% + \def\posAttrNameT{above right}% + \def\posMultiplicityT{below left}% + }% + }% + }% + }% + }{}% + \ifthenelse{\theposT=200}{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionStart}{right}}{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionEnd}{bottom}}{% + \def\posAttrNameT{above left}% + \def\posMultiplicity{below right}% + }{% + \def\posAttrNameT{above right}% + \def\posMultiplicityT{below left}% + }% + }{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionStart}{left}}{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionEnd}{bottom}}{% + \def\posAttrNameT{above right}% + \def\posMultiplicityT{below left}% + }{% + \def\posAttrNameT{above left}% + \def\posMultiplicityT{below right}% + }% + }{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionStart}{top}}{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionEnd}{left}}{% + \def\posAttrNameT{above left}% + \def\posMultiplicityT{below right}% + }{% + \def\posAttrNameT{above right}% + \def\posMultiplicityT{below left}% + }% + }{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionEnd}{left}}{% + \def\posAttrNameT{above right}% + \def\posMultiplicityT{below left}% + }{% + \def\posAttrNameT{above left}% + \def\posMultiplicityT{below right}% + }% + }% + }% + }% + }{}% + % + \ifnum\theposT>300% + \pgfmathsetmacro{\tikzumlRelationPositionT}{(\theposT-300)/100}% + \xdef\tikzumlLastArc{\tikzumlLastArc node[pos=\tikzumlRelationPositionT, \posAttrNameT, \attrAlignT] {\tikzumlRelationAttrNameT}% + node[pos=\tikzumlRelationPositionT, swap, \posMultiplicityT, \multAlignT] {\tikzumlRelationMultiplicityT}% + }% + \else% + \ifnum\theposT<100% + \xdef\tikzumlFirstArc{\tikzumlFirstArc node[pos=\tikzumlRelationPositionT, \posAttrNameT, \attrAlignT] {\tikzumlRelationAttrNameT}% + node[pos=\tikzumlRelationPositionT, swap, \posMultiplicityT, \multAlignT] {\tikzumlRelationMultiplicityT}% + }% + \else% + \ifnum\theposT>200% + \pgfmathsetmacro{\tikzumlRelationPositionT}{(\theposT-200)/100}% + \xdef\tikzumlMidTwoArc{\tikzumlMidTwoArc node[pos=\tikzumlRelationPositionT, \posAttrNameT, \attrAlignT] {\tikzumlRelationAttrNameT}% + node[pos=\tikzumlRelationPositionT, swap, \posMultiplicityT, \multAlignT] {\tikzumlRelationMultiplicityT}% + }% + \else% + \pgfmathsetmacro{\tikzumlRelationPositionT}{(\theposT-100)/100}% + \xdef\tikzumlMidOneArc{\tikzumlMidOneArc node[pos=\tikzumlRelationPositionT, \posAttrNameT, \attrAlignT] {\tikzumlRelationAttrNameT}% + node[pos=\tikzumlRelationPositionT, swap, \posMultiplicityT, \multAlignT] {\tikzumlRelationMultiplicityT}% + }% + \fi% + \fi% + \fi% + \else% + \ifnum\theposStereo>200% + \pgfmathsetmacro{\tikzumlRelationPositionStereotype}{(\theposStereo-200)/100}% + \xdef\tikzumlLastArc{\tikzumlLastArc node[pos=\tikzumlRelationPositionStereotype, anchor=center] {\stereotype} }% + \else% + \ifnum\theposStereo<100% + \xdef\tikzumlFirstArc{\tikzumlFirstArc node[pos=\tikzumlRelationPositionStereotype, anchor=center] {\stereotype} }% + \else% + \pgfmathsetmacro{\tikzumlRelationPositionStereotype}{(\theposStereo-100)/100}% + \xdef\tikzumlMidOneArc{\tikzumlMidOneArc node[pos=\tikzumlRelationPositionStereotype, anchor=center] {\stereotype} }% + \fi% + \fi% + % + \ifthenelse{\thepos=100}{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionStart}{right}}{% + \ifthenelse{\tikzumlRelationEndAngle<\tikzumlRelationStartAngle}{% + \def\posAttrName{above right}% + \def\posMultiplicity{below left}% + }{% + \def\posAttrName{above left}% + \def\posMultiplicity{below right}% + }% + }{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionStart}{left}}{% + \ifthenelse{\tikzumlRelationEndAngle<\tikzumlRelationStartAngle}{% + \def\posAttrName{above left}% + \def\posMultiplicity{below right}% + }{% + \def\posAttrName{above right}% + \def\posMultiplicity{below left}% + }% + }{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionStart}{top}}{% + \ifthenelse{\tikzumlRelationEndAngle<\tikzumlRelationStartAngle}{% + \def\posAttrName{above right}% + \def\posMultiplicity{below left}% + }{% + \def\posAttrName{above left}% + \def\posMultiplicity{below right}% + }% + }{% + \ifthenelse{\tikzumlRelationEndAngle<\tikzumlRelationStartAngle}{% + \def\posAttrName{above left}% + \def\posMultiplicity{below right}% + }{% + \def\posAttrName{above right}% + \def\posMultiplicity{below left}% + }% + }% + }% + }% + }{}% + % + \ifthenelse{\thepos=200}{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionStart}{right}}{% + \ifthenelse{\tikzumlRelationEndAngle<\tikzumlRelationStartAngle}{% + \def\posAttrName{above left}% + \def\posMultiplicity{below right}% + }{% + \def\posAttrName{above right}% + \def\posMultiplicity{below left}% + }% + }{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionStart}{left}}{% + \ifthenelse{\tikzumlRelationEndAngle<\tikzumlRelationStartAngle}{% + \def\posAttrName{above right}% + \def\posMultiplicity{below left}% + }{% + \def\posAttrName{above left}% + \def\posMultiplicity{below right}% + }% + }{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionStart}{top}}{% + \ifthenelse{\tikzumlRelationEndAngle<\tikzumlRelationStartAngle}{% + \def\posAttrName{above left}% + \def\posMultiplicity{below right}% + }{% + \def\posAttrName{above right}% + \def\posMultiplicity{below left}% + }% + }{% + \ifthenelse{\tikzumlRelationEndAngle<\tikzumlRelationStartAngle}{% + \def\posAttrName{above right}% + \def\posMultiplicity{below left}% + }{% + \def\posAttrName{above left}% + \def\posMultiplicity{below right}% + }% + }% + }% + }% + }{}% + % + \ifnum\thepos>200% + \pgfmathsetmacro{\tikzumlRelationPosition}{(\thepos-200)/100}% + \xdef\tikzumlLastArc{\tikzumlLastArc node[pos=\tikzumlRelationPosition, \posAttrName, \attrAlign] {\tikzumlRelationAttrName}% + node[pos=\tikzumlRelationPosition, swap, \posMultiplicity, \multAlign] {\tikzumlRelationMultiplicity}% + }% + \else% + \ifnum\thepos<100% + \xdef\tikzumlFirstArc{\tikzumlFirstArc node[pos=\tikzumlRelationPosition, \posAttrName, \attrAlign] {\tikzumlRelationAttrName}% + node[pos=\tikzumlRelationPosition, swap, \posMultiplicity, \multAlign] {\tikzumlRelationMultiplicity}% + }% + \else% + \pgfmathsetmacro{\tikzumlRelationPosition}{(\thepos-100)/100}% + \xdef\tikzumlMidOneArc{\tikzumlMidOneArc node[pos=\tikzumlRelationPosition, \posAttrName, \attrAlign] {\tikzumlRelationAttrName}% + node[pos=\tikzumlRelationPosition, swap, \posMultiplicity, \multAlign] {\tikzumlRelationMultiplicity}% + }% + \fi% + \fi% + % + \ifthenelse{\theposT=100}{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionStart}{right}}{% + \ifthenelse{\tikzumlRelationEndAngle<\tikzumlRelationStartAngle}{% + \def\posAttrNameT{above right}% + \def\posMultiplicityT{below left}% + }{% + \def\posAttrNameT{above left}% + \def\posMultiplicityT{below right}% + }% + }{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionStart}{left}}{% + \ifthenelse{\tikzumlRelationEndAngle<\tikzumlRelationStartAngle}{% + \def\posAttrNameT{above left}% + \def\posMultiplicityT{below right}% + }{% + \def\posAttrNameT{above right}% + \def\posMultiplicityT{below left}% + }% + }{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionStart}{top}}{% + \ifthenelse{\tikzumlRelationEndAngle<\tikzumlRelationStartAngle}{% + \def\posAttrNameT{above right}% + \def\posMultiplicityT{below left}% + }{% + \def\posAttrNameT{above left}% + \def\posMultiplicityT{below right}% + }% + }{% + \ifthenelse{\tikzumlRelationEndAngle<\tikzumlRelationStartAngle}{% + \def\posAttrNameT{above left}% + \def\posMultiplicityT{below right}% + }{% + \def\posAttrNameT{above right}% + \def\posMultiplicityT{below left}% + }% + }% + }% + }% + }{}% + % + \ifthenelse{\theposT=200}{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionStart}{right}}{% + \ifthenelse{\tikzumlRelationEndAngle<\tikzumlRelationStartAngle}{% + \def\posAttrNameT{above left}% + \def\posMultiplicityT{below right}% + }{% + \def\posAttrNameT{above right}% + \def\posMultiplicityT{below left}% + }% + }{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionStart}{left}}{% + \ifthenelse{\tikzumlRelationEndAngle<\tikzumlRelationStartAngle}{% + \def\posAttrNameT{above right}% + \def\posMultiplicityT{below left}% + }{% + \def\posAttrNameT{above left}% + \def\posMultiplicityT{below right}% + }% + }{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionStart}{top}}{% + \ifthenelse{\tikzumlRelationEndAngle<\tikzumlRelationStartAngle}{% + \def\posAttrNameT{above left}% + \def\posMultiplicityT{below right}% + }{% + \def\posAttrNameT{above right}% + \def\posMultiplicityT{below left}% + }% + }{% + \ifthenelse{\tikzumlRelationEndAngle<\tikzumlRelationStartAngle}{% + \def\posAttrNameT{above right}% + \def\posMultiplicityT{below left}% + }{% + \def\posAttrNameT{above left}% + \def\posMultiplicityT{below right}% + }% + }% + }% + }% + }{}% + % + \ifnum\theposT>200% + \pgfmathsetmacro{\tikzumlRelationPositionT}{(\theposT-200)/100}% + \xdef\tikzumlLastArc{\tikzumlLastArc node[pos=\tikzumlRelationPositionT, \posAttrNameT, \attrAlignT] {\tikzumlRelationAttrNameT}% + node[pos=\tikzumlRelationPositionT, swap, \posMultiplicityT, \multAlignT] {\tikzumlRelationMultiplicityT}% + }% + \else% + \ifnum\theposT<100% + \xdef\tikzumlFirstArc{\tikzumlFirstArc node[pos=\tikzumlRelationPositionT, \posAttrNameT, \attrAlignT] {\tikzumlRelationAttrNameT}% + node[pos=\tikzumlRelationPositionT, swap, \posMultiplicityT, \multAlignT] {\tikzumlRelationMultiplicityT}% + }% + \else% + \pgfmathsetmacro{\tikzumlRelationPositionT}{(\theposT-100)/100}% + \xdef\tikzumlMidOneArc{\tikzumlMidOneArc node[pos=\tikzumlRelationPositionT, \posAttrNameT, \attrAlignT] {\tikzumlRelationAttrNameT}% + node[pos=\tikzumlRelationPositionT, swap, \posMultiplicityT, \multAlignT] {\tikzumlRelationMultiplicityT}% + }% + \fi% + \fi% + \fi% + % + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionStart}{\tikzumlRelationRecursiveDirectionEnd}}{% + \xdef\tikzumlPath{(\tikzumlSrcClassNodeName.\tikzumlRelationStartAngle) -- \tikzumlFirstArc (\tikzumlRelationName-2.center) -- \tikzumlMidOneArc (\tikzumlRelationName-4.center) -- \tikzumlLastArc (\tikzumlDestClassNodeName.\tikzumlRelationEndAngle) }% + \ifnum\thetikzumlStateLevel>0% + \def\tikzumlStateFitTmp{\csname tikzumlStateFit\tikzumlState@parent @@\tikzumlState@fitname\endcsname}% + \expandafter\xdef\csname tikzumlStateFit\tikzumlState@parent @@\tikzumlState@fitname\endcsname{\tikzumlStateFitTmp (\tikzumlRelationName-1) (\tikzumlRelationName-2) (\tikzumlRelationName-3) (\tikzumlRelationName-4) (\tikzumlRelationName-5)}% + \fi% + }{% + \xdef\tikzumlPath{(\tikzumlSrcClassNodeName.\tikzumlRelationStartAngle) -- \tikzumlFirstArc (\tikzumlRelationName-2.center) -- \tikzumlMidOneArc (\tikzumlRelationName-4.center) -- \tikzumlMidTwoArc (\tikzumlRelationName-6.center) -- \tikzumlLastArc (\tikzumlDestClassNodeName.\tikzumlRelationEndAngle) }% + \ifnum\thetikzumlStateLevel>0% + \def\tikzumlStateFitTmp{\csname tikzumlStateFit\tikzumlState@parent @@\tikzumlState@fitname\endcsname}% + \expandafter\xdef\csname tikzumlStateFit\tikzumlState@parent @@\tikzumlState@fitname\endcsname{\tikzumlStateFitTmp (\tikzumlRelationName-1) (\tikzumlRelationName-2) (\tikzumlRelationName-3) (\tikzumlRelationName-4) (\tikzumlRelationName-5) (\tikzumlRelationName-6) (\tikzumlRelationName-7)}% + \fi% + }% + }{}% + }% + }{% + \def\arcNum{1}% + \def\arcNumT{1}% + % + \node[inner sep=0] (\tikzumlRelationName-1) at (\tikzumlRelationName-middle) {};% + \xdef\tikzumlLastArc{node[pos=\tikzumlRelationPosition, \posAttrName, \attrAlign] {\tikzumlRelationAttrName}% + node[pos=\tikzumlRelationPosition, swap, \posMultiplicity, \multAlign] {\tikzumlRelationMultiplicity}% + node[pos=\tikzumlRelationPositionT, \posAttrNameT, \attrAlignT] {\tikzumlRelationAttrNameT}% + node[pos=\tikzumlRelationPositionT, swap, \posMultiplicityT, \multAlignT] {\tikzumlRelationMultiplicityT}% + node[pos=\tikzumlRelationPositionStereotype, anchor=center] {\stereotype} }% + \xdef\tikzumlPath{(\tikzumlSrcClassNodeName\tikzumlRelationSrcAnchor) -- \tikzumlLastArc (\tikzumlDestClassNodeName\tikzumlRelationDestAnchor) }% + \ifnum\thetikzumlStateLevel>0% + \def\tikzumlStateFitTmp{\csname tikzumlStateFit\tikzumlState@parent @@\tikzumlState@fitname\endcsname}% + \expandafter\xdef\csname tikzumlStateFit\tikzumlState@parent @@\tikzumlState@fitname\endcsname{\tikzumlStateFitTmp (\tikzumlRelationName-1) }% + \fi% + }% + }{% + % first vertical then horizontal line + \ifthenelse{\equal{\tikzumlRelationGeometry}{|-}}% + {% + %\setcounter{tikzumlControlNodesNum}{1}% + % + \def\tikzumlFirstArc{node[midway, inner sep=0, name=\tikzumlRelationName-1, anchor=center] {} }% + \def\tikzumlLastArc{node[midway, inner sep=0, name=\tikzumlRelationName-3, anchor=center]{} }% + % + \begin{pgfonlayer}{connections}% + \node[inner sep=0] (\tikzumlRelationName-2) at (\tikzumlSrcClassNodeName\tikzumlRelationSrcAnchor |- \tikzumlDestClassNodeName\tikzumlRelationDestAnchor) {};% + \end{pgfonlayer}% + % + \ifnum\theposStereo>100% + \pgfmathsetmacro{\tikzumlRelationPositionStereotype}{(\theposStereo-100)/100}% + \xdef\tikzumlLastArc{\tikzumlLastArc node[pos=\tikzumlRelationPositionStereotype, anchor=center] {\stereotype} }% + \else% + \xdef\tikzumlFirstArc{\tikzumlFirstArc node[pos=\tikzumlRelationPositionStereotype, anchor=center] {\stereotype} }% + \fi% + % + \ifnum\thepos>100% + \pgfmathsetmacro{\tikzumlRelationPosition}{(\thepos-100)/100}% + \def\arcNum{2}% + \else% + \def\arcNum{1}% + \ifnum\thepos=100% + \def\posAttrName{above left}% + \def\posMultiplicity{below right}% + \fi% + \fi% + % + \ifnum\arcNum=1% + \xdef\tikzumlFirstArc{\tikzumlFirstArc node[pos=\tikzumlRelationPosition, \posAttrName, \attrAlign] {\tikzumlRelationAttrName}% + node[pos=\tikzumlRelationPosition, swap, \posMultiplicity, \multAlign] {\tikzumlRelationMultiplicity} }% + \fi% + \ifnum\arcNum=2% + \xdef\tikzumlLastArc{\tikzumlLastArc node[pos=\tikzumlRelationPosition, \posAttrName, \attrAlign] {\tikzumlRelationAttrName}% + node[pos=\tikzumlRelationPosition, swap, \posMultiplicity, \multAlign] {\tikzumlRelationMultiplicity} }% + \fi% + % + \ifnum\theposT>100% + \pgfmathsetmacro{\tikzumlRelationPositionT}{(\theposT-100)/100}% + \def\arcNumT{2}% + \else% + \def\arcNumT{1}% + \ifnum\theposT=100% + \def\posAttrNameT{above left}% + \def\posMultiplicityT{below right}% + \fi% + \fi% + % + \ifnum\arcNumT=1% + \xdef\tikzumlFirstArc{\tikzumlFirstArc node[pos=\tikzumlRelationPositionT, \posAttrNameT, \attrAlignT] {\tikzumlRelationAttrNameT}% + node[pos=\tikzumlRelationPositionT, swap, \posMultiplicityT, \multAlignT] {\tikzumlRelationMultiplicityT} }% + \fi% + \ifnum\arcNumT=2% + \xdef\tikzumlLastArc{\tikzumlLastArc node[pos=\tikzumlRelationPositionT, \posAttrNameT, \attrAlignT] {\tikzumlRelationAttrNameT}% + node[pos=\tikzumlRelationPositionT, swap, \posMultiplicityT, \multAlignT] {\tikzumlRelationMultiplicityT} }% + \fi% + % + \xdef\tikzumlPath{(\tikzumlSrcClassNodeName\tikzumlRelationSrcAnchor) -- \tikzumlFirstArc (\tikzumlRelationName-2.base) -- \tikzumlLastArc (\tikzumlDestClassNodeName\tikzumlRelationDestAnchor) }% + \ifnum\thetikzumlStateLevel>0% + \def\tikzumlStateFitTmp{\csname tikzumlStateFit\tikzumlState@parent @@\tikzumlState@fitname\endcsname}% + \expandafter\xdef\csname tikzumlStateFit\tikzumlState@parent @@\tikzumlState@fitname\endcsname{\tikzumlStateFitTmp (\tikzumlRelationName-1) (\tikzumlRelationName-2) (\tikzumlRelationName-3) }% + \fi% + }{% + % first horizontal then vertical line + \ifthenelse{\equal{\tikzumlRelationGeometry}{-|}}% + {% + %\setcounter{tikzumlControlNodesNum}{1}% + % + \def\tikzumlFirstArc{node[midway, inner sep=0, name=\tikzumlRelationName-1, anchor=center]{} }% + \def\tikzumlLastArc{node[midway, inner sep=0, name=\tikzumlRelationName-3, anchor=center] {} }% + % + \begin{pgfonlayer}{connections}% + \node[inner sep=0] (\tikzumlRelationName-2) at (\tikzumlSrcClassNodeName\tikzumlRelationSrcAnchor -| \tikzumlDestClassNodeName\tikzumlRelationDestAnchor) {};% + \end{pgfonlayer}% + % + \ifnum\theposStereo>100% + \pgfmathsetmacro{\tikzumlRelationPositionStereotype}{(\theposStereo-100)/100}% + \xdef\tikzumlLastArc{\tikzumlLastArc node[pos=\tikzumlRelationPositionStereotype, anchor=center] {\stereotype} }% + \else% + \xdef\tikzumlFirstArc{\tikzumlFirstArc node[pos=\tikzumlRelationPositionStereotype, anchor=center] {\stereotype} }% + \fi% + % + \ifnum\thepos>100% + \pgfmathsetmacro{\tikzumlRelationPosition}{(\thepos-100)/100}% + \def\arcNum{2}% + \else% + \def\arcNum{1}% + \ifnum\thepos=100% + \def\posAttrName{above left}% + \def\posMultiplicity{below right}% + \fi% + \fi% + % + \ifnum\arcNum=1% + \xdef\tikzumlFirstArc{\tikzumlFirstArc node[pos=\tikzumlRelationPosition, \posAttrName, \attrAlign] {\tikzumlRelationAttrName}% + node[pos=\tikzumlRelationPosition, swap, \posMultiplicity, \multAlign] {\tikzumlRelationMultiplicity} }% + \fi% + \ifnum\arcNum=2% + \xdef\tikzumlLastArc{\tikzumlLastArc node[pos=\tikzumlRelationPosition, \posAttrName, \attrAlign] {\tikzumlRelationAttrName}% + node[pos=\tikzumlRelationPosition, swap, \posMultiplicity, \multAlign] {\tikzumlRelationMultiplicity} }% + \fi% + % + \ifnum\theposT>100% + \pgfmathsetmacro{\tikzumlRelationPositionT}{(\theposT-100)/100}% + \def\arcNumT{2}% + \else% + \def\arcNumT{1}% + \ifnum\theposT=100% + \def\posAttrNameT{above left}% + \def\posMultiplicityT{below right}% + \fi% + \fi% + % + \ifnum\arcNumT=1% + \xdef\tikzumlFirstArc{\tikzumlFirstArc node[pos=\tikzumlRelationPositionT, \posAttrNameT, \attrAlignT] {\tikzumlRelationAttrNameT}% + node[pos=\tikzumlRelationPositionT, swap, \posMultiplicityT, \multAlignT] {\tikzumlRelationMultiplicityT} }% + \fi% + \ifnum\arcNumT=2% + \xdef\tikzumlLastArc{\tikzumlLastArc node[pos=\tikzumlRelationPositionT, \posAttrNameT, \attrAlignT] {\tikzumlRelationAttrNameT}% + node[pos=\tikzumlRelationPositionT, swap, \posMultiplicityT, \multAlignT] {\tikzumlRelationMultiplicityT} }% + \fi% + % + \xdef\tikzumlPath{(\tikzumlSrcClassNodeName\tikzumlRelationSrcAnchor) -- \tikzumlFirstArc (\tikzumlRelationName-2.base) -- \tikzumlLastArc (\tikzumlDestClassNodeName\tikzumlRelationDestAnchor) }% + \ifnum\thetikzumlStateLevel>0% + \def\tikzumlStateFitTmp{\csname tikzumlStateFit\tikzumlState@parent @@\tikzumlState@fitname\endcsname}% + \expandafter\xdef\csname tikzumlStateFit\tikzumlState@parent @@\tikzumlState@fitname\endcsname{\tikzumlStateFitTmp (\tikzumlRelationName-1) (\tikzumlRelationName-2) (\tikzumlRelationName-3) }% + \fi% + }{% + % first vertical, then horizontal, finally vertical line + \ifthenelse{\equal{\tikzumlRelationGeometry}{|-|}}% + {% + %\setcounter{tikzumlControlNodesNum}{2}% + % + \def\tikzumlFirstArc{node[midway, inner sep=0, name=\tikzumlRelationName-1, anchor=center] {} }% + \def\tikzumlLastArc{node[midway, inner sep=0, name=\tikzumlRelationName-5, anchor=center] {} }% + \def\tikzumlMidOneArc{ }% + % + \begin{pgfonlayer}{connections}% + % + \ifthenelse{\equal{\tikzumlRelationArmO}{auto}}{% + \ifthenelse{\equal{\tikzumlRelationArmT}{auto}}{% + \node[inner sep=0] (\tikzumlRelationName-3) at (\tikzumlRelationName-middle) {};% + \node[inner sep=0] (\tikzumlRelationName-2) at (\tikzumlSrcClassNodeName\tikzumlRelationSrcAnchor |- \tikzumlRelationName-3) {};% + \node[inner sep=0] (\tikzumlRelationName-4) at (\tikzumlRelationName-3 -| \tikzumlDestClassNodeName\tikzumlRelationDestAnchor) {};% + }{% + \draw (\tikzumlDestClassNodeName\tikzumlRelationDestAnchor)+(0,\tikzumlRelationArmT) node[inner sep=0, name=\tikzumlRelationName-4] {};% + \node[inner sep=0] (\tikzumlRelationName-2) at (\tikzumlRelationName-4 -| \tikzumlSrcClassNodeName\tikzumlRelationSrcAnchor) {};% + \node[inner sep=0] (\tikzumlRelationName-3) at (barycentric cs:\tikzumlRelationName-2=0.5,\tikzumlRelationName-4=0.5) {};% + }% + }{% + \draw (\tikzumlSrcClassNodeName\tikzumlRelationSrcAnchor)+(0,\tikzumlRelationArmO) node[inner sep=0, name=\tikzumlRelationName-2] {};% + \node[inner sep=0] (\tikzumlRelationName-4) at (\tikzumlRelationName-2 -| \tikzumlDestClassNodeName\tikzumlRelationDestAnchor) {};% + \node[inner sep=0] (\tikzumlRelationName-3) at (barycentric cs:\tikzumlRelationName-2=0.5,\tikzumlRelationName-4=0.5) {};% + }% + \end{pgfonlayer}% + % + \ifnum\theposStereo>200% + \pgfmathsetmacro{\tikzumlRelationPositionStereotype}{(\theposStereo-200)/100}% + \xdef\tikzumlLastArc{\tikzumlLastArc node[pos=\tikzumlRelationPositionStereotype, anchor=center] {\stereotype} }% + \else% + \ifnum\theposStereo<100% + \xdef\tikzumlFirstArc{\tikzumlFirstArc node[pos=\tikzumlRelationPositionStereotype, anchor=center] {\stereotype} }% + \else% + \pgfmathsetmacro{\tikzumlRelationPositionStereotype}{(\theposStereo-100)/100}% + \xdef\tikzumlMidOneArc{\tikzumlMidOneArc node[pos=\tikzumlRelationPositionStereotype, anchor=center] {\stereotype} }% + \fi% + \fi% + % + \ifthenelse{\thepos=200\OR\thepos=100}{% + \def\posAttrName{above left}% + \def\posMultiplicity{below right}% + }{}% + % + \ifthenelse{\thepos>200}{% + \pgfmathsetmacro{\tikzumlRelationPosition}{(\thepos-200)/100}% + \def\arcNum{3}% + }{% + \ifthenelse{\thepos<100}{% + \def\arcNum{1}% + }{% + \pgfmathsetmacro{\tikzumlRelationPosition}{(\thepos-100)/100}% + \def\arcNum{2}% + }% + }% + % + \ifnum\arcNum=1% + \xdef\tikzumlFirstArc{\tikzumlFirstArc node[pos=\tikzumlRelationPosition, \posAttrName, \attrAlign] {\tikzumlRelationAttrName}% + node[pos=\tikzumlRelationPosition, swap, \posMultiplicity, \multAlign] {\tikzumlRelationMultiplicity} }% + \fi% + \ifnum\arcNum=2% + \xdef\tikzumlMidOneArc{\tikzumlMidOneArc node[pos=\tikzumlRelationPosition, \posAttrName, \attrAlign] {\tikzumlRelationAttrName}% + node[pos=\tikzumlRelationPosition, swap, \posMultiplicity, \multAlign] {\tikzumlRelationMultiplicity} }% + \fi% + \ifnum\arcNum=3% + \xdef\tikzumlLastArc{\tikzumlLastArc node[pos=\tikzumlRelationPosition, \posAttrName, \attrAlign] {\tikzumlRelationAttrName}% + node[pos=\tikzumlRelationPosition, swap, \posMultiplicity, \multAlign] {\tikzumlRelationMultiplicity} }% + \fi% + % + \ifthenelse{\theposT=200\OR\theposT=100}{% + \def\posAttrNameT{above left}% + \def\posMultiplicityT{below right}% + }{}% + % + \ifthenelse{\theposT>200}{% + \pgfmathsetmacro{\tikzumlRelationPositionT}{(\theposT-200)/100}% + \def\arcNumT{3}% + }{% + \ifthenelse{\theposT<100}{% + \def\arcNumT{1}% + }{% + \pgfmathsetmacro{\tikzumlRelationPositionT}{(\theposT-100)/100}% + \def\arcNumT{2}% + }% + }% + % + \ifnum\arcNumT=1% + \xdef\tikzumlFirstArc{\tikzumlFirstArc node[pos=\tikzumlRelationPositionT, \posAttrNameT, \attrAlignT] {\tikzumlRelationAttrNameT}% + node[pos=\tikzumlRelationPositionT, swap, \posMultiplicityT, \multAlignT] {\tikzumlRelationMultiplicityT} }% + \fi% + \ifnum\arcNumT=2% + \xdef\tikzumlMidOneArc{\tikzumlMidOneArc node[pos=\tikzumlRelationPositionT, \posAttrNameT, \attrAlignT] {\tikzumlRelationAttrNameT}% + node[pos=\tikzumlRelationPositionT, swap, \posMultiplicityT, \multAlignT] {\tikzumlRelationMultiplicityT} }% + \fi% + \ifnum\arcNumT=3% + \xdef\tikzumlLastArc{\tikzumlLastArc node[pos=\tikzumlRelationPositionT, \posAttrNameT, \attrAlignT] {\tikzumlRelationAttrNameT}% + node[pos=\tikzumlRelationPositionT, swap, \posMultiplicityT, \multAlignT] {\tikzumlRelationMultiplicityT} }% + \fi% + % + \xdef\tikzumlPath{(\tikzumlSrcClassNodeName\tikzumlRelationSrcAnchor) -- \tikzumlFirstArc (\tikzumlRelationName-2.base) -- \tikzumlMidOneArc (\tikzumlRelationName-4.base) -- \tikzumlLastArc (\tikzumlDestClassNodeName\tikzumlRelationDestAnchor) }% + \ifnum\thetikzumlStateLevel>0% + \def\tikzumlStateFitTmp{\csname tikzumlStateFit\tikzumlState@parent @@\tikzumlState@fitname\endcsname}% + \expandafter\xdef\csname tikzumlStateFit\tikzumlState@parent @@\tikzumlState@fitname\endcsname{\tikzumlStateFitTmp (\tikzumlRelationName-1) (\tikzumlRelationName-2) (\tikzumlRelationName-3) (\tikzumlRelationName-4) (\tikzumlRelationName-5) }% + \fi% + }{% + % first horizontal, then vertical, finally horizontal line + \ifthenelse{\equal{\tikzumlRelationGeometry}{-|-}}% + {% + %\setcounter{tikzumlControlNodesNum}{2}% + % + \def\tikzumlFirstArc{node[midway, inner sep=0, name=\tikzumlRelationName-1, anchor=center] {} }% + \def\tikzumlLastArc{node[midway, inner sep=0, name=\tikzumlRelationName-5, anchor=center] {} }% + \def\tikzumlMidOneArc{}% + % + \begin{pgfonlayer}{connections}% + % + \ifthenelse{\equal{\tikzumlRelationArmO}{auto}}{% + \ifthenelse{\equal{\tikzumlRelationArmT}{auto}}{% + \node[inner sep=0] (\tikzumlRelationName-3) at (\tikzumlRelationName-middle) {};% + \node[inner sep=0] (\tikzumlRelationName-2) at (\tikzumlSrcClassNodeName\tikzumlRelationSrcAnchor -| \tikzumlRelationName-3) {};% + \node[inner sep=0] (\tikzumlRelationName-4) at (\tikzumlRelationName-3 |- \tikzumlDestClassNodeName\tikzumlRelationDestAnchor) {};% + }{% + \draw (\tikzumlDestClassNodeName\tikzumlRelationDestAnchor)+(\tikzumlRelationArmT,0) node[inner sep=0, name=\tikzumlRelationName-4] {};% + \node[inner sep=0] (\tikzumlRelationName-2) at (\tikzumlRelationName-4 |- \tikzumlSrcClassNodeName\tikzumlRelationSrcAnchor) {};% + \node[inner sep=0] (\tikzumlRelationName-3) at (barycentric cs:\tikzumlRelationName-2=0.5,\tikzumlRelationName-4=0.5) {};% + }% + }{% + \draw (\tikzumlSrcClassNodeName\tikzumlRelationSrcAnchor)+(\tikzumlRelationArmO,0) node[inner sep=0, name=\tikzumlRelationName-2] {};% + \node[inner sep=0] (\tikzumlRelationName-4) at (\tikzumlRelationName-2 |- \tikzumlDestClassNodeName\tikzumlRelationDestAnchor) {};% + \node[inner sep=0] (\tikzumlRelationName-3) at (barycentric cs:\tikzumlRelationName-2=0.5,\tikzumlRelationName-4=0.5) {};% + }% + \end{pgfonlayer}% + % + \ifnum\theposStereo>200% + \pgfmathsetmacro{\tikzumlRelationPositionStereotype}{(\theposStereo-200)/100}% + \xdef\tikzumlLastArc{\tikzumlLastArc node[pos=\tikzumlRelationPositionStereotype, anchor=center] {\stereotype} }% + \else% + \ifnum\theposStereo<100% + \xdef\tikzumlFirstArc{\tikzumlFirstArc node[pos=\tikzumlRelationPositionStereotype, anchor=center] {\stereotype} }% + \else% + \pgfmathsetmacro{\tikzumlRelationPositionStereotype}{(\theposStereo-100)/100}% + \xdef\tikzumlMidOneArc{\tikzumlMidOneArc node[pos=\tikzumlRelationPositionStereotype, anchor=center] {\stereotype} }% + \fi% + \fi% + % + \ifthenelse{\thepos=200\OR\thepos=100}{% + \def\posAttrName{above left}% + \def\posMultiplicity{below right}% + }{}% + % + \ifthenelse{\thepos>200}{% + \pgfmathsetmacro{\tikzumlRelationPosition}{(\thepos-200)/100}% + \def\arcNum{3}% + }{% + \ifthenelse{\thepos<100}{% + \def\arcNum{1}% + }{% + \pgfmathsetmacro{\tikzumlRelationPosition}{(\thepos-100)/100}% + \def\arcNum{2}% + }% + }% + % + \ifnum\arcNum=1% + \xdef\tikzumlFirstArc{\tikzumlFirstArc node[pos=\tikzumlRelationPosition, \posAttrName, \attrAlign] {\tikzumlRelationAttrName}% + node[pos=\tikzumlRelationPosition, swap, \posMultiplicity, \multAlign] {\tikzumlRelationMultiplicity} }% + \fi% + \ifnum\arcNum=2% + \xdef\tikzumlMidOneArc{\tikzumlMidOneArc node[pos=\tikzumlRelationPosition, \posAttrName, \attrAlign] {\tikzumlRelationAttrName}% + node[pos=\tikzumlRelationPosition, swap, \posMultiplicity, \multAlign] {\tikzumlRelationMultiplicity} }% + \fi% + \ifnum\arcNum=3% + \xdef\tikzumlLastArc{\tikzumlLastArc node[pos=\tikzumlRelationPosition, \posAttrName, \attrAlign] {\tikzumlRelationAttrName}% + node[pos=\tikzumlRelationPosition, swap, \posMultiplicity, \multAlign] {\tikzumlRelationMultiplicity} }% + \fi% + % + \ifthenelse{\theposT=200\OR\theposT=100}{% + \def\posAttrNameT{above left}% + \def\posMultiplicityT{below right}% + }{}% + % + \ifthenelse{\theposT>200}{% + \pgfmathsetmacro{\tikzumlRelationPositionT}{(\theposT-200)/100}% + \def\arcNumT{3}% + }{% + \ifthenelse{\theposT<100}{% + \def\arcNumT{1}% + }{% + \pgfmathsetmacro{\tikzumlRelationPositionT}{(\theposT-100)/100}% + \def\arcNumT{2}% + }% + }% + % + \ifnum\arcNumT=1% + \xdef\tikzumlFirstArc{\tikzumlFirstArc node[pos=\tikzumlRelationPositionT, \posAttrNameT, \attrAlignT] {\tikzumlRelationAttrNameT}% + node[pos=\tikzumlRelationPositionT, swap, \posMultiplicityT, \multAlignT] {\tikzumlRelationMultiplicityT} }% + \fi% + \ifnum\arcNumT=2% + \xdef\tikzumlMidOneArc{\tikzumlMidOneArc node[pos=\tikzumlRelationPositionT, \posAttrNameT, \attrAlignT] {\tikzumlRelationAttrNameT}% + node[pos=\tikzumlRelationPositionT, swap, \posMultiplicityT, \multAlignT] {\tikzumlRelationMultiplicityT} }% + \fi% + \ifnum\arcNumT=3% + \xdef\tikzumlLastArc{\tikzumlLastArc node[pos=\tikzumlRelationPositionT, \posAttrNameT, \attrAlignT] {\tikzumlRelationAttrNameT}% + node[pos=\tikzumlRelationPositionT, swap, \posMultiplicityT, \multAlignT] {\tikzumlRelationMultiplicityT} }% + \fi% + % + \xdef\tikzumlPath{(\tikzumlSrcClassNodeName\tikzumlRelationSrcAnchor) -- \tikzumlFirstArc (\tikzumlRelationName-2.base) -- \tikzumlMidOneArc (\tikzumlRelationName-4.base) -- \tikzumlLastArc (\tikzumlDestClassNodeName\tikzumlRelationDestAnchor) }% + \ifnum\thetikzumlStateLevel>0% + \def\tikzumlStateFitTmp{\csname tikzumlStateFit\tikzumlState@parent @@\tikzumlState@fitname\endcsname}% + \expandafter\xdef\csname tikzumlStateFit\tikzumlState@parent @@\tikzumlState@fitname\endcsname{\tikzumlStateFitTmp (\tikzumlRelationName-1) (\tikzumlRelationName-2) (\tikzumlRelationName-3) (\tikzumlRelationName-4) (\tikzumlRelationName-5) }% + \fi% + }{% + \errmessage{TIKZUML ERROR : Unknown geometry value !!! It should be in the following list : --, |-, -|, |-|, -|-}% + }% + }% + }% + }% + }% + % + \begin{pgfonlayer}{connections}% + \ifthenelse{\equal{\tikzumlRelationStyle}{tikzuml nesting style}}{% + \pgfarrowsdeclare{nested}{nested}{...} + { + \tikzumlNestingSymbolSize=0.2pt% + \advance\tikzumlNestingSymbolSize by .5\pgflinewidth% + \pgfsetdash{}{0pt} % do not dash + \pgfsetroundjoin % fix join + \pgfsetroundcap % fix cap + \pgfpathmoveto{\pgfpoint{-16*\tikzumlNestingSymbolSize}{0pt}}% + \pgfpatharc{180}{90}{8*\tikzumlNestingSymbolSize}% + \pgfpatharc{90}{0}{8*\tikzumlNestingSymbolSize}% + \pgfpatharc{0}{-90}{8*\tikzumlNestingSymbolSize}% + \pgfpatharc{-90}{-180}{8*\tikzumlNestingSymbolSize}% + \pgfpathmoveto{\pgfpoint{-8*\tikzumlNestingSymbolSize}{8*\tikzumlNestingSymbolSize}}% + \pgfpathlineto{\pgfpoint{-8*\tikzumlNestingSymbolSize}{-8*\tikzumlNestingSymbolSize}}% + \pgfusepathqstroke% + }% + \draw[auto, nested-, font=\tikzumlDefaultFont, \tikzumlRelationStyle, /tikzuml/relation/style2] \tikzumlPath ;% + }{ + \draw[auto, font=\tikzumlDefaultFont, \tikzumlRelationStyle, /tikzuml/relation/style2] \tikzumlPath ;% + } + \end{pgfonlayer}% + % + \stepcounter{tikzumlRelationNum}% +}% +% +% shortcuts of \umlrelation +\newcommand{\umlHVrelation}[3][]{% + \pgfkeys{/tikzuml/HVrelation/.cd,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{geometry}}{% + \errmessage{TIKZUML ERROR : in umlHVrelation, forbidden option geometry}% + }{}% + }}% + \pgfkeys{/tikzuml/HVrelation/.cd, #1}% + \umlrelation[geometry=-|, #1]{#2}{#3}% +}% +% +\newcommand{\umlVHrelation}[3][]{% + \pgfkeys{/tikzuml/VHrelation/.cd,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{geometry}}{% + \errmessage{TIKZUML ERROR : in umlVHrelation, forbidden option geometry}% + }{}% + }}% + \pgfkeys{/tikzuml/VHrelation/.cd, #1}% + \umlrelation[geometry=|-, #1]{#2}{#3}% +}% +% +\newcommand{\umlHVHrelation}[3][]{% + \pgfkeys{/tikzuml/HVHrelation/.cd,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{geometry}}{% + \errmessage{TIKZUML ERROR : in umlHVHrelation, forbidden option geometry}% + }{}% + }}% + \pgfkeys{/tikzuml/HVHrelation/.cd, #1}% + \umlrelation[geometry=-|-, #1]{#2}{#3}% +}% +% +\newcommand{\umlVHVrelation}[3][]{% + \pgfkeys{/tikzuml/VHVrelation/.cd,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{geometry}}{% + \errmessage{TIKZUML ERROR : in umlVHVrelation, forbidden option geometry}% + }{}% + }}% + \pgfkeys{/tikzuml/VHVrelation/.cd, #1}% + \umlrelation[geometry=|-|, #1]{#2}{#3}% +}% +% +% +% shortcuts for relations +\newcommand{\umlinherit}[3][]{\umlrelation[style={tikzuml inherit style}, #1]{#2}{#3}}% +\newcommand{\umlimpl}[3][]{\umlrelation[style={tikzuml implements style}, #1]{#2}{#3}}% +\newcommand{\umlreal}[3][]{\umlrelation[style={tikzuml implements style}, #1]{#2}{#3}}% +\newcommand{\umlassoc}[3][]{\umlrelation[style={tikzuml association style}, #1]{#2}{#3}}% +\newcommand{\umlbiassoc}[3][]{\umlrelation[style={tikzuml bidirectional association style}, #1]{#2}{#3}}% +\newcommand{\umluniassoc}[3][]{\umlrelation[style={tikzuml unidirectional association style}, #1]{#2}{#3}}% +\newcommand{\umlaggreg}[3][]{\umlrelation[style={tikzuml aggregation style}, #1]{#2}{#3}}% +\newcommand{\umluniaggreg}[3][]{\umlrelation[style={tikzuml unidirectional aggregation style}, #1]{#2}{#3}}% +\newcommand{\umlcompo}[3][]{\umlrelation[style={tikzuml composition style}, #1]{#2}{#3}}% +\newcommand{\umlunicompo}[3][]{\umlrelation[style={tikzuml unidirectional composition style}, #1]{#2}{#3}}% +\newcommand{\umlimport}[3][]{\umlrelation[style={tikzuml import style}, #1]{#2}{#3}}% +\newcommand{\umlnest}[3][]{\umlrelation[style={tikzuml nesting style}, #1]{#2}{#3}}% +\newcommand{\umldep}[3][]{\umlrelation[style={tikzuml dependency style}, #1]{#2}{#3}}% +\newcommand{\umlfriend}[3][]{% + \pgfkeys{/tikzuml/friendrelation/.cd,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{stereo}}{% + \errmessage{TIKZUML ERROR : in umlfriend, forbidden option stereo}% + }{}% + }}% + \pgfkeys{/tikzuml/friendrelation/.cd, #1}% + \umlrelation[stereo=friend, style={tikzuml dependency style}, #1]{#2}{#3}% +}% +% +\newcommand{\umlHVinherit}[3][]{\umlHVrelation[style={tikzuml inherit style}, #1]{#2}{#3}}% +\newcommand{\umlHVimpl}[3][]{\umlHVrelation[style={tikzuml implements style}, #1]{#2}{#3}}% +\newcommand{\umlHVreal}[3][]{\umlHVrelation[style={tikzuml implements style}, #1]{#2}{#3}}% +\newcommand{\umlHVassoc}[3][]{\umlHVrelation[style={tikzuml association style}, #1]{#2}{#3}}% +\newcommand{\umlHVuniassoc}[3][]{\umlHVrelation[style={tikzuml unidirectional association style}, #1]{#2}{#3}}% +\newcommand{\umlHVaggreg}[3][]{\umlHVrelation[style={tikzuml aggregation style}, #1]{#2}{#3}}% +\newcommand{\umlHVuniaggreg}[3][]{\umlHVrelation[style={tikzuml unidirectional aggregation style}, #1]{#2}{#3}}% +\newcommand{\umlHVcompo}[3][]{\umlHVrelation[style={tikzuml composition style}, #1]{#2}{#3}}% +\newcommand{\umlHVunicompo}[3][]{\umlHVrelation[style={tikzuml unidirectional composition style}, #1]{#2}{#3}}% +\newcommand{\umlHVimport}[3][]{\umlHVrelation[style={tikzuml import style}, #1]{#2}{#3}}% +\newcommand{\umlHVnest}[3][]{\umlHVrelation[style={tikzuml nesting style}, #1]{#2}{#3}}% +\newcommand{\umlHVdep}[3][]{\umlHVrelation[style={tikzuml dependency style}, #1]{#2}{#3}}% +\newcommand{\umlHVfriend}[3][]{% + \pgfkeys{/tikzuml/friendrelation/.cd,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{stereo}}{% + \errmessage{TIKZUML ERROR : in umlHVfriend, forbidden option stereo}% + }{% + \ifthenelse{\equal{\keyname}{geometry}}{% + \errmessage{TIKZUML ERROR : in umlHVfriend, forbidden option geometry}% + }{}% + }% + }}% + \pgfkeys{/tikzuml/friendrelation/.cd, #1}% + \umlrelation[geometry=-|, stereo=friend, style={tikzuml dependency style}, #1]{#2}{#3}% +}% +% +\newcommand{\umlVHinherit}[3][]{\umlVHrelation[style={tikzuml inherit style}, #1]{#2}{#3}}% +\newcommand{\umlVHimpl}[3][]{\umlVHrelation[style={tikzuml implements style}, #1]{#2}{#3}}% +\newcommand{\umlVHreal}[3][]{\umlVHrelation[style={tikzuml implements style}, #1]{#2}{#3}}% +\newcommand{\umlVHassoc}[3][]{\umlVHrelation[style={tikzuml association style}, #1]{#2}{#3}}% +\newcommand{\umlVHuniassoc}[3][]{\umlVHrelation[style={tikzuml unidirectional association style}, #1]{#2}{#3}}% +\newcommand{\umlVHaggreg}[3][]{\umlVHrelation[style={tikzuml aggregation style}, #1]{#2}{#3}}% +\newcommand{\umlVHuniaggreg}[3][]{\umlVHrelation[style={tikzuml unidirectional aggregation style}, #1]{#2}{#3}}% +\newcommand{\umlVHcompo}[3][]{\umlVHrelation[style={tikzuml composition style}, #1]{#2}{#3}}% +\newcommand{\umlVHunicompo}[3][]{\umlVHrelation[style={tikzuml unidirectional composition style}, #1]{#2}{#3}}% +\newcommand{\umlVHimport}[3][]{\umlVHrelation[style={tikzuml import style}, #1]{#2}{#3}}% +\newcommand{\umlVHnest}[3][]{\umlVHrelation[style={tikzuml nesting style}, #1]{#2}{#3}}% +\newcommand{\umlVHdep}[3][]{\umlVHrelation[style={tikzuml dependency style}, #1]{#2}{#3}}% +\newcommand{\umlVHfriend}[3][]{% + \pgfkeys{/tikzuml/friendrelation/.cd,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{stereo}}{% + \errmessage{TIKZUML ERROR : in umlVHfriend, forbidden option stereo}% + }{% + \ifthenelse{\equal{\keyname}{geometry}}{% + \errmessage{TIKZUML ERROR : in umlVHfriend, forbidden option geometry}% + }{}% + }% + }}% + \pgfkeys{/tikzuml/friendrelation/.cd, #1}% + \umlrelation[geometry=|-, stereo=friend, style={tikzuml dependency style}, #1]{#2}{#3}% +}% +% +\newcommand{\umlHVHinherit}[3][]{\umlHVHrelation[style={tikzuml inherit style}, #1]{#2}{#3}}% +\newcommand{\umlHVHimpl}[3][]{\umlHVHrelation[style={tikzuml implements style}, #1]{#2}{#3}}% +\newcommand{\umlHVHreal}[3][]{\umlHVHrelation[style={tikzuml implements style}, #1]{#2}{#3}}% +\newcommand{\umlHVHassoc}[3][]{\umlHVHrelation[style={tikzuml association style}, #1]{#2}{#3}}% +\newcommand{\umlHVHuniassoc}[3][]{\umlHVHrelation[style={tikzuml unidirectional association style}, #1]{#2}{#3}}% +\newcommand{\umlHVHaggreg}[3][]{\umlHVHrelation[style={tikzuml aggregation style}, #1]{#2}{#3}}% +\newcommand{\umlHVHuniaggreg}[3][]{\umlHVHrelation[style={tikzuml unidirectional aggregation style}, #1]{#2}{#3}}% +\newcommand{\umlHVHcompo}[3][]{\umlHVHrelation[style={tikzuml composition style}, #1]{#2}{#3}}% +\newcommand{\umlHVHunicompo}[3][]{\umlHVHrelation[style={tikzuml unidirectional composition style}, #1]{#2}{#3}}% +\newcommand{\umlHVHimport}[3][]{\umlHVHrelation[style={tikzuml import style}, #1]{#2}{#3}}% +\newcommand{\umlHVHnest}[3][]{\umlHVHrelation[style={tikzuml nesting style}, #1]{#2}{#3}}% +\newcommand{\umlHVHdep}[3][]{\umlHVHrelation[style={tikzuml dependency style}, #1]{#2}{#3}}% +\newcommand{\umlHVHfriend}[3][]{% + \pgfkeys{/tikzuml/friendrelation/.cd,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{stereo}}{% + \errmessage{TIKZUML ERROR : in umlHVHfriend, forbidden option stereo}% + }{% + \ifthenelse{\equal{\keyname}{geometry}}{% + \errmessage{TIKZUML ERROR : in umlHVHfriend, forbidden option geometry}% + }{}% + }% + }}% + \pgfkeys{/tikzuml/friendrelation/.cd, #1}% + \umlrelation[geometry=-|-, stereo=friend, style={tikzuml dependency style}, #1]{#2}{#3}% +}% +% +\newcommand{\umlVHVinherit}[3][]{\umlVHVrelation[style={tikzuml inherit style}, #1]{#2}{#3}}% +\newcommand{\umlVHVimpl}[3][]{\umlVHVrelation[style={tikzuml implements style}, #1]{#2}{#3}}% +\newcommand{\umlVHVreal}[3][]{\umlVHVrelation[style={tikzuml implements style}, #1]{#2}{#3}}% +\newcommand{\umlVHVassoc}[3][]{\umlVHVrelation[style={tikzuml association style}, #1]{#2}{#3}}% +\newcommand{\umlVHVuniassoc}[3][]{\umlVHVrelation[style={tikzuml unidirectional association style}, #1]{#2}{#3}}% +\newcommand{\umlVHVaggreg}[3][]{\umlVHVrelation[style={tikzuml aggregation style}, #1]{#2}{#3}}% +\newcommand{\umlVHVuniaggreg}[3][]{\umlVHVrelation[style={tikzuml unidirectional aggregation style}, #1]{#2}{#3}}% +\newcommand{\umlVHVcompo}[3][]{\umlVHVrelation[style={tikzuml composition style}, #1]{#2}{#3}}% +\newcommand{\umlVHVunicompo}[3][]{\umlVHVrelation[style={tikzuml unidirectional composition style}, #1]{#2}{#3}}% +\newcommand{\umlVHVimport}[3][]{\umlVHVrelation[style={tikzuml import style}, #1]{#2}{#3}}% +\newcommand{\umlVHVnest}[3][]{\umlVHVrelation[style={tikzuml nesting style}, #1]{#2}{#3}}% +\newcommand{\umlVHVdep}[3][]{\umlVHVrelation[style={tikzuml dependency style}, #1]{#2}{#3}}% +\newcommand{\umlVHVfriend}[3][]{% + \pgfkeys{/tikzuml/friendrelation/.cd,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{stereo}}{% + \errmessage{TIKZUML ERROR : in umlVHVfriend, forbidden option stereo}% + }{% + \ifthenelse{\equal{\keyname}{geometry}}{% + \errmessage{TIKZUML ERROR : in umlVHVfriend, forbidden option geometry}% + }{}% + }% + }}% + \pgfkeys{/tikzuml/friendrelation/.cd, #1}% + \umlrelation[geometry=|-|, stereo=friend, style={tikzuml dependency style}, #1]{#2}{#3}% +}% +% +% define a node +\newcommand{\umlnode}[2]{% + \node (#2) at (#1) {};% +}% +% +% main command to define a relation between two classes through a control node +% args : src class +% control node +% dest class +% optional : arg1, arg2, arg: name of the src/dest/dest side class type attribute defined by the relation +% mult1, mult2, mult: multiplicity of the src/dest/dest side class type attribute defined by the relation +% pos1, pos2, pos: position of the src/dest/dest side class type attribute defined by the relation +% align1, align2, align: text justification of the src/dest/dest side class type attribute defined by the relation +% anchor1, anchor2: src/dest anchors on linked classes +% stereo: stereotype of the relation +% pos stereo: position of the stereotype on the relation +% style: style of the relation (association, aggregation, composition, inherit, ...) +% name: rootname used for naming nodes of the relation +\newcommand{\umlCNrelation}[4][]{% + \pgfkeys{/tikzuml/relation/.cd, arg1/.initial={}, arg2/.initial={}, arg/.initial={},% + mult1/.initial={}, mult2/.initial={}, mult/.initial={},% + pos1/.initial=0.2, pos2/.initial=0.8, pos/.initial=tikzumlEmpty,% + align1/.initial={}, align2/.initial={}, align/.initial={},% + anchor1/.initial=tikzumlEmpty, anchor2/.initial=tikzumlEmpty,% + stereo/.initial={}, pos stereo/.initial=1,% + style/.initial=->, name/.initial=relation-\thetikzumlRelationNum,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \errmessage{TIKZUML ERROR : in umlCNrelation, invalid option \keyname}% + }% + }% + \pgfkeys{/tikzuml/relation/.cd,#1}% + \pgfkeys{/tikzuml/relation/.cd, arg1/.get=\tikzumlCNRelationAttrName, arg2/.get=\tikzumlCNRelationAttrNameTO, arg/.get=\tikzumlCNRelationAttrNameTT,% + mult1/.get=\tikzumlCNRelationMultiplicity, mult2/.get=\tikzumlCNRelationMultiplicityTO, mult/.get=\tikzumlCNRelationMultiplicityTT,% + pos1/.get=\tikzumlCNRelationPosition, pos2/.get=\tikzumlCNRelationPositionTO, pos/.get=\tikzumlCNRelationPositionTT,% + align1/.get=\tikzumlCNRelationAlign, align2/.get=\tikzumlCNRelationAlignTO, align/.get=\tikzumlCNRelationAlignTT,% + anchor1/.get=\tikzumlCNRelationSrcAnchor, anchor2/.get=\tikzumlCNRelationDestAnchor,% + stereo/.get=\tikzumlCNRelationStereoType, pos stereo/.get=\tikzumlCNRelationPositionStereotype,% + style/.get=\tikzumlCNRelationStyle, name/.get=\tikzumlCNRelationName% + }% + % + % managing \_ in class names for node names + \def\tikzumlSrcClassName{#2}% + \begingroup% + \def\_{_}\edef\x{\endgroup% + \def\noexpand\tikzumlSrcClassNodeName{\tikzumlSrcClassName}}\x% + % + \StrSubstitute{\tikzumlSrcClassNodeName}{:}{@COLON@}[\tikzumlSrcClassNodeName]% + \StrSubstitute{\tikzumlSrcClassNodeName}{\_}{@UNDERSCORE@}[\tikzumlSrcClassNodeName]% + % + \def\tikzumlDestClassName{#4}% + \begingroup% + \def\_{_}\edef\x{\endgroup% + \def\noexpand\tikzumlDestClassNodeName{\tikzumlDestClassName}}\x% + % + \StrSubstitute{\tikzumlDestClassNodeName}{:}{@COLON@}[\tikzumlDestClassNodeName]% + \StrSubstitute{\tikzumlDestClassNodeName}{\_}{@UNDERSCORE@}[\tikzumlDestClassNodeName]% + % + % managing alias keys + \def\tikzumlCNRelationAttrNameT{\tikzumlCNRelationAttrNameTO\tikzumlCNRelationAttrNameTT}% + \def\tikzumlCNRelationMultiplicityT{\tikzumlCNRelationMultiplicityTO\tikzumlCNRelationMultiplicityTT}% + \def\tikzumlCNRelationAlignT{\tikzumlCNRelationAlignTO\tikzumlCNRelationAlignTT}% + \def\orientationT{\orientationTO\orientationTT}% + % + \ifthenelse{\equal{\tikzumlCNRelationPositionTT}{tikzumlEmpty}}{% + \def\tikzumlCNRelationPositionT{\tikzumlCNRelationPositionTO}% + }{% + \def\tikzumlCNRelationPositionT{\tikzumlCNRelationPositionTT}% + }% + % + \def\attrAlign{}% + \def\multAlign{}% + \def\attrAlignT{}% + \def\multAlignT{}% + % + \ifthenelse{\equal{\tikzumlCNRelationAlign}{left}}{% + \def\attrAlign{above right}% + \def\multAlign{below right}% + }{% + \ifthenelse{\equal{\tikzumlCNRelationAlign}{right}}{% + \def\attrAlign{above left}% + \def\multAlign{below left}% + }{}% + }% + % + \ifthenelse{\equal{\tikzumlCNRelationAlignT}{left}}{% + \def\attrAlignT{above right}% + \def\multAlignT{below right}% + }{% + \ifthenelse{\equal{\tikzumlCNRelationAlignT}{right}}{% + \def\attrAlignT{above left}% + \def\multAlignT{below left}% + }{}% + }% + % + % def stereotype + \ifthenelse{\equal{\tikzumlCNRelationStereoType}{}}{% + \def\stereotype{}% + }{% + \def\stereotype{$\ll$\tikzumlCNRelationStereoType$\gg$}% + }% + % + % def anchors macros + \ifthenelse{\equal{\tikzumlCNRelationSrcAnchor}{tikzumlEmpty}}{% + \def\tikzumlCNRelationSrcAnchor{}% + }{% + \let\tikzumlCNRelationSrcAnchorold\tikzumlCNRelationSrcAnchor% + \def\tikzumlCNRelationSrcAnchor{.\tikzumlCNRelationSrcAnchorold}% + }% + % + \ifthenelse{\equal{\tikzumlCNRelationDestAnchor}{tikzumlEmpty}}{% + \def\tikzumlCNRelationDestAnchor{}% + }{% + \let\tikzumlCNRelationDestAnchorold\tikzumlCNRelationDestAnchor% + \def\tikzumlCNRelationDestAnchor{.\tikzumlCNRelationDestAnchorold}% + }% + % + \setcounter{pos}{100*\real{\tikzumlCNRelationPosition}}% + \setcounter{posT}{100*\real{\tikzumlCNRelationPositionT}}% + \setcounter{posStereo}{100*\real{\tikzumlCNRelationPositionStereotype}}% + % + % straight line + %\setcounter{tikzumlControlNodesNum}{1}% + % + \def\tikzumlFirstArc{node[midway, name=\tikzumlCNRelationName-1, anchor=center] {} }% + \def\tikzumlLastArc{node[midway, name=\tikzumlCNRelationName-3, anchor=center]{} }% + \def\posAttrName{}% + \def\posMultiplicity{}% + \def\posAttrNameT{}% + \def\posMultiplicityT{}% + % + \begin{pgfonlayer}{connections}% + \node (\tikzumlCNRelationName-2) at (#3) {};% + \end{pgfonlayer}% + % + \ifnum\theposStereo>100% + \pgfmathsetmacro{\tikzumlCNRelationPositionStereotype}{(\theposStereo-100)/100}% + \xdef\tikzumlLastArc{\tikzumlLastArc node[pos=\tikzumlCNRelationPositionStereotype, anchor=center] {\stereotype} }% + \else% + \xdef\tikzumlFirstArc{\tikzumlFirstArc node[pos=\tikzumlCNRelationPositionStereotype, anchor=center] {\stereotype} }% + \fi% + % + \ifnum\thepos>100% + \pgfmathsetmacro{\tikzumlCNRelationPosition}{(\thepos-100)/100}% + \def\arcNum{2}% + \else% + \def\arcNum{1}% + \ifnum\thepos=100% + \def\posAttrName{above left}% + \def\posMultiplicity{below right}% + \fi% + \fi% + % + \ifnum\arcNum=1% + \xdef\tikzumlFirstArc{\tikzumlFirstArc node[pos=\tikzumlCNRelationPosition, \posAttrName, \attrAlign] {\tikzumlCNRelationAttrName}% + node[pos=\tikzumlCNRelationPosition, swap, \posMultiplicity, \multAlign] {\tikzumlCNRelationMultiplicity} }% + \fi% + \ifnum\arcNum=2% + \xdef\tikzumlLastArc{\tikzumlLastArc node[pos=\tikzumlCNRelationPosition, \posAttrName, \attrAlign] {\tikzumlCNRelationAttrName}% + node[pos=\tikzumlCNRelationPosition, swap, \posMultiplicity, \multAlign] {\tikzumlCNRelationMultiplicity} }% + \fi% + % + \ifnum\theposT>100% + \pgfmathsetmacro{\tikzumlCNRelationPositionT}{(\theposT-100)/100}% + \def\arcNumT{2}% + \else% + \def\arcNumT{1}% + \ifnum\theposT=100% + \def\posAttrNameT{above left}% + \def\posMultiplicityT{below right}% + \fi% + \fi% + % + \ifnum\arcNumT=1% + \xdef\tikzumlFirstArc{\tikzumlFirstArc node[pos=\tikzumlCNRelationPositionT, \posAttrNameT, \attrAlignT] {\tikzumlCNRelationAttrNameT}% + node[pos=\tikzumlCNRelationPositionT, swap, \posMultiplicityT, \multAlignT] {\tikzumlCNRelationMultiplicityT} }% + \fi% + \ifnum\arcNumT=2% + \xdef\tikzumlLastArc{\tikzumlLastArc node[pos=\tikzumlCNRelationPositionT, \posAttrNameT, \attrAlignT] {\tikzumlCNRelationAttrNameT}% + node[pos=\tikzumlCNRelationPositionT, swap, \posMultiplicityT, \multAlignT] {\tikzumlCNRelationMultiplicityT} }% + \fi% + % + \xdef\tikzumlPath{(\tikzumlSrcClassNodeName\tikzumlCNRelationSrcAnchor) -- \tikzumlFirstArc (\tikzumlCNRelationName-2.base) -- \tikzumlLastArc (\tikzumlDestClassNodeName\tikzumlCNRelationDestAnchor) }% + + \begin{pgfonlayer}{connections}% + \ifthenelse{\equal{\tikzumlCNRelationStyle}{tikzuml nesting style}}{% + \pgfarrowsdeclare{nested}{nested}{...} + { + \tikzumlNestingSymbolSize=0.2pt% + \advance\tikzumlNestingSymbolSize by .5\pgflinewidth% + \pgfsetdash{}{0pt} % do not dash + \pgfsetroundjoin % fix join + \pgfsetroundcap % fix cap + \pgfpathmoveto{\pgfpoint{-16*\tikzumlNestingSymbolSize}{0pt}}% + \pgfpatharc{180}{90}{8*\tikzumlNestingSymbolSize}% + \pgfpatharc{90}{0}{8*\tikzumlNestingSymbolSize}% + \pgfpatharc{0}{-90}{8*\tikzumlNestingSymbolSize}% + \pgfpatharc{-90}{-180}{8*\tikzumlNestingSymbolSize}% + \pgfpathmoveto{\pgfpoint{-8*\tikzumlNestingSymbolSize}{8*\tikzumlNestingSymbolSize}}% + \pgfpathlineto{\pgfpoint{-8*\tikzumlNestingSymbolSize}{-8*\tikzumlNestingSymbolSize}}% + \pgfusepathqstroke% + }% + \draw[auto, \tikzumlCNRelationStyle, nested-, font=\tikzumlDefaultFont] \tikzumlPath ;% + }{ + \draw[auto, \tikzumlCNRelationStyle, font=\tikzumlDefaultFont] \tikzumlPath ;% + } + \end{pgfonlayer}% + % + \stepcounter{tikzumlRelationNum}% +}% +% +% shortcuts for cnrelations +\newcommand{\umlCNinherit}[4][]{\umlCNrelation[style={tikzuml inherit style}, #1]{#2}{#3}{#4}}% +\newcommand{\umlCNimpl}[4][]{\umlCNrelation[style={tikzuml implements style}, #1]{#2}{#3}{#4}}% +\newcommand{\umlCNreal}[4][]{\umlCNrelation[style={tikzuml implements style}, #1]{#2}{#3}{#4}}% +\newcommand{\umlCNassoc}[4][]{\umlCNrelation[style={tikzuml association style}, #1]{#2}{#3}{#4}}% +\newcommand{\umlCNuniassoc}[4][]{\umlCNrelation[style={tikzuml unidirectional association style}, #1]{#2}{#3}{#4}}% +\newcommand{\umlCNaggreg}[4][]{\umlCNrelation[style={tikzuml aggregation style}, #1]{#2}{#3}{#4}}% +\newcommand{\umlCNuniaggreg}[4][]{\umlCNrelation[style={tikzuml unidirectional aggregation style}, #1]{#2}{#3}{#4}}% +\newcommand{\umlCNcompo}[4][]{\umlCNrelation[style={tikzuml composition style}, #1]{#2}{#3}{#4}}% +\newcommand{\umlCNunicompo}[4][]{\umlCNrelation[style={tikzuml unidirectional composition style}, #1]{#2}{#3}{#4}}% +\newcommand{\umlCNimport}[4][]{\umlCNrelation[style={tikzuml import style}, #1]{#2}{#3}{#4}}% +\newcommand{\umlCNnest}[4][]{\umlCNrelation[style={tikzuml nesting style}, #1]{#2}{#3}{#4}}% +\newcommand{\umlCNdep}[4][]{\umlCNrelation[style={tikzuml dependency style}, #1]{#2}{#3}{#4}}% +\newcommand{\umlCNfriend}[4][]{% + \pgfkeys{/tikzuml/friendrelation/.cd,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{stereo}}{% + \errmessage{TIKZUML ERROR : in umlCNfriend, forbidden option stereo}% + }{}% + }}% + \pgfkeys{/tikzuml/friendrelation/.cd, #1}% + \umlCNrelation[stereo=friend, style={tikzuml dependency style}, #1]{#2}{#3}{#4}% +}% +% +% define a note +% arg : attached class +% label of the note +% optional : x,y: coordinates of the note +% width: width of the note +% geometry: geometry of the relation between the note and what it is about +% weight: barycentric weight for a 3-line relation +% arm: length of the first arm +% anchor1, anchor2: anchors of the relation +% draw, fill, text: colors +% style: to manage every default TikZ option +% no coords: to tell that the note position is defined relatively +% to another node (automatically used with TikZ options above, below, left, right, below left, ...) +\newcommand{\umlnote}[3][]{% + \pgfkeys{/tikzuml/note/.cd, x/.initial=\tikzumlDefaultX, y/.initial=\tikzumlDefaultX, width/.initial=\tikzumlNoteDefaultWidth,% + geometry/.initial=\tikzumlRelationDefaultGeometry,% + weight/.initial=\tikzumlRelationDefaultWeight, arm/.initial=auto, style/.style={},% + anchor1/.initial=tikzumlEmpty, anchor2/.initial=tikzumlEmpty,% + draw/.initial=\tikzumlDefaultDrawColor, fill/.initial=\tikzumlNoteDefaultFillColor,% + text/.initial=\tikzumlDefaultTextColor,% + no coords/.is if=tikzumlnoteWithoutCoords,% + no coords=false,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{above}\OR% + \equal{\keyname}{left}\OR% + \equal{\keyname}{below}\OR% + \equal{\keyname}{right}\OR% + \equal{\keyname}{above left}\OR% + \equal{\keyname}{above right}\OR% + \equal{\keyname}{below left}\OR% + \equal{\keyname}{below right}}{% + \IfSubStr{\keyvalue}{ of }{% + \pgfkeys{/tikzuml/note/.cd, no coords}% + }{}% + }{}% + \ifx\keyvalue\pgfkeysnovalue% + \pgfkeys{/tikzuml/note/.cd, style/.append style/.expand once={\keyname}}% + \else% + \pgfkeys{/tikzuml/note/.cd, style/.append style/.expand twice={\expandafter\keyname\expandafter=\keyvalue}}% + \fi% + %\errmessage{TIKZUML ERROR : in umlnote, invalid option \keyname}% + }% + }% + \pgfkeys{/tikzuml/note/.cd, #1}% + \pgfkeys{/tikzuml/note/.cd, x/.get=\tikzumlNoteX, y/.get=\tikzumlNoteY, width/.get=\tikzumlNoteTextWidth,% + geometry/.get=\tikzumlNoteGeometry,% + weight/.get=\tikzumlNoteWeight, arm/.get=\tikzumlNoteArm,% + anchor1/.get=\tikzumlNoteSrcAnchor, anchor2/.get=\tikzumlNoteDestAnchor,% + draw/.get=\tikzumlNoteDrawColor, fill/.get=\tikzumlNoteFillColor,% + text/.get=\tikzumlNoteTextColor% + }% + % + \def\tikzumlClassName{#2}% + \begingroup% + \def\_{_}\edef\x{\endgroup% + \def\noexpand\tikzumlClassNodeName{\tikzumlClassName}}\x% + % + % def anchors macros + \ifthenelse{\equal{\tikzumlNoteSrcAnchor}{tikzumlEmpty}}{% + \def\tikzumlNoteSrcAnchor{}% + }{% + \let\tikzumlNoteSrcAnchorold\tikzumlNoteSrcAnchor% + \def\tikzumlNoteSrcAnchor{.\tikzumlNoteSrcAnchorold}% + }% + % + \ifthenelse{\equal{\tikzumlNoteDestAnchor}{tikzumlEmpty}}{% + \def\tikzumlNoteDestAnchor{}% + }{% + \let\tikzumlNoteDestAnchorold\tikzumlNoteDestAnchor% + \def\tikzumlNoteDestAnchor{.\tikzumlNoteDestAnchorold}% + }% + % + \iftikzumlnoteWithoutCoords% + \node[text=\tikzumlNoteTextColor, text width=\tikzumlNoteTextWidth, font=\tikzumlDefaultFont, outer sep=0, inner xsep=1ex, inner ysep=3ex, /tikzuml/note/style] (note-\thetikzumlNoteNum-coord) {#3};% + \else% + \node[text=\tikzumlNoteTextColor, text width=\tikzumlNoteTextWidth, font=\tikzumlDefaultFont, outer sep=0, inner xsep=1ex, inner ysep=3ex, /tikzuml/note/style] (note-\thetikzumlNoteNum-coord) at (\tikzumlNoteX, \tikzumlNoteY) {#3};% + \fi% + \draw (note-\thetikzumlNoteNum-coord.north east) node[name=note-\thetikzumlNoteNum-right-top, below=2ex, coordinate] {};% + \draw (note-\thetikzumlNoteNum-coord.north east) node[name=note-\thetikzumlNoteNum-top-right, left=2ex, coordinate] {};% + \draw[draw=\tikzumlNoteDrawColor, fill=\tikzumlNoteFillColor] (note-\thetikzumlNoteNum-coord.south west) -- (note-\thetikzumlNoteNum-coord.south east) -- (note-\thetikzumlNoteNum-right-top.base) -- (note-\thetikzumlNoteNum-top-right.base) -- (note-\thetikzumlNoteNum-coord.north west) -- cycle;% + \node[text=\tikzumlNoteTextColor, text width=\tikzumlNoteTextWidth, outer sep=0, inner xsep=1ex, inner ysep=3ex, font=\tikzumlDefaultFont] (note-\thetikzumlNoteNum) at (note-\thetikzumlNoteNum-coord) {#3};% + \draw[draw=\tikzumlNoteDrawColor] (note-\thetikzumlNoteNum-right-top) -| (note-\thetikzumlNoteNum-top-right);% + % + \pgfmathsetmacro{\tikzumlNoteWeightT}{1.0-\tikzumlNoteWeight}% + \node (note-\thetikzumlNoteNum-middle) at (barycentric cs:note-\thetikzumlNoteNum-coord=\tikzumlNoteWeight,\tikzumlClassNodeName=\tikzumlNoteWeightT) {};% + % + \ifthenelse{\equal{\tikzumlNoteGeometry}{--}% + \OR\equal{\tikzumlNoteGeometry}{-|}% + \OR\equal{\tikzumlNoteGeometry}{|-}}{% + \edef\tikzumlnotepath{\tikzumlNoteGeometry} + }{% + \ifthenelse{\equal{\tikzumlNoteGeometry}{-|-}}{% + \ifthenelse{\equal{\tikzumlNoteArm}{auto}}{% + \edef\tikzumlnotepath{-- (note-\thetikzumlNoteNum-coord\tikzumlNoteSrcAnchor -| note-\thetikzumlNoteNum-middle.center) -- (note-\thetikzumlNoteNum-middle.center) -- (note-\thetikzumlNoteNum-middle.center |- \tikzumlClassNodeName\tikzumlNoteDestAnchor) --}% + }{% + \draw (note-\thetikzumlNoteNum-coord\tikzumlNoteSrcAnchor)+(\tikzumlNoteArm,0) node[name=note-\thetikzumlNoteNum-tmp] {}; + \edef\tikzumlnotepath{-- (note-\thetikzumlNoteNum-tmp.center) |-}% + }% + }{% + \ifthenelse{\equal{\tikzumlNoteGeometry}{|-|}}{% + \ifthenelse{\equal{\tikzumlNoteArm}{auto}}{% + \edef\tikzumlnotepath{-- (note-\thetikzumlNoteNum-coord\tikzumlNoteSrcAnchor |- note-\thetikzumlNoteNum-middle.center) -- (note-\thetikzumlNoteNum-middle.center) -- (note-\thetikzumlNoteNum-middle.center -| \tikzumlClassNodeName\tikzumlNoteDestAnchor) --}% + }{% + \draw (note-\thetikzumlNoteNum-coord\tikzumlNoteSrcAnchor)+(0,\tikzumlNoteArm) node[name=note-\thetikzumlNoteNum-tmp] {}; + \edef\tikzumlnotepath{-- (note-\thetikzumlNoteNum-tmp.center) -|}% + }% + + }{% + \errmessage{TIKZUML ERROR : Unknown geometry value !!! It should be in the following list : --, |-, -|, |-|, -|-}% + }% + }% + }% + % + \begin{pgfonlayer}{connections}% + \draw[dashed] (note-\thetikzumlNoteNum-coord\tikzumlNoteSrcAnchor) \tikzumlnotepath (\tikzumlClassNodeName\tikzumlNoteDestAnchor);% + \end{pgfonlayer}% + % + \stepcounter{tikzumlNoteNum}% +}% +% +% shortcuts for note with geometry +\newcommand{\umlHVnote}[3][]{% + \pgfkeys{/tikzuml/note/.cd,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{geometry}}{% + \errmessage{TIKZUML ERROR : in umlHVnote, forbidden option geometry}% + }{}% + }}% + \pgfkeys{/tikzuml/note/.cd, #1}% + \umlnote[geometry=-|, #1]{#2}{#3}% +}% +\newcommand{\umlVHnote}[3][]{% + \pgfkeys{/tikzuml/note/.cd,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{geometry}}{% + \errmessage{TIKZUML ERROR : in umlVHnote, forbidden option geometry}% + }{}% + }}% + \pgfkeys{/tikzuml/note/.cd, #1}% + \umlnote[geometry=|-, #1]{#2}{#3}% +}% +\newcommand{\umlVHVnote}[3][]{% + \pgfkeys{/tikzuml/note/.cd,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{geometry}}{% + \errmessage{TIKZUML ERROR : in umlVHVnote, forbidden option geometry}% + }{}% + }}% + \pgfkeys{/tikzuml/note/.cd, #1}% + \umlnote[geometry=|-|, #1]{#2}{#3}% +}% +\newcommand{\umlHVHnote}[3][]{% + \pgfkeys{/tikzuml/note/.cd,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{geometry}}{% + \errmessage{TIKZUML ERROR : in umlHVHnote, forbidden option geometry}% + }{}% + }}% + \pgfkeys{/tikzuml/note/.cd, #1}% + \umlnote[geometry=-|-, #1]{#2}{#3}% +}% +% +% define a uml association class (command) +% args : name of the class +% attributes of the class +% operations of the class +% optional : x,y: coordinates of the class +% width: width of the class node +% type: type of of class (class, interface, typedef, enum) +% template: template parameters +% name: name of the class node +% geometry: geometry of the line +% weight: barycentric weight of the middle part when geometry is a 3-line +% arm: length of first part when geometry id a 3-line +% anchor1, anchor2: src/dest anchors on linked classes +% style: style of the association class (association, aggregation, composition, inherit, ...) +% draw, fill, fill template, text: colors +% style: to manage every default TikZ option +% no coords: to tell that the class position is defined relatively +% to another node (automatically used with TikZ options above, below, left, right, below left, ...) +\newcommand{\umlassocclass}[5][]{% + \pgfkeys{/tikzuml/assocclass/.cd, x/.initial=\tikzumlDefaultX, y/.initial=\tikzumlDefaultX, + width/.initial=\tikzumlClassDefaultWidth, type/.initial=\tikzumlClassDefaultType, style/.style={},% + template/.initial={}, name/.initial=tikzumlEmpty, geometry/.initial=\tikzumlRelationDefaultGeometry,% + weight/.initial=\tikzumlRelationDefaultWeight, arm/.initial=auto,% + anchor1/.initial=tikzumlEmpty, anchor2/.initial=tikzumlEmpty,% + draw/.initial=\tikzumlDefaultDrawColor,% + fill template/.initial=\tikzumlClassTemplateFillColorDefaultFillColor,% + fill/.initial=\tikzumlClassDefaultFillColor,% + text/.initial=\tikzumlDefaultTextColor,% + no coords/.is if=tikzumlassocclassWithoutCoords,% + no coords=false,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{above}\OR% + \equal{\keyname}{left}\OR% + \equal{\keyname}{below}\OR% + \equal{\keyname}{right}\OR% + \equal{\keyname}{above left}\OR% + \equal{\keyname}{above right}\OR% + \equal{\keyname}{below left}\OR% + \equal{\keyname}{below right}}{% + \IfSubStr{\keyvalue}{ of }{% + \pgfkeys{/tikzuml/assocclass/.cd, no coords}% + }{}% + }{}% + \ifx\keyvalue\pgfkeysnovalue% + \pgfkeys{/tikzuml/assocclass/.cd, style/.append style/.expand once={\keyname}}% + \else% + \pgfkeys{/tikzuml/assocclass/.cd, style/.append style/.expand twice={\expandafter\keyname\expandafter=\keyvalue}}% + \fi% + %\errmessage{TIKZUML ERROR : in umlassocclass, invalid option \keyname}% + }% + }% + % + \pgfkeys{/tikzuml/assocclass/.cd,#1}% + \pgfkeys{/tikzuml/assocclass/.cd, x/.get=\tikzumlAssocClassX, y/.get=\tikzumlAssocClassY,% + width/.get=\tikzumlAssocClassMinimumWidth, type/.get=\tikzumlAssocClassTypeTmp,% + template/.get=\tikzumlAssocClassTemplateParam,% + name/.get=\tikzumlAssocClassName, geometry/.get=\tikzumlAssocClassGeometry,% + weight/.get=\tikzumlAssocClassWeight, arm/.get=\tikzumlAssocClassArm,% + anchor1/.get=\tikzumlAssocClassSrcAnchor,% + anchor2/.get=\tikzumlAssocClassDestAnchor,% + draw/.get=\tikzumlAssocClassDrawColor, fill/.get=\tikzumlAssocClassFillColor,% + text/.get=\tikzumlAssocClassTextColor, fill template/.get=\tikzumlAssocClassTemplateFillColor% + }% + % + \ifthenelse{\equal{\tikzumlAssocClassTypeTmp}{class}\OR\equal{\tikzumlAssocClassTypeTmp}{abstract}}{% + \def\tikzumlAssocClassType{}% + }{% + \def\tikzumlAssocClassType{$\ll$\tikzumlAssocClassTypeTmp$\gg$ \\}% + }% + % + \ifthenelse{\equal{\tikzumlAssocClassTemplateParam}{}}{% + \def\tikzumlAssocClassVPadding{}% + \def\tikzumlAssocClassHPadding{}% + }{% + \def\tikzumlAssocClassVPadding{\vspace{0.1em} \\}% + \def\tikzumlAssocClassHPadding{\hspace{0.5ex} $ $}% + }% + % + \def\tikzumlAssocClassName{#2}% + \def\tikzumlAssocClassRelationName{#3}% + % + \begingroup% + \def\_{_}\edef\x{\endgroup% + \def\noexpand\tikzumlAssocClassNodeName{\tikzumlAssocClassName}}\x% + % + \begingroup% + \def\_{_}\edef\x{\endgroup% + \def\noexpand\tikzumlAssocClassRelationNodeName{\tikzumlAssocClassRelationName}}\x% + % + \ifthenelse{\equal{\tikzumlAssocClassName}{tikzumlEmpty}}{}{% + \def\tikzumlAssocClassNodeName{\tikzumlAssocClassName}% + }% + % + \StrSubstitute{\tikzumlAssocClassNodeName}{:}{@COLON@}[\tikzumlAssocClassNodeName]% + \StrSubstitute{\tikzumlAssocClassNodeName}{\_}{@UNDERSCORE@}[\tikzumlAssocClassNodeName]% + % + \ifthenelse{\equal{\tikzumlAssocClassTypeTmp}{abstract}}{% + \let\tikzumlAssocClassNameOld\tikzumlAssocClassName% + \def\tikzumlAssocClassName{{\it \tikzumlAssocClassNameOld}}% + }{}% + % + \def\tikzumlAssocClassPos{\tikzumlAssocClassX,\tikzumlAssocClassY}% + \def\tikzumlAssocClassAttributes{#4}% + \def\tikzumlAssocClassOperations{#5}% + % + % def anchors macros + \ifthenelse{\equal{\tikzumlAssocClassSrcAnchor}{tikzumlEmpty}}{% + \def\tikzumlAssocClassSrcAnchor{}% + }{% + \let\tikzumlAssocClassSrcAnchorold\tikzumlAssocClassSrcAnchor% + \def\tikzumlAssocClassSrcAnchor{.\tikzumlAssocClassSrcAnchorold}% + }% + % + \ifthenelse{\equal{\tikzumlAssocClassDestAnchor}{tikzumlEmpty}}{% + \def\tikzumlAssocClassDestAnchor{}% + }{% + \let\tikzumlAssocClassDestAnchorold\tikzumlAssocClassDestAnchor% + \def\tikzumlAssocClassDestAnchor{.\tikzumlAssocClassDestAnchorold}% + }% + % + \iftikzumlassocclassWithoutCoords% + \node[tikzuml class style, draw=\tikzumlAssocClassDrawColor, fill=\tikzumlAssocClassFillColor, text=\tikzumlAssocClassTextColor, font=\tikzumlDefaultFont, minimum width=\tikzumlAssocClassMinimumWidth, /tikzuml/assocclass/style] (\tikzumlAssocClassNodeName) {\begin{tabular}{c}\tikzumlAssocClassVPadding \tikzumlAssocClassType \tikzumlAssocClassHPadding \textbf{\tikzumlAssocClassName} \tikzumlAssocClassHPadding \end{tabular}% + \nodepart{second}% + \begin{tabular}{l}% + \tikzumlAssocClassAttributes% + \end{tabular}% + \nodepart{third}% + \begin{tabular}{l}% + \tikzumlAssocClassOperations% + \end{tabular}% + };% + \else% + \node[tikzuml class style, draw=\tikzumlAssocClassDrawColor, fill=\tikzumlAssocClassFillColor, text=\tikzumlAssocClassTextColor, font=\tikzumlDefaultFont, minimum width=\tikzumlAssocClassMinimumWidth, /tikzuml/assocclass/style] (\tikzumlAssocClassNodeName) at (\tikzumlAssocClassPos) {\begin{tabular}{c}\tikzumlAssocClassVPadding \tikzumlAssocClassType \tikzumlAssocClassHPadding \textbf{\tikzumlAssocClassName} \tikzumlAssocClassHPadding \end{tabular}% + \nodepart{second}% + \begin{tabular}{l}% + \tikzumlAssocClassAttributes% + \end{tabular}% + \nodepart{third}% + \begin{tabular}{l}% + \tikzumlAssocClassOperations% + \end{tabular}% + };% + \fi% + % + \ifthenelse{\equal{\tikzumlAssocClassTemplateParam}{}}{}{% + \draw (\tikzumlAssocClassNodeName.north east) node[tikzuml template style, name=\tikzumlAssocClassNodeName-template, draw=\tikzumlAssocClassDrawColor, fill=\tikzumlAssocClassTemplateFillColor, text=\tikzumlAssocClassTextColor, font=\tikzumlDefaultFont] {\tikzumlAssocClassTemplateParam};% + }% + % + \pgfmathsetmacro{\tikzumlAssocClassWeightT}{1.0-\tikzumlAssocClassWeight} + \node (\tikzumlAssocClassNodeName-middle) at (barycentric cs:\tikzumlAssocClassNodeName=\tikzumlAssocClassWeight,\tikzumlAssocClassRelationNodeName=\tikzumlAssocClassWeightT) {};% + % + \ifthenelse{\equal{\tikzumlAssocClassGeometry}{--}\OR\equal{\tikzumlAssocClassGeometry}{-|}\OR\equal{\tikzumlAssocClassGeometry}{|-}}{% + \edef\tikzumlassocclasspath{\tikzumlAssocClassGeometry} + }{% + \ifthenelse{\equal{\tikzumlAssocClassGeometry}{-|-}}{% + \ifthenelse{\equal{\tikzumlAssocClassArm}{auto}}{% + \edef\tikzumlassocclasspath{-- (\tikzumlAssocClassNodeName\tikzumlAssocClassSrcAnchor -| \tikzumlAssocClassNodeName-middle.center) -- (\tikzumlAssocClassNodeName-middle.center) -- (\tikzumlAssocClassNodeName-middle.center |- \tikzumlAssocClassRelationNodeName\tikzumlAssocClassDestAnchor) --}% + }{% + \draw (\tikzumlAssocClassNodeName\tikzumlAssocClassSrcAnchor)+(\tikzumlAssocClassArm,0) node[name=\tikzumlAssocClassNodeName-tmp] {}; + \edef\tikzumlnotepath{-- (\tikzumlAssocClassNodeName-tmp.center) |-}% + }% + }{% + \ifthenelse{\equal{\tikzumlAssocClassGeometry}{|-|}}{% + \ifthenelse{\equal{\tikzumlAssocClassArm}{auto}}{% + \edef\tikzumlassocclasspath{-- (\tikzumlAssocClassNodeName\tikzumlAssocClassSrcAnchor |- \tikzumlAssocClassNodeName-middle.center) -- (\tikzumlAssocClassNodeName-middle.center) -- (\tikzumlAssocClassNodeName-middle.center -| \tikzumlAssocClassRelationNodeName\tikzumlAssocClassDestAnchor) --}% + }{% + \draw (\tikzumlAssocClassNodeName\tikzumlAssocClassSrcAnchor)+(0,\tikzumlAssocClassArm) node[name=\tikzumlAssocClassNodeName-tmp] {}; + \edef\tikzumlassocclasspath{-- (\thetikzumlAssocClassNodeName-tmp.center) -|}% + }% + + }{% + \errmessage{TIKZUML ERROR : Unknown geometry value !!! It should be in the following list : --, |-, -|, |-|, -|-}% + }% + }% + }% + % + \begin{pgfonlayer}{connections}% + \draw[dashed] (\tikzumlAssocClassNodeName\tikzumlAssocClassSrcAnchor) \tikzumlassocclasspath (\tikzumlAssocClassRelationNodeName\tikzumlAssocClassDestAnchor);% + \end{pgfonlayer}% + % + % add to fit + \ifnum\c@tikzumlPackageLevel>0% + \edef\tikzumlPackageFitOld{\csname tikzumlPackageFit\tikzumlPackage@parent @@\tikzumlPackage@fitname\endcsname}% + \ifthenelse{\equal{\tikzumlAssocClassTemplateParam}{}}{% + \expandafter\xdef\csname tikzumlPackageFit\tikzumlPackage@parent @@\tikzumlPackage@fitname\endcsname{\tikzumlPackageFitOld (\tikzumlAssocClassNodeName)(\tikzumlAssocClassNodeName-middle)}% + }{% + \expandafter\xdef\csname tikzumlPackageFit\tikzumlPackage@parent @@\tikzumlPackage@fitname\endcsname{\tikzumlPackageFitOld (\tikzumlAssocClassNodeName) (\tikzumlAssocClassNodeName-template)(\tikzumlAssocClassNodeName-middle)}% + }% + \stepcounter{tikzumlPackageClassNum}% + \fi% +}% +% +% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% use case diagrams % +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +\tikzstyle{tikzuml usecase style}=[ellipse, text centered]% +\tikzstyle{tikzuml actor style}=[ellipse, inner sep=0, outer sep=0]% +% +\newcounter{tikzumlSystemUseCaseNum}% +\newcounter{tikzumlSystemLevel}% +\newcounter{tikzumlUseCaseNum}% +\newcounter{tikzumlActorNum}% +% +\newif\iftikzumlusecaseWithoutCoords% +\newif\iftikzumlactorWithoutCoords% +% +% define a system +% arg : name +% optional : x, y: coordinates of the system +% draw, fill, text: colors +\newenvironment{umlsystem}[2][]{% + \gdef\tikzumlSystemFit{}% + \def\tikzumlSystemName{#2}% + \setcounter{tikzumlSystemUseCaseNum}{0}% + % + \pgfkeys{/tikzuml/system/.cd, x/.initial=\tikzumlDefaultX, y/.initial=\tikzumlDefaultX,% + draw/.initial=\tikzumlDefaultDrawColor, fill/.initial=\tikzumlSystemDefaultFillColor,% + text/.initial=\tikzumlDefaultTextColor,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \errmessage{TIKZUML ERROR : in umlsystem, invalid option \keyname}% + }% + }% + % + \pgfkeys{/tikzuml/system/.cd, #1}% + \pgfkeys{/tikzuml/system/.cd, x/.get=\tikzumlSystemXShift, y/.get=\tikzumlSystemYShift,% + draw/.get=\tikzumlSystemDrawColor, fill/.get=\tikzumlSystemFillColor,% + text/.get=\tikzumlSystemTextColor}% + % + \stepcounter{tikzumlSystemLevel}% + % + \begin{scope}[xshift=\tikzumlSystemXShift cm, yshift=\tikzumlSystemYShift cm]% +}{% + \addtocounter{tikzumlSystemLevel}{-1}% + % if contains no usecase, one define a fictive node to enable the fit option + \ifnum\c@tikzumlSystemUseCaseNum=0% + \node[inner xsep=10ex, inner ysep=1em] (\tikzumlSystemName-root) at (0,0) {};% + \xdef\tikzumlSystemFit{(\tikzumlSystemName-root)}% + \fi% + % + \begin{pgfonlayer}{background}% + \node[inner ysep=1em, inner xsep=2ex, fit = \tikzumlSystemFit] (\tikzumlSystemName-tmp) {};% + \node[text=\tikzumlSystemTextColor, font=\tikzumlDefaultFont] (\tikzumlSystemName-caption-tmp) at (\tikzumlSystemName-tmp.north) {\tikzumlSystemName};% + \node[draw=\tikzumlSystemDrawColor, fill=\tikzumlSystemFillColor, text=\tikzumlSystemTextColor, font=\tikzumlDefaultFont, inner ysep=1em, inner xsep=2ex, fit = (\tikzumlSystemName-tmp) (\tikzumlSystemName-caption-tmp)] (\tikzumlSystemName) {};% + \node[text=\tikzumlSystemTextColor, font=\tikzumlDefaultFont] (\tikzumlSystemName-caption) at (\tikzumlSystemName-caption-tmp.north) {\tikzumlSystemName};% + \end{pgfonlayer}% + \end{scope}% + % +}% +% +% define a use case +% arg : label of the use case +% optional : x, y: coordinates of the use case +% name: name of the node +% width: node width +% draw, fill, text: colors +% style: to manage every default TikZ option +% no coords: to tell that the use case position is defined relatively +% to another node (automatically used with TikZ options above, below, left, right, below left, ...) +\newcommand{\umlusecase}[2][]{% + \stepcounter{tikzumlUseCaseNum}% + \pgfkeys{/tikzuml/usecase/.cd, x/.initial=\tikzumlDefaultX, y/.initial=\tikzumlDefaultX, width/.initial=auto,% + name/.initial=usecase-\thetikzumlUseCaseNum,% + draw/.initial=\tikzumlDefaultDrawColor, fill/.initial=\tikzumlUseCaseDefaultFillColor,% + text/.initial=\tikzumlDefaultTextColor, style/.style={},% + no coords/.is if=tikzumlusecaseWithoutCoords,% + no coords=false,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{above}\OR% + \equal{\keyname}{left}\OR% + \equal{\keyname}{below}\OR% + \equal{\keyname}{right}\OR% + \equal{\keyname}{above left}\OR% + \equal{\keyname}{above right}\OR% + \equal{\keyname}{below left}\OR% + \equal{\keyname}{below right}}{% + \IfSubStr{\keyvalue}{ of }{% + \pgfkeys{/tikzuml/usecase/.cd, no coords}% + }{}% + }{}% + \ifx\keyvalue\pgfkeysnovalue% + \pgfkeys{/tikzuml/usecase/.cd, style/.append style/.expand once={\keyname}}% + \else% + \pgfkeys{/tikzuml/usecase/.cd, style/.append style/.expand twice={\expandafter\keyname\expandafter=\keyvalue}}% + \fi% + %\errmessage{TIKZUML ERROR : in umlusecase, invalid option \keyname}% + }% + }% + \pgfkeys{/tikzuml/usecase/.cd, #1}% + \pgfkeys{/tikzuml/usecase/.cd, x/.get=\tikzumlUseCaseX, y/.get=\tikzumlUseCaseY, width/.get=\tikzumlUseCaseTextWidth,% + name/.get=\tikzumlUseCaseName,% + draw/.get=\tikzumlUseCaseDrawColor, fill/.get=\tikzumlUseCaseFillColor,% + text/.get=\tikzumlUseCaseTextColor% + }% + % + \def\tikzumlUseCaseText{#2}% + % + \def\tikzumlUseCasePos{\tikzumlUseCaseX,\tikzumlUseCaseY}% + % + \ifthenelse{\equal{\tikzumlUseCaseTextWidth}{auto}}{% + \iftikzumlusecaseWithoutCoords% + \node[tikzuml usecase style, draw=\tikzumlUseCaseDrawColor, fill=\tikzumlUseCaseFillColor, text=\tikzumlUseCaseTextColor, font=\tikzumlDefaultFont, /tikzuml/usecase/style] (\tikzumlUseCaseName) {\tikzumlUseCaseText};% + \else% + \node[tikzuml usecase style, draw=\tikzumlUseCaseDrawColor, fill=\tikzumlUseCaseFillColor, text=\tikzumlUseCaseTextColor, font=\tikzumlDefaultFont, /tikzuml/usecase/style] (\tikzumlUseCaseName) at (\tikzumlUseCasePos) {\tikzumlUseCaseText};% + \fi% + }{% + \iftikzumlusecaseWithoutCoords% + \node[tikzuml usecase style, draw=\tikzumlUseCaseDrawColor, fill=\tikzumlUseCaseFillColor, text=\tikzumlUseCaseTextColor, font=\tikzumlDefaultFont, text width=\tikzumlUseCaseTextWidth, /tikzuml/usecase/style] (\tikzumlUseCaseName) {\tikzumlUseCaseText};% + \else% + \node[tikzuml usecase style, draw=\tikzumlUseCaseDrawColor, fill=\tikzumlUseCaseFillColor, text=\tikzumlUseCaseTextColor, font=\tikzumlDefaultFont, text width=\tikzumlUseCaseTextWidth, /tikzuml/usecase/style] (\tikzumlUseCaseName) at (\tikzumlUseCasePos) {\tikzumlUseCaseText};% + \fi% + }% + % + % add to fit + \ifnum\c@tikzumlSystemLevel>0% + \let\tikzumlSystemFitOld\tikzumlSystemFit% + \xdef\tikzumlSystemFit{\tikzumlSystemFitOld (\tikzumlUseCaseName)}% + \stepcounter{tikzumlSystemUseCaseNum}% + \fi% +}% +% +% define the actor symbol +% optional : global tikzpicture styles +\newcommand{\picturedactor}[1]{% + \pgfkeys{/tikzuml/picactor/.cd, scale/.initial=1, .unknown/.code={}}% + \pgfkeys{/tikzuml/picactor/.cd,#1}% + \pgfkeys{/tikzuml/picactor/.cd, scale/.get=\tikzumlPicturedActorScale}% + % + \begin{tikzpicture}[#1]% + \coordinate (head) at (0,4ex);% + \coordinate (left-hand) at (-2ex,2ex);% + \coordinate (right-hand) at (2ex,2ex);% + \coordinate (left-foot) at (-2ex,-2ex);% + \coordinate (right-foot) at (2ex,-2ex);% + \coordinate (empty) at (0,-3ex);% + \draw (empty) (0,0) -- (head);% + \draw (left-hand) -- (right-hand);% + \draw (0,0) -- (left-foot) (0,0) -- (right-foot);% + \node[fill, draw, circle, inner sep=\tikzumlPicturedActorScale*0.3333ex, minimum size=\tikzumlPicturedActorScale*2ex, anchor=base] at (head) {};% + \end{tikzpicture}% +}% +% +% define an actor +% arg : var name +% optional : x, y: coordinates of the actor +% scale: scale factor of the actor symbol +% below: distance between the actor symbol and its name below +% draw, text: colors +% style: to manage every default TikZ option +% no coords: to tell that the actor position is defined relatively +% to another node (automatically used with TikZ options above, below, left, right, below left, ...) +\newcommand{\umlactor}[2][]{% + \stepcounter{tikzumlActorNum}% + \pgfkeys{/tikzuml/actor/.cd, x/.initial=\tikzumlDefaultX, y/.initial=\tikzumlDefaultX, scale/.initial=1, below/.initial=\tikzumlActorDefaultBelow,% + draw/.initial=\tikzumlDefaultDrawColor, text/.initial=\tikzumlDefaultTextColor,% + style/.style={},% + no coords/.is if=tikzumlactorWithoutCoords,% + no coords=false,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{above}\OR% + \equal{\keyname}{left}\OR% + \equal{\keyname}{below}\OR% + \equal{\keyname}{right}\OR% + \equal{\keyname}{above left}\OR% + \equal{\keyname}{above right}\OR% + \equal{\keyname}{below left}\OR% + \equal{\keyname}{below right}}{% + \IfSubStr{\keyvalue}{ of }{% + \pgfkeys{/tikzuml/actor/.cd, no coords}% + }{}% + }{}% + \ifx\keyvalue\pgfkeysnovalue% + \pgfkeys{/tikzuml/actor/.cd, style/.append style/.expand once={\keyname}}% + \else% + \pgfkeys{/tikzuml/actor/.cd, style/.append style/.expand twice={\expandafter\keyname\expandafter=\keyvalue}}% + \fi% + %\errmessage{TIKZUML ERROR : in umlactor, invalid option \keyname}% + }% + }% + \pgfkeys{/tikzuml/actor/.cd, #1}% + \pgfkeys{/tikzuml/actor/.cd,% + x/.get=\tikzumlActorX, y/.get=\tikzumlActorY, scale/.get=\tikzumlActorScale,% + below/.get=\tikzumlActorBelow,% + draw/.get=\tikzumlActorDrawColor, text/.get=\tikzumlActorTextColor}% + % + \def\tikzumlActorName{#2}% + \begingroup% + \def\_{_}\edef\x{\endgroup% + \def\noexpand\tikzumlActorNodeName{\tikzumlActorName}}\x% + % + \def\tikzumlActorPos{\tikzumlActorX,\tikzumlActorY}% + % + \iftikzumlactorWithoutCoords% + \node[tikzuml actor style, text=\tikzumlActorTextColor, font=\tikzumlDefaultFont, /tikzuml/actor/style] (\tikzumlActorNodeName) {\picturedactor{scale=\tikzumlActorScale, fill=white, draw=\tikzumlActorDrawColor, thick}};% + \else% + \node[tikzuml actor style, text=\tikzumlActorTextColor, font=\tikzumlDefaultFont, /tikzuml/actor/style] (\tikzumlActorNodeName) at (\tikzumlActorPos) {\picturedactor{scale=\tikzumlActorScale, fill=white, draw=\tikzumlActorDrawColor, thick}};% + \fi% + \node[text=\tikzumlActorTextColor, font=\tikzumlDefaultFont, below=\tikzumlActorScale*\tikzumlActorBelow] at (\tikzumlActorNodeName) {\tikzumlActorName};% + % +}% + +% shortcuts for include and extend relation +\newcommand{\umlinclude}[3][]{% + \pgfkeys{/tikzuml/includerelation/.cd,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{stereo}}{% + \errmessage{TIKZUML ERROR : in umlinclude, forbidden option stereo}% + }{% + \ifthenelse{\equal{\keyname}{geometry}}{% + \errmessage{TIKZUML ERROR : in umlinclude, forbidden option geometry}% + }{}% + }% + }}% + \pgfkeys{/tikzuml/includerelation/.cd, #1}% + \umlrelation[stereo=include, style={tikzuml dependency style}, #1]{#2}{#3}% +}% +\newcommand{\umlextend}[3][]{% + \pgfkeys{/tikzuml/extendrelation/.cd,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{stereo}}{% + \errmessage{TIKZUML ERROR : in umlextend, forbidden option stereo}% + }{% + \ifthenelse{\equal{\keyname}{geometry}}{% + \errmessage{TIKZUML ERROR : in umlextend, forbidden option geometry}% + }{}% + }% + }}% + \pgfkeys{/tikzuml/extendrelation/.cd, #1}% + \umlrelation[stereo=extend, style={tikzuml dependency style}, #1]{#2}{#3}% +}% +% +\newcommand{\umlHVinclude}[3][]{% + \pgfkeys{/tikzuml/includerelation/.cd,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{stereo}}{% + \errmessage{TIKZUML ERROR : in umlHVinclude, forbidden option stereo}% + }{% + \ifthenelse{\equal{\keyname}{geometry}}{% + \errmessage{TIKZUML ERROR : in umlHVinclude, forbidden option geometry}% + }{}% + }% + }}% + \pgfkeys{/tikzuml/includerelation/.cd, #1}% + \umlrelation[geometry=-|, stereo=include, style={tikzuml dependency style}, #1]{#2}{#3}% +}% +\newcommand{\umlHVextend}[3][]{% + \pgfkeys{/tikzuml/extendrelation/.cd,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{stereo}}{% + \errmessage{TIKZUML ERROR : in umlHVextend, forbidden option stereo}% + }{% + \ifthenelse{\equal{\keyname}{geometry}}{% + \errmessage{TIKZUML ERROR : in umlHVextend, forbidden option geometry}% + }{}% + }% + }}% + \pgfkeys{/tikzuml/extendrelation/.cd, #1}% + \umlrelation[geometry=-|, stereo=extend, style={tikzuml dependency style}, #1]{#2}{#3}% +}% +% +\newcommand{\umlVHinclude}[3][]{% + \pgfkeys{/tikzuml/includerelation/.cd,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{stereo}}{% + \errmessage{TIKZUML ERROR : in umlVHinclude, forbidden option stereo}% + }{% + \ifthenelse{\equal{\keyname}{geometry}}{% + \errmessage{TIKZUML ERROR : in umlVHinclude, forbidden option geometry}% + }{}% + }% + }}% + \pgfkeys{/tikzuml/includerelation/.cd, #1}% + \umlrelation[geometry=|-, stereo=include, style={tikzuml dependency style}, #1]{#2}{#3}% +}% +\newcommand{\umlVHextend}[3][]{% + \pgfkeys{/tikzuml/extendrelation/.cd,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{stereo}}{% + \errmessage{TIKZUML ERROR :in umlVHextend, forbidden option stereo}% + }{% + \ifthenelse{\equal{\keyname}{geometry}}{% + \errmessage{TIKZUML ERROR : in umlVHextend, forbidden option geometry}% + }{}% + }% + }}% + \pgfkeys{/tikzuml/extendrelation/.cd, #1}% + \umlrelation[geometry=|-, stereo=extend, style={tikzuml dependency style}, #1]{#2}{#3}% +}% +% +\newcommand{\umlHVHinclude}[3][]{% + \pgfkeys{/tikzuml/includerelation/.cd,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{stereo}}{% + \errmessage{TIKZUML ERROR : in umlHVHinclude, forbidden option stereo}% + }{% + \ifthenelse{\equal{\keyname}{geometry}}{% + \errmessage{TIKZUML ERROR : in umlHVHinclude, forbidden option geometry}% + }{}% + }% + }}% + \pgfkeys{/tikzuml/includerelation/.cd, #1}% + \umlrelation[geometry=-|-, stereo=include, style={tikzuml dependency style}, #1]{#2}{#3}% +}% +\newcommand{\umlHVHextend}[3][]{% + \pgfkeys{/tikzuml/extendrelation/.cd,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{stereo}}{% + \errmessage{TIKZUML ERROR : in umlHVHextend, forbidden option stereo}% + }{% + \ifthenelse{\equal{\keyname}{geometry}}{% + \errmessage{TIKZUML ERROR : in umlHVHextend, forbidden option geometry}% + }{}% + }% + }}% + \pgfkeys{/tikzuml/extendrelation/.cd, #1}% + \umlrelation[geometry=-|-, stereo=extend, style={tikzuml dependency style}, #1]{#2}{#3}% +}% +% +\newcommand{\umlVHVinclude}[3][]{% + \pgfkeys{/tikzuml/includerelation/.cd,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{stereo}}{% + \errmessage{TIKZUML ERROR : in umlVHVinclude, forbidden option stereo}% + }{% + \ifthenelse{\equal{\keyname}{geometry}}{% + \errmessage{TIKZUML ERROR : in umlVHVinclude, forbidden option geometry}% + }{}% + }% + }}% + \pgfkeys{/tikzuml/includerelation/.cd, #1}% + \umlrelation[geometry=|-|, stereo=include, style={tikzuml dependency style}, #1]{#2}{#3}% +}% +\newcommand{\umlVHVextend}[3][]{% + \pgfkeys{/tikzuml/extendrelation/.cd,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{stereo}}{% + \errmessage{TIKZUML ERROR : in umlVHVextend, forbidden option stereo}% + }{% + \ifthenelse{\equal{\keyname}{geometry}}{% + \errmessage{TIKZUML ERROR : in umlVHVextend, forbidden option geometry}% + }{}% + }% + }}% + \pgfkeys{/tikzuml/extendrelation/.cd, #1}% + \umlrelation[geometry=|-|, stereo=extend, style={tikzuml dependency style}, #1]{#2}{#3}% +}% +% +\newcommand{\umlCNinclude}[4][]{% + \pgfkeys{/tikzuml/includerelation/.cd,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{stereo}}{% + \errmessage{TIKZUML ERROR : in umlCNinclude, forbidden option stereo}% + }{}% + }}% + \pgfkeys{/tikzuml/includerelation/.cd, #1}% + \umlCNrelation[stereo=include, style={tikzuml dependency style}, #1]{#2}{#3}{#4}% +}% +\newcommand{\umlCNextend}[4][]{% + \pgfkeys{/tikzuml/extendrelation/.cd,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{stereo}}{% + \errmessage{TIKZUML ERROR : in umlCNextend, forbidden option stereo}% + }{}% + }}% + \pgfkeys{/tikzuml/extendrelation/.cd, #1}% + \umlCNrelation[stereo=extend, style={tikzuml dependency style}, #1]{#2}{#3}{#4}% +}% +% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% state diagrams % +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +\tikzstyle{tikzuml state style}=[rectangle split, rectangle split parts=2, rounded corners, inner xsep=1.5ex]% +\tikzstyle{tikzuml transition style}=[color=\tikzumlDefaultDrawColor, rounded corners, -angle 45]% +% +\newcounter{tikzumlStateJoinNum}% +\newcounter{tikzumlStateDecisionNum}% +\newcounter{tikzumlStateInitialNum}% +\newcounter{tikzumlStateFinalNum}% +\newcounter{tikzumlStateEnterNum}% +\newcounter{tikzumlStateExitNum}% +\newcounter{tikzumlStateEndNum}% +\newcounter{tikzumlStateHistoryNum}% +\newcounter{tikzumlStateDeepHistoryNum}% +\newcounter{tikzumlStateLevel}% +\newcounter{tikzumlStateSubStateNum}% +\newcounter{tikzumlStateText}% +% +\newif\iftikzumlstatejoinWithoutCoords% +\newif\iftikzumlstatedecisionWithoutCoords% +\newif\iftikzumlstateinitialWithoutCoords% +\newif\iftikzumlstatefinalWithoutCoords% +\newif\iftikzumlstateenterWithoutCoords% +\newif\iftikzumlstateexitWithoutCoords% +\newif\iftikzumlstateendWithoutCoords% +\newif\iftikzumlstatehistoryWithoutCoords% +\newif\iftikzumlstatedeephistoryWithoutCoords% +\newif\iftikzumlstateWithoutCoords% +% +% define a uml join state +% args : name of the state +% optional : x,y: coordinates of the state +% width: width of the state node +% name: name of the state node +% color: color of the join symbol +% style: to manage every default TikZ option +% no coords: to tell that the state position is defined relatively +% to another node (automatically used with TikZ options above, below, left, right, below left, ...) +\newcommand{\umlstatejoin}[1][]{% + \pgfkeys{/tikzuml/statejoin/.cd, x/.initial=\tikzumlDefaultX, y/.initial=\tikzumlDefaultX, width/.initial=\tikzumlStateJoinDefaultWidth,% + name/.initial=statejoin-\thetikzumlStateJoinNum,% + color/.initial=\tikzumlDefaultDrawColor, style/.style={},% + no coords/.is if=tikzumlstatejoinWithoutCoords,% + no coords=false,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{above}\OR% + \equal{\keyname}{left}\OR% + \equal{\keyname}{below}\OR% + \equal{\keyname}{right}\OR% + \equal{\keyname}{above left}\OR% + \equal{\keyname}{above right}\OR% + \equal{\keyname}{below left}\OR% + \equal{\keyname}{below right}}{% + \IfSubStr{\keyvalue}{ of }{% + \pgfkeys{/tikzuml/statejoin/.cd, no coords}% + }{}% + }{}% + \ifx\keyvalue\pgfkeysnovalue% + \pgfkeys{/tikzuml/statejoin/.cd, style/.append style/.expand once={\keyname}}% + \else% + \pgfkeys{/tikzuml/statejoin/.cd, style/.append style/.expand twice={\expandafter\keyname\expandafter=\keyvalue}}% + \fi% + %\errmessage{TIKZUML ERROR : in umlstatejoin, invalid option \keyname}% + }% + }% + \pgfkeys{/tikzuml/statejoin/.cd, #1}% + \pgfkeys{/tikzuml/statejoin/.cd, x/.get=\tikzumlStateJoinX, y/.get=\tikzumlStateJoinY, width/.get=\tikzumlStateJoinMinimumWidth,% + name/.get=\tikzumlStateJoinName, color/.get=\tikzumlStateJoinColor% + }% + % + \def\tikzumlStateJoinPos{\tikzumlStateJoinX,\tikzumlStateJoinY}% + % + \iftikzumlstatejoinWithoutCoords% + \node[circle, minimum size=\tikzumlStateJoinMinimumWidth, draw=\tikzumlStateJoinColor, fill=\tikzumlStateJoinColor, /tikzuml/statejoin/style] (\tikzumlStateJoinName) {};% + \else% + \node[circle, minimum size=\tikzumlStateJoinMinimumWidth, draw=\tikzumlStateJoinColor, fill=\tikzumlStateJoinColor, /tikzuml/statejoin/style] (\tikzumlStateJoinName) at (\tikzumlStateJoinPos) {};% + \fi% + % + % add to fit + \ifnum\c@tikzumlStateLevel>0% + \edef\tikzumlStateFitOld{\csname tikzumlStateFit\tikzumlState@parent @@\tikzumlState@fitname\endcsname}% + \expandafter\xdef\csname tikzumlStateFit\tikzumlState@parent @@\tikzumlState@fitname\endcsname{\tikzumlStateFitOld (\tikzumlStateJoinName)}% + \stepcounter{tikzumlStateSubStateNum}% + \fi% + \stepcounter{tikzumlStateJoinNum}% +}% +% +% define a uml decision state +% args : name of the state +% optional : x,y: coordinates of the state +% width: width of the state node +% name: name of the state node +% color: color of the join symbol +% style: to manage every default TikZ option +% no coords: to tell that the state position is defined relatively +% to another node (automatically used with TikZ options above, below, left, right, below left, ...) +\newcommand{\umlstatedecision}[1][]{% + \pgfkeys{/tikzuml/statedecision/.cd, x/.initial=\tikzumlDefaultX, y/.initial=\tikzumlDefaultX, width/.initial=\tikzumlStateDecisionDefaultWidth,% + name/.initial=statedecision-\thetikzumlStateDecisionNum,% + color/.initial=\tikzumlDefaultDrawColor, style/.style={},% + no coords/.is if=tikzumlstatedecisionWithoutCoords,% + no coords=false,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{above}\OR% + \equal{\keyname}{left}\OR% + \equal{\keyname}{below}\OR% + \equal{\keyname}{right}\OR% + \equal{\keyname}{above left}\OR% + \equal{\keyname}{above right}\OR% + \equal{\keyname}{below left}\OR% + \equal{\keyname}{below right}}{% + \IfSubStr{\keyvalue}{ of }{% + \pgfkeys{/tikzuml/statedecision/.cd, no coords}% + }{}% + }{}% + \ifx\keyvalue\pgfkeysnovalue% + \pgfkeys{/tikzuml/statedecision/.cd, style/.append style/.expand once={\keyname}}% + \else% + \pgfkeys{/tikzuml/statedecision/.cd, style/.append style/.expand twice={\expandafter\keyname\expandafter=\keyvalue}}% + \fi% + %\errmessage{TIKZUML ERROR : in umlstatedecision, invalid option \keyname}% + }% + }% + \pgfkeys{/tikzuml/statedecision/.cd, #1}% + \pgfkeys{/tikzuml/statedecision/.cd, x/.get=\tikzumlStateDecisionX, y/.get=\tikzumlStateDecisionY, width/.get=\tikzumlStateDecisionMinimumWidth,% + name/.get=\tikzumlStateDecisionName, color/.get=\tikzumlStateDecisionColor% + }% + % + \def\tikzumlStateDecisionPos{\tikzumlStateDecisionX,\tikzumlStateDecisionY}% + % + \iftikzumlstatedecisionWithoutCoords% + \node[rectangle, rotate=45, minimum size=\tikzumlStateDecisionMinimumWidth, draw=\tikzumlStateDecisionColor, /tikzuml/statedecision/style] (\tikzumlStateDecisionName) {};% + \else% + \node[rectangle, rotate=45, minimum size=\tikzumlStateDecisionMinimumWidth, draw=\tikzumlStateDecisionColor, /tikzuml/statedecision/style] (\tikzumlStateDecisionName) at (\tikzumlStateDecisionPos) {};% + \fi% + % + % add to fit + \ifnum\c@tikzumlStateLevel>0% + \edef\tikzumlStateFitOld{\csname tikzumlStateFit\tikzumlState@parent @@\tikzumlState@fitname\endcsname}% + \expandafter\xdef\csname tikzumlStateFit\tikzumlState@parent @@\tikzumlState@fitname\endcsname{\tikzumlStateFitOld (\tikzumlStateDecisionName)}% + \stepcounter{tikzumlStateSubStateNum}% + \fi% + \stepcounter{tikzumlStateDecisionNum}% +}% +% +% define a uml initial state +% args : name of the state +% optional : x,y: coordinates of the state +% width: width of the state node +% name: name of the state node +% entry, do, exit: entry/do/exit action of the state +% color: color of the join symbol +% style: to manage every default TikZ option +% no coords: to tell that the state position is defined relatively +% to another node (automatically used with TikZ options above, below, left, right, below left, ...) +\newcommand{\umlstateinitial}[1][]{% + \pgfkeys{/tikzuml/stateinitial/.cd, x/.initial=\tikzumlDefaultX, y/.initial=\tikzumlDefaultX, width/.initial=\tikzumlStateInitialDefaultWidth,% + name/.initial=stateinitial-\thetikzumlStateInitialNum,% + color/.initial=\tikzumlDefaultDrawColor, style/.style={},% + no coords/.is if=tikzumlstateinitialWithoutCoords,% + no coords=false,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{above}\OR% + \equal{\keyname}{left}\OR% + \equal{\keyname}{below}\OR% + \equal{\keyname}{right}\OR% + \equal{\keyname}{above left}\OR% + \equal{\keyname}{above right}\OR% + \equal{\keyname}{below left}\OR% + \equal{\keyname}{below right}}{% + \IfSubStr{\keyvalue}{ of }{% + \pgfkeys{/tikzuml/stateinitial/.cd, no coords}% + }{}% + }{}% + \ifx\keyvalue\pgfkeysnovalue% + \pgfkeys{/tikzuml/stateinitial/.cd, style/.append style/.expand once={\keyname}}% + \else% + \pgfkeys{/tikzuml/stateinitial/.cd, style/.append style/.expand twice={\expandafter\keyname\expandafter=\keyvalue}}% + \fi% + %\errmessage{TIKZUML ERROR : in umlstateinitial, invalid option \keyname}% + }% + }% + \pgfkeys{/tikzuml/stateinitial/.cd, #1}% + \pgfkeys{/tikzuml/stateinitial/.cd, x/.get=\tikzumlStateInitialX, y/.get=\tikzumlStateInitialY, width/.get=\tikzumlStateInitialMinimumWidth,% + name/.get=\tikzumlStateInitialName, color/.get=\tikzumlStateInitialColor% + }% + % + \def\tikzumlStateInitialPos{\tikzumlStateInitialX,\tikzumlStateInitialY}% + % + \iftikzumlstateinitialWithoutCoords% + \node[circle, minimum size=\tikzumlStateInitialMinimumWidth, fill=\tikzumlStateInitialColor, /tikzuml/stateinitial/style] (\tikzumlStateInitialName) {};% + \else% + \node[circle, minimum size=\tikzumlStateInitialMinimumWidth, fill=\tikzumlStateInitialColor, /tikzuml/stateinitial/style] (\tikzumlStateInitialName) at (\tikzumlStateInitialPos) {};% + \fi% + % + % add to fit + \ifnum\c@tikzumlStateLevel>0% + \edef\tikzumlStateFitOld{\csname tikzumlStateFit\tikzumlState@parent @@\tikzumlState@fitname\endcsname}% + \expandafter\xdef\csname tikzumlStateFit\tikzumlState@parent @@\tikzumlState@fitname\endcsname{\tikzumlStateFitOld (\tikzumlStateInitialName)}% + \stepcounter{tikzumlStateSubStateNum}% + \fi% + \stepcounter{tikzumlStateInitialNum}% +}% +% +% define a uml final state +% args : name of the state +% optional : x,y: coordinates of the state +% width: width of the state node +% name: name of the state node +% color: color of the join symbol +% style: to manage every default TikZ option +% no coords: to tell that the state position is defined relatively +% to another node (automatically used with TikZ options above, below, left, right, below left, ...) +\newcommand{\umlstatefinal}[1][]{% + \pgfkeys{/tikzuml/statefinal/.cd, x/.initial=\tikzumlDefaultX, y/.initial=\tikzumlDefaultX, width/.initial=\tikzumlStateFinalDefaultWidth,% + name/.initial=statefinal-\thetikzumlStateFinalNum,% + color/.initial=\tikzumlDefaultDrawColor, style/.style={},% + no coords/.is if=tikzumlstatefinalWithoutCoords,% + no coords=false,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{above}\OR% + \equal{\keyname}{left}\OR% + \equal{\keyname}{below}\OR% + \equal{\keyname}{right}\OR% + \equal{\keyname}{above left}\OR% + \equal{\keyname}{above right}\OR% + \equal{\keyname}{below left}\OR% + \equal{\keyname}{below right}}{% + \IfSubStr{\keyvalue}{ of }{% + \pgfkeys{/tikzuml/statefinal/.cd, no coords}% + }{}% + }{}% + \ifx\keyvalue\pgfkeysnovalue% + \pgfkeys{/tikzuml/statefinal/.cd, style/.append style/.expand once={\keyname}}% + \else% + \pgfkeys{/tikzuml/statefinal/.cd, style/.append style/.expand twice={\expandafter\keyname\expandafter=\keyvalue}}% + \fi% + %\errmessage{TIKZUML ERROR : in umlstatefinal, invalid option \keyname}% + }% + }% + \pgfkeys{/tikzuml/statefinal/.cd, #1}% + \pgfkeys{/tikzuml/statefinal/.cd, x/.get=\tikzumlStateFinalX, y/.get=\tikzumlStateFinalY, width/.get=\tikzumlStateFinalMinimumWidth,% + name/.get=\tikzumlStateFinalName, color/.get=\tikzumlStateFinalColor% + }% + % + \def\tikzumlStateFinalPos{\tikzumlStateFinalX,\tikzumlStateFinalY}% + % + \iftikzumlstatefinalWithoutCoords% + \node[circle, minimum size=\tikzumlStateFinalMinimumWidth, draw=\tikzumlStateFinalColor, fill=\tikzumlStateFinalColor, double, double distance=0.1cm, /tikzuml/statefinal/style] (\tikzumlStateFinalName) {};% + \else% + \node[circle, minimum size=\tikzumlStateFinalMinimumWidth, draw=\tikzumlStateFinalColor, fill=\tikzumlStateFinalColor, double, double distance=0.1cm, /tikzuml/statefinal/style] (\tikzumlStateFinalName) at (\tikzumlStateFinalPos) {};% + \fi% + % + % add to fit + \ifnum\c@tikzumlStateLevel>0% + \edef\tikzumlStateFitOld{\csname tikzumlStateFit\tikzumlState@parent @@\tikzumlState@fitname\endcsname}% + \expandafter\xdef\csname tikzumlStateFit\tikzumlState@parent @@\tikzumlState@fitname\endcsname{\tikzumlStateFitOld (\tikzumlStateFinalName)}% + \stepcounter{tikzumlStateSubStateNum}% + \fi% + \stepcounter{tikzumlStateFinalNum}% +}% +% +% define a uml enter state +% args : name of the state +% optional : x,y: coordinates of the state +% width: width of the state node +% name: name of the state node +% color: color of the join symbol +% style: to manage every default TikZ option +% no coords: to tell that the state position is defined relatively +% to another node (automatically used with TikZ options above, below, left, right, below left, ...) +\newcommand{\umlstateenter}[1][]{% + \pgfkeys{/tikzuml/stateenter/.cd, x/.initial=\tikzumlDefaultX, y/.initial=\tikzumlDefaultX, width/.initial=\tikzumlStateEnterDefaultWidth,% + name/.initial=stateenter-\thetikzumlStateEnterNum,% + color/.initial=\tikzumlDefaultDrawColor, style/.style={},% + no coords/.is if=tikzumlstateenterWithoutCoords,% + no coords=false,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{above}\OR% + \equal{\keyname}{left}\OR% + \equal{\keyname}{below}\OR% + \equal{\keyname}{right}\OR% + \equal{\keyname}{above left}\OR% + \equal{\keyname}{above right}\OR% + \equal{\keyname}{below left}\OR% + \equal{\keyname}{below right}}{% + \IfSubStr{\keyvalue}{ of }{% + \pgfkeys{/tikzuml/stateenter/.cd, no coords}% + }{}% + }{}% + \ifx\keyvalue\pgfkeysnovalue% + \pgfkeys{/tikzuml/stateenter/.cd, style/.append style/.expand once={\keyname}}% + \else% + \pgfkeys{/tikzuml/stateenter/.cd, style/.append style/.expand twice={\expandafter\keyname\expandafter=\keyvalue}}% + \fi% + %\errmessage{TIKZUML ERROR : in umlstateenter, invalid option \keyname}% + }% + }% + \pgfkeys{/tikzuml/stateenter/.cd, #1}% + \pgfkeys{/tikzuml/stateenter/.cd, x/.get=\tikzumlStateEnterX, y/.get=\tikzumlStateEnterY, width/.get=\tikzumlStateEnterMinimumWidth,% + name/.get=\tikzumlStateEnterName, color/.get=\tikzumlStateEnterColor% + }% + % + \def\tikzumlStateEnterPos{\tikzumlStateEnterX,\tikzumlStateEnterY}% + % + \iftikzumlstateenterWithoutCoords% + \node[circle, minimum size=\tikzumlStateEnterMinimumWidth, draw=\tikzumlStateEnterColor, /tikzuml/stateenter/style] (\tikzumlStateEnterName) {};% + \else% + \node[circle, minimum size=\tikzumlStateEnterMinimumWidth, draw=\tikzumlStateEnterColor, /tikzuml/stateenter/style] (\tikzumlStateEnterName) at (\tikzumlStateEnterPos) {};% + \fi% + % + % add to fit + \ifnum\c@tikzumlStateLevel>0% + \edef\tikzumlStateFitOld{\csname tikzumlStateFit\tikzumlState@parent @@\tikzumlState@fitname\endcsname}% + \expandafter\xdef\csname tikzumlStateFit\tikzumlState@parent @@\tikzumlState@fitname\endcsname{\tikzumlStateFitOld (\tikzumlStateEnterName)}% + \stepcounter{tikzumlStateSubStateNum}% + \fi% + \stepcounter{tikzumlStateEnterNum}% +}% +% +% define a uml exit state +% args : name of the state +% optional : x,y: coordinates of the state +% width: width of the state node +% name: name of the state node +% color: color of the join symbol +% style: to manage every default TikZ option +% no coords: to tell that the state position is defined relatively +% to another node (automatically used with TikZ options above, below, left, right, below left, ...) +\newcommand{\umlstateexit}[1][]{% + \pgfkeys{/tikzuml/stateexit/.cd, x/.initial=\tikzumlDefaultX, y/.initial=\tikzumlDefaultX, width/.initial=\tikzumlStateExitDefaultWidth,% + name/.initial=stateexit-\thetikzumlStateExitNum,% + color/.initial=\tikzumlDefaultDrawColor, style/.style={},% + no coords/.is if=tikzumlstateexitWithoutCoords,% + no coords=false,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{above}\OR% + \equal{\keyname}{left}\OR% + \equal{\keyname}{below}\OR% + \equal{\keyname}{right}\OR% + \equal{\keyname}{above left}\OR% + \equal{\keyname}{above right}\OR% + \equal{\keyname}{below left}\OR% + \equal{\keyname}{below right}}{% + \IfSubStr{\keyvalue}{ of }{% + \pgfkeys{/tikzuml/stateexit/.cd, no coords}% + }{}% + }{}% + \ifx\keyvalue\pgfkeysnovalue% + \pgfkeys{/tikzuml/stateexit/.cd, style/.append style/.expand once={\keyname}}% + \else% + \pgfkeys{/tikzuml/stateexit/.cd, style/.append style/.expand twice={\expandafter\keyname\expandafter=\keyvalue}}% + \fi% + %\errmessage{TIKZUML ERROR : in umlstateexit, invalid option \keyname}% + }% + }% + \pgfkeys{/tikzuml/stateexit/.cd, #1}% + \pgfkeys{/tikzuml/stateexit/.cd, x/.get=\tikzumlStateExitX, y/.get=\tikzumlStateExitY, width/.get=\tikzumlStateExitMinimumWidth,% + name/.get=\tikzumlStateExitName, color/.get=\tikzumlStateExitColor% + }% + % + \def\tikzumlStateExitPos{\tikzumlStateExitX,\tikzumlStateExitY}% + % + \iftikzumlstateexitWithoutCoords% + \node[circle, minimum size=\tikzumlStateExitMinimumWidth, draw=\tikzumlStateExitColor, /tikzuml/stateexit/style] (\tikzumlStateExitName) {};% + \else% + \node[circle, minimum size=\tikzumlStateExitMinimumWidth, draw=\tikzumlStateExitColor, /tikzuml/stateexit/style] (\tikzumlStateExitName) at (\tikzumlStateExitPos) {};% + \fi% + \draw[draw=\tikzumlStateExitColor] (\tikzumlStateExitName.north east) -- (\tikzumlStateExitName.south west) (\tikzumlStateExitName.north west) -- (\tikzumlStateExitName.south east); + % + % add to fit + \ifnum\c@tikzumlStateLevel>0% + \edef\tikzumlStateFitOld{\csname tikzumlStateFit\tikzumlState@parent @@\tikzumlState@fitname\endcsname}% + \expandafter\xdef\csname tikzumlStateFit\tikzumlState@parent @@\tikzumlState@fitname\endcsname{\tikzumlStateFitOld (\tikzumlStateExitName)}% + \stepcounter{tikzumlStateSubStateNum}% + \fi% + \stepcounter{tikzumlStateExitNum}% +}% +% +% define a uml end state +% args : name of the state +% optional : x,y: coordinates of the state +% width: width of the state node +% name: name of the state node +% color: color of the join symbol +% style: to manage every default TikZ option +% no coords: to tell that the state position is defined relatively +% to another node (automatically used with TikZ options above, below, left, right, below left, ...) +\newcommand{\umlstateend}[1][]{% + \pgfkeys{/tikzuml/stateend/.cd, x/.initial=\tikzumlDefaultX, y/.initial=\tikzumlDefaultX, width/.initial=\tikzumlStateEndDefaultWidth,% + name/.initial=stateend-\thetikzumlStateEndNum,% + color/.initial=\tikzumlDefaultDrawColor, style/.style={},% + no coords/.is if=tikzumlstateendWithoutCoords,% + no coords=false,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{above}\OR% + \equal{\keyname}{left}\OR% + \equal{\keyname}{below}\OR% + \equal{\keyname}{right}\OR% + \equal{\keyname}{above left}\OR% + \equal{\keyname}{above right}\OR% + \equal{\keyname}{below left}\OR% + \equal{\keyname}{below right}}{% + \IfSubStr{\keyvalue}{ of }{% + \pgfkeys{/tikzuml/stateend/.cd, no coords}% + }{}% + }{}% + \ifx\keyvalue\pgfkeysnovalue% + \pgfkeys{/tikzuml/stateend/.cd, style/.append style/.expand once={\keyname}}% + \else% + \pgfkeys{/tikzuml/stateend/.cd, style/.append style/.expand twice={\expandafter\keyname\expandafter=\keyvalue}}% + \fi% + %\errmessage{TIKZUML ERROR : in umlstateend, invalid option \keyname}% + }% + }% + \pgfkeys{/tikzuml/stateend/.cd, #1}% + \pgfkeys{/tikzuml/stateend/.cd, x/.get=\tikzumlStateEndX, y/.get=\tikzumlStateEndY, width/.get=\tikzumlStateEndMinimumWidth,% + name/.get=\tikzumlStateEndName, color/.get=\tikzumlStateEndColor% + }% + % + \def\tikzumlStateEndPos{\tikzumlStateEndX,\tikzumlStateEndY}% + % + \iftikzumlstateendWithoutCoords% + \node[circle, minimum size=\tikzumlStateEndMinimumWidth, /tikzuml/stateend/style] (\tikzumlStateEndName) {};% + \else% + \node[circle, minimum size=\tikzumlStateEndMinimumWidth, /tikzuml/stateend/style] (\tikzumlStateEndName) at (\tikzumlStateEndPos) {};% + \fi% + \draw[draw=\tikzumlStateEndColor] (\tikzumlStateEndName.north east) -- (\tikzumlStateEndName.south west) (\tikzumlStateEndName.north west) -- (\tikzumlStateEndName.south east); + % + % add to fit + \ifnum\c@tikzumlStateLevel>0% + \edef\tikzumlStateFitOld{\csname tikzumlStateFit\tikzumlState@parent @@\tikzumlState@fitname\endcsname}% + \expandafter\xdef\csname tikzumlStateFit\tikzumlState@parent @@\tikzumlState@fitname\endcsname{\tikzumlStateFitOld (\tikzumlStateEndName)}% + \stepcounter{tikzumlStateSubStateNum}% + \fi% + \stepcounter{tikzumlStateEndNum}% +}% +% +\newcommand{\picturedhistory}[1]{% + \begin{tikzpicture}[#1]% + \draw[thick] (-0.1cm,-0.15cm) -- (-0.1cm,0.15cm) + (-0.1cm,0) -- (0.1cm,0) + (0.1cm,-0.15cm) -- (0.1cm,0.15cm);% + \end{tikzpicture}% +}% +% +% define a uml history state +% args : name of the state +% optional : x,y: coordinates of the state +% width: width of the state node +% name: name of the state node +% color: color of the join symbol +% style: to manage every default TikZ option +% no coords: to tell that the state position is defined relatively +% to another node (automatically used with TikZ options above, below, left, right, below left, ...) +\newcommand{\umlstatehistory}[1][]{% + \pgfkeys{/tikzuml/statehistory/.cd, x/.initial=\tikzumlDefaultX, y/.initial=\tikzumlDefaultX, width/.initial=\tikzumlStateHistoryDefaultWidth,% + name/.initial=statehistory-\thetikzumlStateHistoryNum,% + color/.initial=\tikzumlDefaultDrawColor, style/.style={},% + no coords/.is if=tikzumlstatehistoryWithoutCoords,% + no coords=false,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{above}\OR% + \equal{\keyname}{left}\OR% + \equal{\keyname}{below}\OR% + \equal{\keyname}{right}\OR% + \equal{\keyname}{above left}\OR% + \equal{\keyname}{above right}\OR% + \equal{\keyname}{below left}\OR% + \equal{\keyname}{below right}}{% + \IfSubStr{\keyvalue}{ of }{% + \pgfkeys{/tikzuml/statehistory/.cd, no coords}% + }{}% + }{}% + \ifx\keyvalue\pgfkeysnovalue% + \pgfkeys{/tikzuml/statehistory/.cd, style/.append style/.expand once={\keyname}}% + \else% + \pgfkeys{/tikzuml/statehistory/.cd, style/.append style/.expand twice={\expandafter\keyname\expandafter=\keyvalue}}% + \fi% + %\errmessage{TIKZUML ERROR : in umlstatehistory, invalid option \keyname}% + }% + }% + \pgfkeys{/tikzuml/statehistory/.cd, #1}% + \pgfkeys{/tikzuml/statehistory/.cd, x/.get=\tikzumlStateHistoryX, y/.get=\tikzumlStateHistoryY, width/.get=\tikzumlStateHistoryMinimumWidth,% + name/.get=\tikzumlStateHistoryName, color/.get=\tikzumlStateHistoryColor% + }% + % + \def\tikzumlStateHistoryPos{\tikzumlStateHistoryX,\tikzumlStateHistoryY}% + % + \iftikzumlstatehistoryWithoutCoords% + \node[circle, minimum size=\tikzumlStateHistoryMinimumWidth, draw=\tikzumlStateHistoryColor, /tikzuml/statehistory/style] (\tikzumlStateHistoryName) {\picturedhistory{draw=\tikzumlStateHistoryColor}};% + \else% + \node[circle, minimum size=\tikzumlStateHistoryMinimumWidth, draw=\tikzumlStateHistoryColor, /tikzuml/statehistory/style] (\tikzumlStateHistoryName) at (\tikzumlStateHistoryPos) {\picturedhistory{draw=\tikzumlStateHistoryColor}};% + \fi% + % + % add to fit + \ifnum\c@tikzumlStateLevel>0% + \edef\tikzumlStateFitOld{\csname tikzumlStateFit\tikzumlState@parent @@\tikzumlState@fitname\endcsname}% + \expandafter\xdef\csname tikzumlStateFit\tikzumlState@parent @@\tikzumlState@fitname\endcsname{\tikzumlStateFitOld (\tikzumlStateHistoryName)}% + \stepcounter{tikzumlStateSubStateNum}% + \fi% + \stepcounter{tikzumlStateHistoryNum}% +}% +% +\newcommand{\pictureddeephistory}[1]{% + \begin{tikzpicture}[#1]% + \draw[thick] (-0.1cm,-0.15cm) -- (-0.1cm,0.15cm) + (-0.1cm,0) -- (0.1cm,0) + (0.1cm,-0.15cm) -- (0.1cm,0.15cm) + (0.23cm,0.19cm) -- (0.23cm,0.11cm) + (0.20cm,0.17cm) -- (0.26cm,0.13cm) + (0.20cm,0.13cm) -- (0.26cm,0.17cm);% + \end{tikzpicture}% +}% +% +% define a uml deep-history state +% args : name of the state +% optional : x,y: coordinates of the state +% width: width of the state node +% name: name of the state node +% color: color of the join symbol +% style: to manage every default TikZ option +% no coords: to tell that the state position is defined relatively +% to another node (automatically used with TikZ options above, below, left, right, below left, ...) +\newcommand{\umlstatedeephistory}[1][]{% + \pgfkeys{/tikzuml/statedeephistory/.cd, x/.initial=\tikzumlDefaultX, y/.initial=\tikzumlDefaultX, width/.initial=\tikzumlStateDeepHistoryDefaultWidth,% + name/.initial=statedeephistory-\thetikzumlStateDeepHistoryNum,% + color/.initial=\tikzumlDefaultDrawColor, style/.style={},% + no coords/.is if=tikzumlstatedeephistoryWithoutCoords,% + no coords=false,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{above}\OR% + \equal{\keyname}{left}\OR% + \equal{\keyname}{below}\OR% + \equal{\keyname}{right}\OR% + \equal{\keyname}{above left}\OR% + \equal{\keyname}{above right}\OR% + \equal{\keyname}{below left}\OR% + \equal{\keyname}{below right}}{% + \IfSubStr{\keyvalue}{ of }{% + \pgfkeys{/tikzuml/statedeephistory/.cd, no coords}% + }{}% + }{}% + \ifx\keyvalue\pgfkeysnovalue% + \pgfkeys{/tikzuml/statedeephistory/.cd, style/.append style/.expand once={\keyname}}% + \else% + \pgfkeys{/tikzuml/statedeephistory/.cd, style/.append style/.expand twice={\expandafter\keyname\expandafter=\keyvalue}}% + \fi% + %\errmessage{TIKZUML ERROR : in umlstatedeephistory, invalid option \keyname}% + }% + }% + \pgfkeys{/tikzuml/statedeephistory/.cd, #1}% + \pgfkeys{/tikzuml/statedeephistory/.cd, x/.get=\tikzumlStateDeepHistoryX, y/.get=\tikzumlStateDeepHistoryY, width/.get=\tikzumlStateDeepHistoryMinimumWidth,% + name/.get=\tikzumlStateDeepHistoryName, color/.get=\tikzumlStateDeepHistoryColor% + }% + % + \def\tikzumlStateDeepHistoryPos{\tikzumlStateDeepHistoryX,\tikzumlStateDeepHistoryY}% + % + \iftikzumlstatedeephistoryWithoutCoords% + \node[circle, minimum size=\tikzumlStateDeepHistoryMinimumWidth, draw=\tikzumlStateDeepHistoryColor, /tikzuml/statedeephistory/style] (\tikzumlStateDeepHistoryName) {\pictureddeephistory{draw=\tikzumlStateDeepHistoryColor}};% + \else% + \node[circle, minimum size=\tikzumlStateDeepHistoryMinimumWidth, draw=\tikzumlStateDeepHistoryColor, /tikzuml/statedeephistory/style] (\tikzumlStateDeepHistoryName) at (\tikzumlStateDeepHistoryPos) {\pictureddeephistory{draw=\tikzumlStateDeepHistoryColor}};% + \fi% + % + % add to fit + \ifnum\c@tikzumlStateLevel>0% + \edef\tikzumlStateFitOld{\csname tikzumlStateFit\tikzumlState@parent @@\tikzumlState@fitname\endcsname}% + \expandafter\xdef\csname tikzumlStateFit\tikzumlState@parent @@\tikzumlState@fitname\endcsname{\tikzumlStateFitOld (\tikzumlStateDeepHistoryName)}% + \stepcounter{tikzumlStateSubStateNum}% + \fi% + \stepcounter{tikzumlStateDeepHistoryNum}% +}% +% +% define a uml state +% args : name of the state +% content of the state +% optional : x,y: coordinates of the state +% width: width of the state node +% name: name of the state node +% entry, do, exit: entry/do/exit action of the state +% draw, fill, text: colors +% style: to manage every default TikZ option +% no coords: to tell that the state position is defined relatively +% to another node (automatically used with TikZ options above, below, left, right, below left, ...) +\newenvironment{umlstate}[2][]{% + \ifnum\thetikzumlStateLevel>0% + \let\tikzumlState@nameold\tikzumlState@fitname% + \let\tikzumlState@parentold\tikzumlState@parent% + \edef\tikzumlState@parent{\tikzumlState@parentold @@\tikzumlState@nameold}% + \else% + \def\tikzumlState@parent{}% + \fi% + % + \stepcounter{tikzumlStateLevel}% + % + \pgfkeys{/tikzuml/state/.cd, x/.initial=\tikzumlDefaultX, y/.initial=\tikzumlDefaultX, width/.initial=\tikzumlStateDefaultWidth,% + name/.initial={},% + entry/.initial={}, do/.initial={}, exit/.initial={},% + draw/.initial=\tikzumlDefaultDrawColor, fill/.initial=\tikzumlStateDefaultFillColor,% + text/.initial=\tikzumlDefaultTextColor, style/.style={},% + no coords/.is if=tikzumlstateWithoutCoords,% + no coords=false,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{above}\OR% + \equal{\keyname}{left}\OR% + \equal{\keyname}{below}\OR% + \equal{\keyname}{right}\OR% + \equal{\keyname}{above left}\OR% + \equal{\keyname}{above right}\OR% + \equal{\keyname}{below left}\OR% + \equal{\keyname}{below right}}{% + \IfSubStr{\keyvalue}{ of }{% + \pgfkeys{/tikzuml/state/.cd, no coords}% + }{}% + }{}% + \ifx\keyvalue\pgfkeysnovalue% + \pgfkeys{/tikzuml/state/.cd, style/.append style/.expand once={\keyname}}% + \else% + \pgfkeys{/tikzuml/state/.cd, style/.append style/.expand twice={\expandafter\keyname\expandafter=\keyvalue}}% + \fi% + % \errmessage{TIKZUML ERROR : in umlstate, invalid option \keyname}% + }% + }% + \pgfkeys{/tikzuml/state/.cd, #1}% + \pgfkeys{/tikzuml/state/.cd, x/.get=\tikzumlStateXShift, y/.get=\tikzumlStateYShift, width/.get=\tikzumlStateMinimumWidth, name/.get=\tikzumlStateName,% + entry/.get=\tikzumlStateEntry, do/.get=\tikzumlStateDo, exit/.get=\tikzumlStateExit,% + draw/.get=\tikzumlStateDrawColor, fill/.get=\tikzumlStateFillColor,% + text/.get=\tikzumlStateTextColor% + }% + % + \ifthenelse{\equal{\tikzumlStateName}{}}{% + \edef\tikzumlState@name{#2}% + }{% + \edef\tikzumlState@name{\tikzumlStateName}% + }% + % + \begingroup% + \def\_{@}\edef\x{\endgroup% + \def\noexpand\tikzumlState@fitname{\tikzumlState@name}}\x% + % + \let\tikzumlState@nodeNameold\tikzumlState@nodeName% + \def\tikzumlState@caption{#2}% + % + \begingroup% + \def\_{_}\edef\x{\endgroup% + \def\noexpand\tikzumlState@nodeName{\tikzumlState@name}}\x% + % + \expandafter\gdef\csname tikzumlStateFit\tikzumlState@parent @@\tikzumlState@fitname\endcsname{}% + % + \setcounter{tikzumlStateSubStateNum}{0}% + \setcounter{tikzumlStateText}{0}% + % + \def\tikzumlStateText{tikzumlEmpty}% + \begin{scope}[xshift=\tikzumlStateXShift cm, yshift=\tikzumlStateYShift cm]% +}{% + % + \def\tikzumlstaterootlabel{\phantom{\tikzumlState@nodeName}}% + % + \def\tikzumlstaterootinnerysep{0.5ex}% + \def\tikzumlstatebodyinnerysep{2ex}% + % + \ifthenelse{\equal{\tikzumlStateEntry}{}}{}{% + \ifnum\c@tikzumlStateText=0% + \def\tikzumlStateText{entry/\tikzumlStateEntry}% + \else% + \let\tikzumlStateTextOld\tikzumlStateText% + \ifthenelse{\equal{\tikzumlStateText}{tikzumlEmpty}}{% + \def\tikzumlStateText{entry/\tikzumlStateEntry}% + }{% + \expandafter\def\expandafter\tikzumlStateText\expandafter{\tikzumlStateTextOld \\ entry/\tikzumlStateEntry}% + }% + \fi% + \setcounter{tikzumlStateText}{1}% + \ifnum\c@tikzumlStateSubStateNum=0% + \def\tikzumlstatebodyinnerysep{0}% + \def\tikzumlstaterootinnerysep{0}% + \fi% + }% + \ifthenelse{\equal{\tikzumlStateDo}{}}{}{% + \ifnum\c@tikzumlStateText=0% + \def\tikzumlStateText{do/\tikzumlStateDo}% + \else% + \let\tikzumlStateTextOld\tikzumlStateText% + \ifthenelse{\equal{\tikzumlStateText}{tikzumlEmpty}}{% + \def\tikzumlStateText{do/\tikzumlStateDo}% + }{% + \expandafter\def\expandafter\tikzumlStateText\expandafter{\tikzumlStateTextOld \\ do/\tikzumlStateDo}% + }% + \fi% + \setcounter{tikzumlStateText}{1}% + \ifnum\c@tikzumlStateSubStateNum=0% + \def\tikzumlstatebodyinnerysep{0}% + \def\tikzumlstaterootinnerysep{0}% + \fi% + }% + \ifthenelse{\equal{\tikzumlStateExit}{}}{}{% + \ifnum\c@tikzumlStateText=0% + \def\tikzumlStateText{exit/\tikzumlStateExit}% + \else% + \let\tikzumlStateTextOld\tikzumlStateText% + \ifthenelse{\equal{\tikzumlStateText}{tikzumlEmpty}}{% + \def\tikzumlStateText{exit/\tikzumlStateExit}% + }{% + \expandafter\def\expandafter\tikzumlStateText\expandafter{\tikzumlStateTextOld \\ exit/\tikzumlStateExit}% + }% + \fi% + \setcounter{tikzumlStateText}{1}% + \ifnum\c@tikzumlStateSubStateNum=0% + \def\tikzumlstatebodyinnerysep{0}% + \def\tikzumlstaterootinnerysep{0}% + \fi% + }% + % + \addtocounter{tikzumlStateLevel}{-1}% + \begin{pgfonlayer}{state\thetikzumlStateLevel}% + % + % if contains nothing, one define a fictive node to enable the fit option + \ifnum\c@tikzumlStateSubStateNum=0% + \iftikzumlstateWithoutCoords% + \node[inner ysep=\tikzumlstaterootinnerysep, minimum width=\tikzumlStateMinimumWidth, /tikzuml/state/style] (\tikzumlState@nodeName-root) {\tikzumlstaterootlabel};% + \else% + \node[inner ysep=\tikzumlstaterootinnerysep, minimum width=\tikzumlStateMinimumWidth, /tikzuml/state/style] (\tikzumlState@nodeName-root) at (0,0) {\tikzumlstaterootlabel};% + \fi% + \expandafter\xdef\csname tikzumlStateFit\tikzumlState@parent @@\tikzumlState@fitname\endcsname{(\tikzumlState@nodeName-root)}% + \fi% + % + \ifnum\c@tikzumlStateLevel>0% + \def\tikzumlStateFitTmp{\csname tikzumlStateFit\tikzumlState@parent\endcsname}% + \expandafter\xdef\csname tikzumlStateFit\tikzumlState@parent\endcsname{\tikzumlStateFitTmp (\tikzumlState@nodeName-body) (\tikzumlState@nodeName-caption)}% + \stepcounter{tikzumlStateSubStateNum}% + \fi% + % + \node[inner xsep=2ex, inner ysep=\tikzumlstatebodyinnerysep, fit = \csname tikzumlStateFit\tikzumlState@parent @@\tikzumlState@fitname\endcsname, /tikzuml/state/style ] (\tikzumlState@nodeName-body) {};% + \def\tikzumlState@orig{body}% + \ifnum\c@tikzumlStateText=1% + \node[above=0] (\tikzumlState@nodeName-texttmp) at (\tikzumlState@nodeName-\tikzumlState@orig.north) {\begin{tabular}{l}\tikzumlStateText \end{tabular}};% + \def\tikzumlState@orig{texttmp}% + \fi% + \node[above] (\tikzumlState@nodeName-captiontmp) at (\tikzumlState@nodeName-\tikzumlState@orig.north) {\tikzumlState@caption};% + \ifnum\c@tikzumlStateText=1% + \node[rounded corners, draw=\tikzumlStateDrawColor, fill=\tikzumlStateFillColor, name=\tikzumlState@nodeName, fit=(\tikzumlState@nodeName-body) (\tikzumlState@nodeName-texttmp) (\tikzumlState@nodeName-captiontmp)] {};% + \else% + \node[rounded corners, draw=\tikzumlStateDrawColor, fill=\tikzumlStateFillColor, name=\tikzumlState@nodeName, fit=(\tikzumlState@nodeName-body) (\tikzumlState@nodeName-captiontmp)] {};% + \fi% + \ifnum\c@tikzumlStateText=1% + \node (\tikzumlState@nodeName-text) at (\tikzumlState@nodeName-texttmp) {\begin{tabular}{l}\tikzumlStateText \end{tabular}};% + \fi% + \node (\tikzumlState@nodeName-caption) at (\tikzumlState@nodeName-captiontmp) {\tikzumlState@caption};% + \draw (\tikzumlState@nodeName-caption.south -| \tikzumlState@nodeName.north west) -- (\tikzumlState@nodeName-caption.south -| \tikzumlState@nodeName.north east);% + \end{pgfonlayer}% + \end{scope}% +}% +% +% shortcut for empty state +\newcommand{\umlbasicstate}[2][]{\begin{umlstate}[#1]{#2}\end{umlstate}}% +% +% command to add text in a state, to be used inside umlstate environment +\newcommand{\umlstatetext}[1]{% + \def\tikzumlStateText{#1}% + \setcounter{tikzumlStateText}{1}% +}% +% +% shortcuts for state transitions macros +\newcommand{\umltrans}[3][]{% + \ifthenelse{\equal{#2}{#3}}{% + \umlrelation[style={tikzuml transition style}, recursive mode=transition, #1]{#2}{#3}% + }{% + \umlrelation[style={tikzuml transition style}, #1]{#2}{#3}% + }% +}% +\newcommand{\umlHVtrans}[3][]{\umlHVrelation[style={tikzuml transition style}, #1]{#2}{#3}}% +\newcommand{\umlVHtrans}[3][]{\umlVHrelation[style={tikzuml transition style}, #1]{#2}{#3}}% +\newcommand{\umlVHVtrans}[3][]{\umlVHVrelation[style={tikzuml transition style}, #1]{#2}{#3}}% +\newcommand{\umlHVHtrans}[3][]{\umlHVHrelation[style={tikzuml transition style}, #1]{#2}{#3}}% +\newcommand{\umlCNtrans}[4][]{\umlCNrelation[style={tikzuml transition style}, #1]{#2}{#3}{#4}}% +% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% sequence diagrams % +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +\tikzstyle{tikzuml synchron-msg style}=[color=\tikzumlDefaultDrawColor, -triangle 45]% +\tikzstyle{tikzuml asynchron-msg style}=[color=\tikzumlDefaultDrawColor, -angle 45]% +\tikzstyle{tikzuml return-msg style}=[color=\tikzumlDefaultDrawColor, dashed, -angle 45]% +\tikzstyle{tikzuml call return style}=[color=\tikzumlDefaultDrawColor, dashed, -angle 45]% +\tikzstyle{tikzuml activity style}=[inner xsep=1ex, inner ysep=1ex]% +% +\newcounter{tikzumlObjectNum}% +\newcounter{tikzumlCallLevel}% +\newcounter{tikzumlCallNum}% +\newcounter{tikzumlFragmentLevel}% +\newcounter{tikzumlFragmentLevelNum}% +\newcounter{tikzumlFragmentNum}% +\newcounter{tikzumlFragmentPartNum}% +\newcounter{tikzumlCallStartFragmentNum}% +\newcounter{tikzumlCallEndFragmentNum}% +% +\newif\iftikzumlobjectNoDDots% +\newif\iftikzumlcreatecallNoDDots% +% +% define a sequence diagram +% +\newenvironment{umlseqdiag}{% + \gdef\tikzumlInCreateCall{0}% + \setcounter{tikzumlObjectNum}{0}% + \setcounter{tikzumlCallLevel}{0}% + \setcounter{tikzumlCallNum}{0}% + \setcounter{tikzumlFragmentLevel}{0}% + \setcounter{tikzumlFragmentLevelNum}{0}% + \setcounter{tikzumlFragmentNum}{0}% + \setcounter{tikzumlFragmentPartNum}{0}% + \setcounter{tikzumlCallStartFragmentNum}{0}% + \setcounter{tikzumlCallEndFragmentNum}{0}% + % + \ifx \@umlactor \@empty + \newcommand{\umlactor}[2][]{% + \pgfkeys{/tikzuml/actorobj/.cd, .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{stereo}}{% + \errmessage{TIKZUML ERROR : in umlactor, forbidden option stereo}% + }{}% + }% + }% + % + \pgfkeys{/tikzuml/actorobj/.cd, ##1}% + \umlobject[stereo=actor, ##1]{##2}% + }% + \else% + \renewcommand{\umlactor}[2][]{% + \pgfkeys{/tikzuml/actorobj/.cd, .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{stereo}}{% + \errmessage{TIKZUML ERROR : in umlactor, forbidden option stereo}% + }{}% + }% + }% + % + \pgfkeys{/tikzuml/actorobj/.cd, ##1}% + \umlobject[stereo=actor, ##1]{##2}% + }% + \fi% + \begin{scope}[font=\tikzumlDefaultFont]% +}{% + % draw lifelines of each object + \begin{pgfonlayer}{lifelines}% + \foreach \id in \tikzumlIdList {% + \draw (\csname tikzumlLastChild@\id \endcsname)+(0,-2.5ex) node[inner sep=0, name=end-\id] {};% + \draw[dotted] (\id) -- (end-\id);% + }% + \end{pgfonlayer}% + \end{scope}% +}% +% +% define the database symbol +% optional : global tikzpicture styles +\newcommand{\pictureddatabase}[1]{% + \pgfkeys{/tikzuml/database/.cd, scale/.initial=1, .unknown/.code={}}% + \pgfkeys{/tikzuml/database/.cd,#1}% + \pgfkeys{/tikzuml/database/.cd, scale/.get=\tikzumlDatabaseScale}% + % + \begin{tikzpicture}[#1]% + \node[fill, draw, ellipse, minimum width=\tikzumlDatabaseScale*4ex, minimum height=\tikzumlDatabaseScale*2ex, inner sep=0] (bottom) at (0,-2ex) {};% + \node[fill, draw, ellipse, minimum width=\tikzumlDatabaseScale*4ex, minimum height=\tikzumlDatabaseScale*2ex, inner sep=0] (top) at (0,4ex) {};% + \fill (bottom.west) rectangle (top.east);% + \begin{scope}% + \clip (-3.5ex,-0.5ex) rectangle (3.5ex,2.5ex);% + \node[draw, dashed, ellipse, minimum width=\tikzumlDatabaseScale*4ex, minimum height=\tikzumlDatabaseScale*2ex, inner sep=0] (bottom2) at (0,-2ex) {};% + \end{scope}% + \node[draw, ellipse, minimum width=\tikzumlDatabaseScale*4ex, minimum height=\tikzumlDatabaseScale*2ex, inner sep=0] (top2) at (0,4ex) {};% + \draw (bottom.west) -- (top.west) (bottom.east) -- (top.east);% + \end{tikzpicture}% +}% +% +% define the entity symbol +% optional : global tikzpicture styles +\newcommand{\picturedentity}[1]{% + \pgfkeys{/tikzuml/entity/.cd, scale/.initial=1, .unknown/.code={}}% + \pgfkeys{/tikzuml/entity/.cd,#1}% + \pgfkeys{/tikzuml/entity/.cd, scale/.get=\tikzumlEntityScale}% + % + \begin{tikzpicture}[#1]% + \node[fill, draw, circle, inner sep=0, minimum size=\tikzumlEntityScale*5ex] (center) at (0,0) {};% + \draw (center.south) node[coordinate, name=bottom] {};% + \draw (bottom)+(-2ex,0) node[coordinate, name=bottom-left] {};% + \draw (bottom)+(2ex,0) node[coordinate, name=bottom-right] {};% + \draw (center) -- (bottom);% + \draw (bottom-left) -- (bottom-right);% + \end{tikzpicture}% +}% +% +% define the boundary symbol +% optional : global tikzpicture styles +\newcommand{\picturedboundary}[1]{% + \pgfkeys{/tikzuml/boundary/.cd, scale/.initial=1, .unknown/.code={}}% + \pgfkeys{/tikzuml/boundary/.cd,#1}% + \pgfkeys{/tikzuml/boundary/.cd, scale/.get=\tikzumlBoundaryScale}% + % + \begin{tikzpicture}[#1] + \node[fill, draw, circle, inner sep=0, minimum size=\tikzumlBoundaryScale*5ex] (center) at (0,0) {}; + \draw (center.west)+(-0.8ex,0) node[coordinate, name=left] {}; + \draw (left)+(0,0.2ex) node[coordinate, name=left-top] {}; + \draw (left)+(0,-0.2ex) node[coordinate, name=left-bottom] {}; + \draw (center) -- (left); + \draw (left-top) -- (left-bottom); + \end{tikzpicture} +}% +% +% define the control symbol +% optional : global tikzpicture styles +\newcommand{\picturedcontrol}[1]{% + \pgfkeys{/tikzuml/control/.cd, scale/.initial=1, .unknown/.code={}}% + \pgfkeys{/tikzuml/control/.cd,#1}% + \pgfkeys{/tikzuml/control/.cd, scale/.get=\tikzumlControlScale}% + % + \begin{tikzpicture}[#1, decoration={markings, mark=at position 0.25 with {\arrow{>}}}] + \node[fill, draw, circle, inner sep=0, minimum size=\tikzumlControlScale*5ex, postaction={decorate}] (center) at (0,0) {}; + \end{tikzpicture} +}% +% +% define a uml object for a sequence diagram +% args : name of the object +% optional : x, y: coordinates of the object +% stereo: stereotype of the object (object, actor, database, boundary, control, entity, multiobject) +% class: class of the object +% scale: scale factor of the object symbol +% draw, fill, text; colors +% no ddots: when used, disable printing of double dots +\newcommand{\umlobject}[2][]{ + \stepcounter{tikzumlObjectNum}% + % + \edef\tikzumlobject@ddot{:}% + \pgfkeys{/tikzuml/obj/.cd, x/.initial=tikzumlEmpty, y/.initial=\tikzumlDefaultX, stereo/.initial=\tikzumlObjectDefaultStereo,% + class/.initial={}, scale/.initial=1,% + draw/.initial=\tikzumlDefaultDrawColor, fill/.initial=\tikzumlObjectDefaultFillColor,% + text/.initial=\tikzumlDefaultTextColor,% + no ddots/.is if=tikzumlobjectNoDDots,% + no ddots=false,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \errmessage{TIKZUML ERROR : in umlobject, invalid option \keyname}% + }% + }% + \pgfkeys{/tikzuml/obj/.cd, #1}% + \pgfkeys{/tikzuml/obj/.cd, x/.get=\tikzumlObjectX, y/.get=\tikzumlObjectY,% + stereo/.get=\tikzumlObjectStereo, class/.get=\tikzumlObjectClass,% + scale/.get=\tikzumlObjectScale,% + draw/.get=\tikzumlObjectDrawColor, fill/.get=\tikzumlObjectFillColor,% + text/.get=\tikzumlObjectTextColor% + }% + % + \iftikzumlobjectNoDDots% + \edef\tikzumlobject@ddot{}% + \fi% + % + \ifthenelse{\equal{\tikzumlObjectX}{tikzumlEmpty}}{% + \pgfmathsetmacro{\tikzumlObjectX}{4*(\thetikzumlObjectNum-1)}% + }{}% + % + \def\tikzumlObjectName{#2}% + \expandafter\xdef\csname tikzumlLastChild@\tikzumlObjectName \endcsname{\tikzumlObjectName}% + % + \ifnum\thetikzumlObjectNum=1% + \xdef\tikzumlIdList{\tikzumlObjectName}% + \else% + \let\tikzumlIdListOld\tikzumlIdList% + \expandafter\xdef\expandafter\tikzumlIdList\expandafter{\tikzumlIdListOld,\tikzumlObjectName}% + \fi% + % + \tikzstyle{tikzuml object box style}=[rectangle, text=\tikzumlObjectTextColor, font=\tikzumlDefaultFont]% + % + \ifthenelse{\equal{\tikzumlObjectStereo}{object}}{% + \tikzstyle{tikzuml object box style}+=[draw=\tikzumlObjectDrawColor, fill=\tikzumlObjectFillColor]% + }{% + \ifthenelse{\equal{\tikzumlObjectStereo}{multi}}{% + \tikzstyle{tikzuml object box style}+=[fill=\tikzumlObjectFillColor]% + }{}% + }% + % + \ifnum\tikzumlInCreateCall=1% + \draw (\tikzumlCreateCallObjectSrc -| \tikzumlObjectX,0) node[tikzuml object box style] (\tikzumlObjectName) {\tikzumlObjectName\tikzumlobject@ddot\tikzumlObjectClass};% + \else% + \node[tikzuml object box style] (\tikzumlObjectName) at (\tikzumlObjectX,\tikzumlObjectY) {\tikzumlObjectName\tikzumlobject@ddot\tikzumlObjectClass};% + \fi% + % + \ifthenelse{\equal{\tikzumlObjectStereo}{multi}}{% + \draw (\tikzumlObjectName.north east)+(0.4ex,0.4ex) node[name=\tikzumlObjectName-tr, coordinate] {}; + \draw (\tikzumlObjectName.north west)+(0.4ex,0.4ex) node[name=\tikzumlObjectName-tl, coordinate] {}; + \draw (\tikzumlObjectName.south east)+(0.4ex,0.4ex) node[name=\tikzumlObjectName-br, coordinate] {}; + \draw (\tikzumlObjectName-tr)+(0.4ex,0.4ex) node[name=\tikzumlObjectName-ttr, coordinate] {}; + \draw (\tikzumlObjectName-tl)+(0.4ex,0.4ex) node[name=\tikzumlObjectName-ttl, coordinate] {}; + \draw (\tikzumlObjectName-br)+(0.4ex,0.4ex) node[name=\tikzumlObjectName-tbr, coordinate] {}; + \fill[fill=\tikzumlObjectFillColor] (\tikzumlObjectName-ttl |- \tikzumlObjectName.north) -- (\tikzumlObjectName-ttl) -- (\tikzumlObjectName-ttr) -- (\tikzumlObjectName-tbr) -- (\tikzumlObjectName-tbr -| \tikzumlObjectName.east) -- (\tikzumlObjectName.north east) -- (\tikzumlObjectName-ttl |- \tikzumlObjectName.north); + \draw[draw=\tikzumlObjectDrawColor] (\tikzumlObjectName-ttl |- \tikzumlObjectName.north) -- (\tikzumlObjectName-ttl) -- (\tikzumlObjectName-ttr) -- (\tikzumlObjectName-tbr) -- (\tikzumlObjectName-tbr -| \tikzumlObjectName.east); + \fill[fill=\tikzumlObjectFillColor] (\tikzumlObjectName-tl |- \tikzumlObjectName.north) -- (\tikzumlObjectName-tl) -- (\tikzumlObjectName-tr) -- (\tikzumlObjectName-br) -- (\tikzumlObjectName-br -| \tikzumlObjectName.east) -- (\tikzumlObjectName.north east) -- (\tikzumlObjectName-tl |- \tikzumlObjectName.north); + \draw[draw=\tikzumlObjectDrawColor] (\tikzumlObjectName-tl |- \tikzumlObjectName.north) -- (\tikzumlObjectName-tl) -- (\tikzumlObjectName-tr) -- (\tikzumlObjectName-br) -- (\tikzumlObjectName-br -| \tikzumlObjectName.east); + \draw[draw=\tikzumlObjectDrawColor] (\tikzumlObjectName.north west) rectangle (\tikzumlObjectName.south east); + }{% + \ifthenelse{\equal{\tikzumlObjectStereo}{object}}{}{% + \node[above=1ex, name=\tikzumlObjectName-picture] at (\tikzumlObjectName) {\csname pictured\tikzumlObjectStereo \endcsname{draw=\tikzumlObjectDrawColor, fill=\tikzumlObjectFillColor, scale=\tikzumlObjectScale}}; + }% + }% +}% +% +% shortcuts for objects +\newcommand{\umlbasicobject}[2][]{% + \pgfkeys{/tikzuml/basicobj/.cd, .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{no ddots}}{% + \errmessage{TIKZUML ERROR : in umlbasicobject, forbidden option no ddots}% + }{}% + }% + }% + \pgfkeys{/tikzuml/basicobj/.cd, #1}% + \umlobject[no ddots, #1]{#2}% +}% +% +\newcommand{\umldatabase}[2][]{% + \pgfkeys{/tikzuml/databaseobj/.cd, .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{stereo}}{% + \errmessage{TIKZUML ERROR : in umldatabase, forbidden option stereo}% + }{}% + }% + }% + \pgfkeys{/tikzuml/databaseobj/.cd, #1}% + \umlobject[stereo=database, #1]{#2}% +}% +\newcommand{\umlentity}[2][]{% + \pgfkeys{/tikzuml/entityobj/.cd, .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{stereo}}{% + \errmessage{TIKZUML ERROR : in umlentity, forbidden option stereo}% + }{}% + }% + }% + \pgfkeys{/tikzuml/entityobj/.cd, #1}% + \umlobject[stereo=entity, #1]{#2}% +}% +\newcommand{\umlcontrol}[2][]{% + \pgfkeys{/tikzuml/controlobj/.cd, .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{stereo}}{% + \errmessage{TIKZUML ERROR : in umlcontrol, forbidden option stereo}% + }{}% + }% + }% + \pgfkeys{/tikzuml/controlobj/.cd, #1}% + \umlobject[stereo=control, #1]{#2}% +}% +\newcommand{\umlboundary}[2][]{% + \pgfkeys{/tikzuml/boundaryobj/.cd, .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{stereo}}{% + \errmessage{TIKZUML ERROR : in umlboundary, forbidden option stereo}% + }{}% + }% + }% + \pgfkeys{/tikzuml/boundaryobj/.cd, #1}% + \umlobject[stereo=boundary, #1]{#2}% +}% +\newcommand{\umlmulti}[2][]{% + \pgfkeys{/tikzuml/multiobj/.cd, .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{stereo}}{% + \errmessage{TIKZUML ERROR : in umlmulti, forbidden option stereo}% + }{}% + }% + }% + \pgfkeys{/tikzuml/multiobj/.cd, #1}% + \umlobject[stereo=multi, #1]{#2}% +}% +% +\newcounter{tikzumlSDNodeNum}% +% +% define a hidden node to lengthen lifeline of a object +% args : object node +% optional : dt: distance between the sdnode and the last call defined on the lifeline of the object +% name: name of the sdnode +\newcommand{\umlsdnode}[2][]{% + \pgfkeys{/tikzuml/sdnode/.cd, dt/.initial=0, name/.initial=tikzumlEmpty}% + \pgfkeys{/tikzuml/sdnode/.cd, #1}% + \pgfkeys{/tikzuml/sdnode/.cd, dt/.get=\tikzumlSDNodeDT, name/.get=\tikzumlSDNodeName}% + % + \ifthenelse{\equal{\tikzumlSDNodeName}{tikzumlEmpty}}{% + \expandafter\def\expandafter\tikzumlSDNode@nodeName{sdnode-\thetikzumlSDNodeNum}% + }{% + \expandafter\def\expandafter\tikzumlSDNode@nodeName{\tikzumlSDNodeName}% + }% + % + \stepcounter{tikzumlSDNodeNum}% + % + \begingroup% + \def\_{_}\edef\x{\endgroup% + \def\noexpand\tikzumlSDNode@objnodeName{#2}}\x% + % + \draw (\expandafter\csname tikzumlLastChild@\tikzumlSDNode@objnodeName \endcsname)+(0,-\tikzumlSDNodeDT ex) node[name=\tikzumlSDNode@nodeName,coordinate] {};% + % + % update last node drawn on sender lifeline + \expandafter\xdef\csname tikzumlLastChild@\tikzumlSDNode@objnodeName \endcsname{\tikzumlSDNode@nodeName}% +}% +% +\newlength{\tikzumlCall@xa}% +\newlength{\tikzumlCall@xb}% +% +% define a uml operation call for sequence diagrams +% args : call sender +% call receiver +% optional : dt: time delay from precedent event end +% name: name of the call +% op: operation name and input args +% return: return value +% type: type of the call (synchron, asynchron) +% draw, fill, text: colors +% padding: time padding from call start and to call end +\newenvironment{umlcall}[3][]{% + \stepcounter{tikzumlCallNum}% + \def\tikzumlCallWithReturn{tikzumlFalse}% + \edef\tikzumlCall@lastchildNum{\thetikzumlCallNum}% for testing presence of sub-calls + \gdef\tikzumlCallBottom{0}% + % + \pgfkeys{/tikzuml/call/.cd, dt/.initial=\tikzumlCallDefaultDT, name/.initial={call-\thetikzumlCallNum},% + op/.initial={}, return/.initial={}, type/.initial=\tikzumlCallDefaultType,% + draw/.initial=\tikzumlDefaultDrawColor, fill/.initial=\tikzumlCallDefaultFillColor,% + text/.initial=\tikzumlDefaultTextColor,% + padding/.initial=\tikzumlCallDefaultPadding,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{with return}}{% + \def\tikzumlCallWithReturn{tikzumlTrue}% + }{% + \errmessage{TIKZUML ERROR : in umlcall, invalid option \keyname}% + }% + }% + }% + \pgfkeys{/tikzuml/call/.cd, #1}% + \pgfkeys{/tikzuml/call/.cd, dt/.get=\tikzumlCallDT, name/.get=\tikzumlCallName, op/.get=\tikzumlCallOp,% + return/.get=\tikzumlCallReturn, type/.get=\tikzumlCallType,% + padding/.get=\tikzumlCallPadding,% + draw/.get=\tikzumlCallDrawColor, fill/.get=\tikzumlCallFillColor,% + text/.get=\tikzumlCallTextColor% + }% + % + \edef\tikzumlfillcall{\tikzumlCallFillColor}% + \edef\tikzumldrawcall{\tikzumlCallDrawColor}% + \edef\tikzumltextcall{\tikzumlCallTextColor}% + \edef\tikzumltypecall{\tikzumlCallType}% + % + \ifthenelse{\equal{\tikzumlCallDT}{tikzumlEmpty}}{% + \ifnum\thetikzumlCallNum=1% + \def\tikzumlCallDT{2}% + \def\tikzumlcallSrc{2}% + \else% + \def\tikzumlCallDT{2}% + \def\tikzumlcallSrc{1}% + \fi% + }{% + \def\tikzumlcallSrc{0}% + }% + % + \let\tikzumlCallStartNodeNameold\tikzumlCallStartNodeName% + \def\tikzumlCallStartNodeName{#2}% + \let\tikzumlCallEndNodeNameold\tikzumlCallEndNodeName% + \def\tikzumlCallEndNodeName{#3}% + \def\tikzumlcallheight{\tikzumlCallPadding}% + % + % managing time delays from previous/parent fragments + \ifnum\thetikzumlCallStartFragmentNum>0% + \let\tikzumlCallDTold\tikzumlCallDT% + \pgfmathparse{0.5*\tikzumlFragment@paddingy+\tikzumlCallDTold}% + \edef\tikzumlCallDT{\pgfmathresult}% + \addtocounter{tikzumlCallStartFragmentNum}{-1} + \fi% + \ifnum\thetikzumlCallEndFragmentNum>0% + \let\tikzumlCallDTold\tikzumlCallDT% + \pgfmathparse{0.5*\tikzumlFragment@paddingy+\tikzumlCallDTold}% + \edef\tikzumlCallDT{\pgfmathresult}% + \addtocounter{tikzumlCallEndFragmentNum}{-1} + \fi% + \ifnum\thetikzumlFragmentPartNum>0% + \let\tikzumlCallDTold\tikzumlCallDT% + \pgfmathparse{0.5*\tikzumlFragment@paddingy+\tikzumlCallDTold}% + \edef\tikzumlCallDT{\pgfmathresult}% + \fi% + % + % managing parent-child structure + \ifnum\thetikzumlCallLevel>0% + \let\tikzumlCall@nameold\tikzumlCall@name% + \edef\tikzumlCall@name{\tikzumlCallName}% + \let\tikzumlCall@parentold\tikzumlCall@parent% + \edef\tikzumlCall@parent{\tikzumlCall@parentold @@\tikzumlCall@nameold}% + \else% + \edef\tikzumlCall@parent{}% + \edef\tikzumlCall@parentold{}% + \edef\tikzumlCall@nameold{} + \edef\tikzumlCall@name{\tikzumlCallName}% + \fi% + % + \begingroup% + \def\_{_}\edef\x{\endgroup% + \def\noexpand\tikzumlCall@nodeName{\tikzumlCall@name}}\x% + % + \let\tikzumlCall@nodeNameold\tikzumlCall@nodeName% + % + \def\tikzumlcallstyle{tikzuml \tikzumlCallType-msg style}% + % + % top node of activity period of call sender + \begin{pgfonlayer}{connections}% + \pgfmathparse{\tikzumlCallDT+\tikzumlcallSrc}% + \draw (\csname tikzumlLastChild@\tikzumlCallStartNodeName \endcsname)+(0,-\pgfmathresult ex) node[coordinate, name=tikzumlTmpNode] {};% + \node[tikzuml activity style] (st-\tikzumlCall@nodeName) at (\tikzumlCallStartNodeName |- tikzumlTmpNode) {};% + % + % update last node drawn on sender lifeline + \expandafter\xdef\csname tikzumlLastChild@\tikzumlCallStartNodeName \endcsname{st-\tikzumlCall@nodeName}% + % + % top node of activity period of call receiver + \ifstrequal{\tikzumlCallStartNodeName}{\tikzumlCallEndNodeName}{% + \draw (st-\tikzumlCall@nodeName)+(0,-0.75*\tikzumlCallPadding ex) node[coordinate, name=tikzumlTmpNode] {};% + \node[tikzuml activity style] (et-\tikzumlCall@nodeName) at (\tikzumlCallStartNodeName |- tikzumlTmpNode) {};% + }{% + \node[tikzuml activity style] (et-\tikzumlCall@nodeName) at (\tikzumlCallEndNodeName |- st-\tikzumlCall@nodeName) {};% + }% + % + % update last node drawn on receiver lifeline + \expandafter\xdef\csname tikzumlLastChild@\tikzumlCallEndNodeName \endcsname{et-\tikzumlCall@nodeName}% + \xdef\tikzumlCallBottomSrc{et-\tikzumlCall@nodeName}% + \end{pgfonlayer}% + % + \stepcounter{tikzumlCallLevel}% +}{% + \addtocounter{tikzumlCallLevel}{-1}% + % + % bottom nodes of activity periods of call sender and receiver + \begin{pgfonlayer}{connections}% + \ifnum\tikzumlCall@lastchildNum=\thetikzumlCallNum% + % + % this test occurs a bug with latex package preview + \ifstrequal{\tikzumlCallStartNodeName}{\tikzumlCallEndNodeName}{% + \draw (\tikzumlCallBottomSrc)+(0,-\tikzumlCallPadding ex) node[coordinate, name=tikzumlTmpNode] {};% + \node[tikzuml activity style] (eb-\tikzumlCall@nodeName) at (\tikzumlCallEndNodeName |- tikzumlTmpNode) {};% + \draw (eb-\tikzumlCall@nodeName)+(0,-0.75*\tikzumlCallPadding ex) node[coordinate, name=tikzumlTmpNode] {};% + \node[tikzuml activity style] (sb-\tikzumlCall@nodeName) at (\tikzumlCallStartNodeName |- tikzumlTmpNode) {};% + }{% + \ifthenelse{\equal{\tikzumlCallReturn}{tikzumlEmpty}}{% + \pgfmathsetmacro{\tikzumlCallPaddingd}{0.5*\tikzumlCallPadding}% + }{% + \pgfmathsetmacro{\tikzumlCallPaddingd}{1.2*\tikzumlCallPadding}% + }% + \draw (\tikzumlCallBottomSrc)+(0,-\tikzumlCallPaddingd ex) node[coordinate, name=tikzumlTmpNode] {};% + \node[tikzuml activity style] (eb-\tikzumlCall@nodeName) at (\tikzumlCallEndNodeName |- tikzumlTmpNode) {};% + \node[tikzuml activity style] (sb-\tikzumlCall@nodeName) at (\tikzumlCallStartNodeName |- eb-\tikzumlCall@nodeName) {};% + }% + \xdef\tikzumlCallBottomSrc{sb-\tikzumlCall@nodeName}% + \else% + % + % managing time delays from previous/parent fragments + \ifnum\thetikzumlCallStartFragmentNum>0% + \let\tikzumlcallheightold\tikzumlCallPadding% + \pgfmathparse{\tikzumlcallheightold+0.5*\tikzumlFragment@paddingy}% + \edef\tikzumlcallheight{\pgfmathresult}% + \addtocounter{tikzumlCallStartFragmentNum}{-1}% + \fi% + \ifnum\thetikzumlCallEndFragmentNum>0% + \let\tikzumlcallheightold\tikzumlCallPadding% + \pgfmathparse{\tikzumlcallheightold+0.5*\tikzumlFragment@paddingy}% + \edef\tikzumlcallheight{\pgfmathresult}% + \addtocounter{tikzumlCallEndFragmentNum}{-1}% + \fi% + % + \ifstrequal{\tikzumlCallStartNodeName}{\tikzumlCallEndNodeName}{% + \draw (\tikzumlCallBottomSrc)+(0,-\tikzumlcallheight ex) node[coordinate, name=tikzumlTmpNode] {};% + \node[tikzuml activity style] (eb-\tikzumlCall@nodeName) at (\tikzumlCallEndNodeName |- tikzumlTmpNode) {};% + \draw (eb-\tikzumlCall@nodeName)+(0,-0.75*\tikzumlCallPadding ex) node[coordinate, name=tikzumlTmpNode] {};% + \node[tikzuml activity style] (sb-\tikzumlCall@nodeName) at (\tikzumlCallStartNodeName |- tikzumlTmpNode) {};% + }{% + \draw (\tikzumlCallBottomSrc)+(0,-\tikzumlcallheight ex) node[coordinate, name=tikzumlTmpNode] {};% + \node[tikzuml activity style] (eb-\tikzumlCall@nodeName) at (\tikzumlCallEndNodeName |- tikzumlTmpNode) {};% + \node[tikzuml activity style] (sb-\tikzumlCall@nodeName) at (\tikzumlCallStartNodeName |- eb-\tikzumlCall@nodeName) {};% + }% + % + \xdef\tikzumlCallBottomSrc{sb-\tikzumlCall@nodeName}% + \fi% + \end{pgfonlayer}% + % + % draw activity periods + \begin{pgfonlayer}{activity}% + \ifstrequal{\tikzumlCallStartNodeName}{\tikzumlCallEndNodeName}{% + % draw root activity period only + \ifnum\thetikzumlCallLevel=0% + \draw[draw=\tikzumldrawcall, fill=\tikzumlfillcall] (st-\tikzumlCall@nodeName.north west) rectangle (sb-\tikzumlCall@nodeName.south east);% + \else% + % draw root activity from inner call + \ifthenelse{\equal{\tikzumlCallStartNodeName}{\tikzumlCallEndNodeNameold}}{}{% + \draw[draw=\tikzumldrawcall, fill=\tikzumlfillcall] (st-\tikzumlCall@nodeName.north west) rectangle (sb-\tikzumlCall@nodeName.south east);% + }% + \fi% + }{% + % draw root activity period + \ifnum\thetikzumlCallLevel=0% + \draw[draw=\tikzumldrawcall, fill=\tikzumlfillcall] (st-\tikzumlCall@nodeName.north west) rectangle (sb-\tikzumlCall@nodeName.south east);% + \else% + % draw root activity from inner call + \ifthenelse{\equal{\tikzumlCallStartNodeName}{\tikzumlCallEndNodeNameold}}{}{% + \draw[draw=\tikzumldrawcall, fill=\tikzumlfillcall] (st-\tikzumlCall@nodeName.north west) rectangle (sb-\tikzumlCall@nodeName.south east);% + }% + \fi% + % draw receiver activity period + \draw[draw=\tikzumldrawcall, fill=\tikzumlfillcall] (et-\tikzumlCall@nodeName.north west) rectangle (eb-\tikzumlCall@nodeName.south east);% + }% + \end{pgfonlayer}% + \ifthenelse{\equal{\tikzumlCallDefaultFillColor}{\tikzumlCallFillColor}}{}{% + \fill[\tikzumlfillcall] (st-\tikzumlCall@nodeName.north west) rectangle (sb-\tikzumlCall@nodeName.south east);% + \draw[\tikzumldrawcall] (st-\tikzumlCall@nodeName.north west) rectangle (sb-\tikzumlCall@nodeName.south west) (st-\tikzumlCall@nodeName.north east) rectangle (sb-\tikzumlCall@nodeName.south east);% + }% + % + % update last nodes drawn on sender and receiver lifelines + \expandafter\xdef\csname tikzumlLastChild@\tikzumlCallEndNodeName \endcsname{eb-\tikzumlCall@nodeName}% + \expandafter\xdef\csname tikzumlLastChild@\tikzumlCallStartNodeName \endcsname{sb-\tikzumlCall@nodeName}% + % + % draw call arrows + \begin{pgfonlayer}{connections}% + \ifstrequal{\tikzumlCallStartNodeName}{\tikzumlCallEndNodeName}{% + \draw[\tikzumlcallstyle, \tikzumldrawcall] (st-\tikzumlCall@nodeName.east) -- ++(2.5*\tikzumlCallPadding ex,0) % + -- ++(0,-0.75*\tikzumlCallPadding ex) % + node[font=\tikzumlDefaultFont, text=\tikzumltextcall, midway, right, name=\tikzumlCall@nodeName-op] {\tikzumlCallOp} % + -- (et-\tikzumlCall@nodeName.east);% + % + % draw return arrow and update fit for parent fragment + \ifthenelse{\equal{\tikzumltypecall}{synchron}}{% + \ifthenelse{\NOT\equal{\tikzumlCallReturn}{}\OR\equal{\tikzumlCallWithReturn}{tikzumlTrue}}{% + \ifnum\c@tikzumlFragmentLevel>0% + \edef\tikzumlFragmentFitOld{\csname tikzumlFragmentFit\tikzumlFragment@name \endcsname}% + \expandafter\xdef\csname tikzumlFragmentFit\tikzumlFragment@name \endcsname{\tikzumlFragmentFitOld (st-\tikzumlCall@nodeName) (et-\tikzumlCall@nodeName) (eb-\tikzumlCall@nodeName) (sb-\tikzumlCall@nodeName) (\tikzumlCall@nodeName-op) (\tikzumlCall@nodeName-return)}% + \fi% + % + \draw[tikzuml call return style, \tikzumldrawcall] (eb-\tikzumlCall@nodeName.east) -- ++(2.5*\tikzumlCallPadding ex,0) + -- ++(0,-0.75*\tikzumlCallPadding ex) % + node[font=\tikzumlDefaultFont, text=\tikzumltextcall, midway, right, name=\tikzumlCall@nodeName-return] {\tikzumlCallReturn} % + -- (sb-\tikzumlCall@nodeName.east);% + }{% + \ifnum\c@tikzumlFragmentLevel>0% + \edef\tikzumlFragmentFitOld{\csname tikzumlFragmentFit\tikzumlFragment@name \endcsname}% + \expandafter\xdef\csname tikzumlFragmentFit\tikzumlFragment@name \endcsname{\tikzumlFragmentFitOld (st-\tikzumlCall@nodeName) (et-\tikzumlCall@nodeName) (eb-\tikzumlCall@nodeName) (sb-\tikzumlCall@nodeName) (\tikzumlCall@nodeName-op)}% + \fi% + }% + }{% + \ifnum\c@tikzumlFragmentLevel>0% + \edef\tikzumlFragmentFitOld{\csname tikzumlFragmentFit\tikzumlFragment@name \endcsname}% + \expandafter\xdef\csname tikzumlFragmentFit\tikzumlFragment@name \endcsname{\tikzumlFragmentFitOld (st-\tikzumlCall@nodeName) (et-\tikzumlCall@nodeName) (eb-\tikzumlCall@nodeName) (sb-\tikzumlCall@nodeName) (\tikzumlCall@nodeName-op)}% + \fi% + }% + }{% + % draw call arrows + \pgfextractx{\tikzumlCall@xa}{\pgfpointanchor{\tikzumlCallStartNodeName}{center}}% + \pgfextractx{\tikzumlCall@xb}{\pgfpointanchor{\tikzumlCallEndNodeName}{center}}% + % + \ifthenelse{\tikzumlCall@xb>\tikzumlCall@xa}{% + \draw[\tikzumlcallstyle, \tikzumldrawcall] (st-\tikzumlCall@nodeName.east) -- (et-\tikzumlCall@nodeName.west) % + node[font=\tikzumlDefaultFont, text=\tikzumltextcall, midway, above=-0.4ex, name=\tikzumlCall@nodeName-op] {\tikzumlCallOp};% + }{% + \draw[\tikzumlcallstyle, \tikzumldrawcall] (st-\tikzumlCall@nodeName.west) -- (et-\tikzumlCall@nodeName.east) % + node[font=\tikzumlDefaultFont, text=\tikzumltextcall, midway, above=-0.4ex, name=\tikzumlCall@nodeName-op] {\tikzumlCallOp};% + }% + % + % draw return arrow and update fit for parent fragment + \ifthenelse{\equal{\tikzumltypecall}{synchron}}{% + \ifthenelse{\NOT\equal{\tikzumlCallReturn}{}\OR\equal{\tikzumlCallWithReturn}{tikzumlTrue}}{% + \ifnum\c@tikzumlFragmentLevel>0% + \edef\tikzumlFragmentFitOld{\csname tikzumlFragmentFit\tikzumlFragment@name \endcsname}% + \expandafter\xdef\csname tikzumlFragmentFit\tikzumlFragment@name \endcsname{\tikzumlFragmentFitOld (st-\tikzumlCall@nodeName) (et-\tikzumlCall@nodeName) (eb-\tikzumlCall@nodeName) (sb-\tikzumlCall@nodeName) (\tikzumlCall@nodeName-op) (\tikzumlCall@nodeName-return)}% + \fi% + % + \ifthenelse{\tikzumlCall@xb>\tikzumlCall@xa}{% + \draw[tikzuml call return style, \tikzumldrawcall] (eb-\tikzumlCall@nodeName.west) -- (sb-\tikzumlCall@nodeName.east) % + node[font=\tikzumlDefaultFont, text=\tikzumltextcall, midway, above=-0.4ex, name=\tikzumlCall@nodeName-return] {\tikzumlCallReturn};% + }{% + \draw[tikzuml call return style, \tikzumldrawcall] (eb-\tikzumlCall@nodeName.east) -- (sb-\tikzumlCall@nodeName.west) % + node[font=\tikzumlDefaultFont, text=\tikzumltextcall, midway, above=-0.4ex, name=\tikzumlCall@nodeName-return] {\tikzumlCallReturn};% + }% + }{% + \ifnum\c@tikzumlFragmentLevel>0% + \edef\tikzumlFragmentFitOld{\csname tikzumlFragmentFit\tikzumlFragment@name \endcsname}% + \expandafter\xdef\csname tikzumlFragmentFit\tikzumlFragment@name \endcsname{\tikzumlFragmentFitOld (st-\tikzumlCall@nodeName) (et-\tikzumlCall@nodeName) (eb-\tikzumlCall@nodeName) (sb-\tikzumlCall@nodeName) (\tikzumlCall@nodeName-op)}% + \fi% + }% + }{% + \ifnum\c@tikzumlFragmentLevel>0% + \edef\tikzumlFragmentFitOld{\csname tikzumlFragmentFit\tikzumlFragment@name \endcsname}% + \expandafter\xdef\csname tikzumlFragmentFit\tikzumlFragment@name \endcsname{\tikzumlFragmentFitOld (st-\tikzumlCall@nodeName) (et-\tikzumlCall@nodeName) (eb-\tikzumlCall@nodeName) (sb-\tikzumlCall@nodeName) (\tikzumlCall@nodeName-op)}% + \fi% + }% + }% + \end{pgfonlayer}% +}% +% +% alias for function self call +\newenvironment{umlcallself}[2][]{\begin{umlcall}[#1]{#2}{#2} }{\end{umlcall}}% +% +% define a combined fragment +% optional : name: name of fragment +% type: type of fragment (opt, alt, break, loop, par, critical, ignore, consider, assert, neg, weak, strict, ref) +% label: label of fragment (ex : condition for opt, iterator for loop, ...) +% inner xsep, inner ysep: padding of the fragment box +% draw, fill, text: colors +\newenvironment{umlfragment}[1][]{% + % define a fragment separator + % optional : label of the fragment part (ex : else for alt) + \providecommand{\umlfpart}[1][]{% + \stepcounter{tikzumlFragmentPartNum}% + % + \node[outer sep=0, inner xsep=\tikzumlFragmentXSep ex, inner ysep=\tikzumlFragmentYSep ex, fit=\csname tikzumlFragmentFit\tikzumlFragment@name \endcsname, name=\tikzumlFragment@name-Part-tmp] {};% + \node[anchor=east, name=\tikzumlFragment@name-PartType-\thetikzumlFragmentPartNum] at (\tikzumlFragment@name-Part-tmp.north west |- \tikzumlCallBottomSrc) {\phantom{\tikzumlFragmentType}};% + \draw (\tikzumlFragment@name-PartType-\thetikzumlFragmentPartNum.north west |- \tikzumlCallBottomSrc)+(0,-0.4*\tikzumlFragment@paddingy ex) node[name=\tikzumlFragment@name-PartWest-\thetikzumlFragmentPartNum] {};% + \draw (\tikzumlFragment@name-Part-tmp.north east |- \tikzumlCallBottomSrc)+(0,-0.4*\tikzumlFragment@paddingy ex) node[name=\tikzumlFragment@name-PartEast-\thetikzumlFragmentPartNum] {};% + \draw[dashed] (\tikzumlFragment@name-PartWest-\thetikzumlFragmentPartNum) -- (\tikzumlFragment@name-PartEast-\thetikzumlFragmentPartNum); + \draw (\tikzumlFragment@name-PartType-\thetikzumlFragmentPartNum)+(0,-0.4*\tikzumlFragment@paddingy ex) node[name=tikzumlTmpNode] {\phantom{\tikzumlFragmentType}};% + \node[anchor=north west] at (tikzumlTmpNode.south west) {[##1]};% + }% + % + \stepcounter{tikzumlFragmentNum}% + % + \pgfkeys{/tikzuml/fragment/.cd, name/.initial=fragment@\alph{tikzumlFragmentNum}, type/.initial=\tikzumlFragmentDefaultType,% + label/.initial=tikzumlEmpty,% + inner xsep/.initial=\tikzumlFragmentDefaultXSep, inner ysep/.initial=\tikzumlFragmentDefaultYSep,% + draw/.initial=\tikzumlDefaultDrawColor, fill/.initial=\tikzumlFragmentDefaultFillColor,% + text/.initial=\tikzumlDefaultTextColor,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \errmessage{TIKZUML ERROR : in umlfragment, invalid option \keyname}% + }% + }% + \pgfkeys{/tikzuml/fragment/.cd, #1}% + \pgfkeys{/tikzuml/fragment/.cd, name/.get=\tikzumlFragmentName, type/.get=\tikzumlFragmentType,% + label/.get=\tikzumlFragmentLabel,% + inner xsep/.get=\tikzumlFragmentXSep, inner ysep/.get=\tikzumlFragmentYSep,% + draw/.get=\tikzumlFragmentDrawColor, fill/.get=\tikzumlFragmentFillColor,% + text/.get=\tikzumlFragmentTextColor% + }% + % + \ifthenelse{\equal{\tikzumlFragmentLabel}{tikzumlEmpty}}{% + \def\tikzumlFragmentLabel{}% + }{% + \let\tikzumlFragmentLabelold\tikzumlFragmentLabel% + \def\tikzumlFragmentLabel{[\tikzumlFragmentLabelold]}% + }% + % + \ifnum\thetikzumlFragmentLevel>0% + \let\tikzumlFragment@parentold\tikzumlFragment@parent% + \let\tikzumlFragment@nameold\tikzumlFragment@name% + \edef\tikzumlFragment@parent{\tikzumlFragment@nameold}% + \else% + \setcounter{tikzumlFragmentPartNum}{0}% + \edef\tikzumlFragment@parent{}% + \edef\tikzumlFragment@parentold{}% + \edef\tikzumlFragment@nameold{}% + \fi% + % + \edef\tikzumlFragment@name{\tikzumlFragmentName}% + \expandafter\gdef\csname tikzumlFragmentFit\tikzumlFragment@name \endcsname{}% + % + \stepcounter{tikzumlFragmentLevel}% + % + \ifnum\thetikzumlCallLevel>0% + \stepcounter{tikzumlCallStartFragmentNum}% + \fi% + % + \pgfmathparse{6*\tikzumlFragmentYSep}% + \xdef\tikzumlFragment@paddingy{\pgfmathresult}% + \if\c@tikzumlFragmentLevelNum=0% + \setcounter{tikzumlFragmentLevelNum}{\thetikzumlFragmentLevel}% + \fi% + % + % time delay adjustment for two consecutive fragments + \ifnum\thetikzumlCallEndFragmentNum>0% + \addtocounter{tikzumlCallEndFragmentNum}{-1} + \fi% +}{% + % + \addtocounter{tikzumlFragmentLevel}{-1}% + % + \ifnum\thetikzumlFragmentLevel>0% + \edef\tikzumlFragmentFitOld{\csname tikzumlFragmentFit\tikzumlFragment@parent \endcsname}% + \expandafter\xdef\csname tikzumlFragmentFit\tikzumlFragment@parent \endcsname{\tikzumlFragmentFitOld (\tikzumlFragment@name)}% + \fi% + % + % draw working fragment box + \begin{pgfonlayer}{fragment\thetikzumlFragmentLevel}% + \node[outer sep=0, inner xsep=\tikzumlFragmentXSep ex, inner ysep=\tikzumlFragmentYSep ex, fit=\csname tikzumlFragmentFit\tikzumlFragment@name \endcsname, name=\tikzumlFragment@name-back] {};% + \end{pgfonlayer}% + % + % draw type and label + \node[text=\tikzumlFragmentTextColor, font=\tikzumlDefaultFont, anchor=north east, name=\tikzumlFragment@name-type] % + at (\tikzumlFragment@name-back.north west) {\tikzumlFragmentType};% + \node[text=\tikzumlFragmentTextColor, font=\tikzumlDefaultFont, anchor=north west, name=\tikzumlFragment@name-label] % + at (\tikzumlFragment@name-type.south west) {\tikzumlFragmentLabel};% + % + % draw final fragment box + \begin{pgfonlayer}{fragment\thetikzumlFragmentLevel}% + \node[draw=\tikzumlFragmentDrawColor, fill=\tikzumlFragmentFillColor, outer sep=0, inner sep=0, font=\tikzumlDefaultFont, fit=(\tikzumlFragment@name-back) (\tikzumlFragment@name-type) (\tikzumlFragment@name-label), name=\tikzumlFragment@name] {};% + \end{pgfonlayer}% + % + \draw[draw=\tikzumlFragmentDrawColor] (\tikzumlFragment@name.north west) rectangle (\tikzumlFragment@name.south east);% + \draw (\tikzumlFragment@name-type.south east)+(0,1ex) node[name=\tikzumlFragment@name-typetop, inner sep=0] {};% + \draw (\tikzumlFragment@name-type.south east)+(-1ex,0) node[name=\tikzumlFragment@name-typeleft, inner sep=0] {};% + \draw (\tikzumlFragment@name.north west) -| (\tikzumlFragment@name-typetop.center) -- (\tikzumlFragment@name-typeleft.center) -| (\tikzumlFragment@name.north west);% + % + \ifnum\thetikzumlCallLevel>0% + \stepcounter{tikzumlCallEndFragmentNum}% + \fi% +}% +% +% define a constructor call +% arg : call sender +% name of constructed object +% optional : x: coordinate of the new object +% stereo: stereotype of the new object +% class: class type of the new object +% dt: time delay from last event +% name: name of the call +% draw, fill, text: colors +% no ddots: when used, disable printing of double dots +\newcommand{\umlcreatecall}[3][]{% + \stepcounter{tikzumlCallNum}% + \edef\tikzumlCall@lastchildNum{\thetikzumlCallNum}% for testing presence of sub-calls + \gdef\tikzumlInCreateCall{1}% + \pgfkeys{/tikzuml/createcall/.cd, x/.initial=tikzumlEmpty, stereo/.initial=\tikzumlObjectDefaultStereo, class/.initial={},% + dt/.initial=\tikzumlCreateCallDefaultDT, name/.initial=call-\thetikzumlCallNum,% + draw/.initial=\tikzumlDefaultDrawColor, fill/.initial=\tikzumlCallDefaultFillColor,% + text/.initial=\tikzumlDefaultTextColor,% + draw obj/.initial=\tikzumlDefaultDrawColor, fill obj/.initial=\tikzumlObjectDefaultFillColor,% + text obj/.initial=\tikzumlDefaultTextColor,% + no ddots/.is if=tikzumlcreatecallNoDDots,% + no ddots=false,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \errmessage{TIKZUML ERROR : in umlcreatecall, invalid option \keyname}% + }% + }% + \pgfkeys{/tikzuml/createcall/.cd, #1}% + \pgfkeys{/tikzuml/createcall/.cd, x/.get=\tikzumlCreateCallX, stereo/.get=\tikzumlCreateCallStereo,% + class/.get=\tikzumlCreateCallClass,% + dt/.get=\tikzumlCreateCallDT, name/.get=\tikzumlCreateCallName,% + draw/.get=\tikzumlCreateCallDrawColor, fill/.get=\tikzumlCreateCallFillColor,% + text/.get=\tikzumlCreateCallTextColor,% + draw obj/.get=\tikzumlCreateCallObjectDrawColor, fill obj/.get=\tikzumlCreateCallObjectFillColor,% + text obj/.get=\tikzumlCreateCallObjectTextColor% + }% + % + \def\tikzumlCreateCallSrc@name{#2}% + % + \begingroup% + \def\_{_}\edef\x{\endgroup% + \def\noexpand\tikzumlCreateCallSrc@nodeName{\tikzumlCreateCallSrc@name}}\x% + % + % managing time delays from previous/parent fragments + \ifnum\thetikzumlCallStartFragmentNum>0% + \let\tikzumlCreateCallDTold\tikzumlCreateCallDT% + \pgfmathparse{0.5*\tikzumlFragment@paddingy+\tikzumlCreateCallDTold}% + \edef\tikzumlCreateCallDT{\pgfmathresult}% + \addtocounter{tikzumlCallStartFragmentNum}{-1} + \fi% + \ifnum\thetikzumlCallEndFragmentNum>0% + \let\tikzumlCreateCallDTold\tikzumlCreateCallDT% + \pgfmathparse{0.5*\tikzumlFragment@paddingy+\tikzumlCreateCallDTold}% + \edef\tikzumlCreateCallDT{\pgfmathresult}% + \addtocounter{tikzumlCallEndFragmentNum}{-1} + \fi% + \ifnum\thetikzumlFragmentPartNum>0% + \let\tikzumlCreateCallDTold\tikzumlCreateCallDT% + \pgfmathparse{0.5*\tikzumlFragment@paddingy+\tikzumlCreateCallDTold}% + \edef\tikzumlCreateCallDT{\pgfmathresult}% + \fi% + % + % managing parent-child structure + \ifnum\thetikzumlCallLevel>0% + \let\tikzumlCall@nameold\tikzumlCall@name% + \def\tikzumlCall@name{\tikzumlCreateCallName}% + \let\tikzumlCall@parentold\tikzumlCall@parent% + \edef\tikzumlCall@parent{\tikzumlCall@parentold @@\tikzumlCall@nameold}% + \else% + \edef\tikzumlCall@parent{}% + \edef\tikzumlCall@parentold{}% + \edef\tikzumlCall@nameold{} + \edef\tikzumlCall@name{\tikzumlCreateCallName}% + \fi% + % + \begingroup% + \def\_{_}\edef\x{\endgroup% + \def\noexpand\tikzumlCreateCall@nodeName{\tikzumlCall@name}}\x% + % + \draw (\csname tikzumlLastChild@\tikzumlCreateCallSrc@nodeName \endcsname)+(0,-\tikzumlCreateCallDT ex) node[name=st-\tikzumlCreateCall@nodeName, tikzuml activity style] {};% + % + \xdef\tikzumlCreateCallObjectSrc{st-\tikzumlCreateCall@nodeName}% + % + \iftikzumlcreatecallNoDDots% + \umlobject[x=\tikzumlCreateCallX, stereo=\tikzumlCreateCallStereo, class=\tikzumlCreateCallClass, draw=\tikzumlCreateCallObjectDrawColor, fill=\tikzumlCreateCallObjectFillColor, text=\tikzumlCreateCallObjectTextColor, no ddots]{#3}% + \else + \umlobject[x=\tikzumlCreateCallX, stereo=\tikzumlCreateCallStereo, class=\tikzumlCreateCallClass, draw=\tikzumlCreateCallObjectDrawColor, fill=\tikzumlCreateCallObjectFillColor, text=\tikzumlCreateCallObjectTextColor]{#3}% + \fi + % + \draw (\csname tikzumlLastChild@\tikzumlCreateCallSrc@nodeName \endcsname |- #3)+(0,-0.5*\tikzumlCreateCallDT ex) node[name=sb-\tikzumlCreateCall@nodeName, tikzuml activity style] {};% + % + \expandafter\xdef\csname tikzumlLastChild@\tikzumlCreateCallSrc@nodeName \endcsname{sb-\tikzumlCreateCall@nodeName}% + \xdef\tikzumlCallBottomSrc{sb-\tikzumlCreateCall@nodeName}% + % + \begin{pgfonlayer}{connections}% + \draw[tikzuml synchron-msg style, \tikzumlCreateCallDrawColor] (st-\tikzumlCreateCall@nodeName) -- (#3) node[midway, above, font=\tikzumlDefaultFont, text=\tikzumlCreateCallTextColor, name=\tikzumlCreateCall@nodeName-op] {create};% + \end{pgfonlayer}% + % + \ifnum\thetikzumlCallLevel=0% + \begin{pgfonlayer}{activity}% + \draw[draw=\tikzumlCreateCallDrawColor, fill=\tikzumlCreateCallFillColor] (st-\tikzumlCreateCall@nodeName.north west) rectangle (sb-\tikzumlCreateCall@nodeName.south east);% + \end{pgfonlayer}% + \fi% + % add to fit fragment + \ifnum\c@tikzumlFragmentLevel>0% + \edef\tikzumlFragmentFitOld{\csname tikzumlFragmentFit\tikzumlFragment@name \endcsname}% + \expandafter\xdef\csname tikzumlFragmentFit\tikzumlFragment@name \endcsname{\tikzumlFragmentFitOld (st-\tikzumlCreateCall@nodeName) (sb-\tikzumlCreateCall@nodeName) (\tikzumlCreateCall@nodeName-op) (#3) }% + \fi% +} +% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% component diagrams % +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +\tikzstyle{tikzuml connector style}=[color=\tikzumlDefaultDrawColor, -]% +% +\newcounter{tikzumlComponentLevel}% +\newcounter{tikzumlComponentSubComponentNum}% +\newcounter{tikzumlConnectorNum}% +\setcounter{tikzumlConnectorNum}{1}% +% +\newif\iftikzumlcomponentWithoutCoords% +% +\newcommand{\picturedcomponent}[1]{% + \pgfkeys{/tikzuml/component/picture/.cd, scale/.initial=1, .unknown/.code={}}% + \pgfkeys{/tikzuml/component/picture/.cd,#1}% + \pgfkeys{/tikzuml/component/picture/.cd, scale/.get=\tikzumlComponentScale}% + \begin{tikzpicture}[#1]% + \filldraw (0,0) rectangle (1ex,1.5ex);% + \filldraw (-0.2ex,0.4ex) rectangle (0.2ex,0.6ex);% + \filldraw (-0.2ex,0.9ex) rectangle (0.2ex,1.1ex);% + \end{tikzpicture}% +}% +% +% define a uml component +% args : name of the component +% content of the component +% optional args : x,y coordinates of the component +% width of the component node +\newenvironment{umlcomponent}[2][]{% + \ifnum\thetikzumlComponentLevel>0% + \let\tikzumlComponent@nameold\tikzumlComponent@fitname% + \let\tikzumlComponent@parentold\tikzumlComponent@parent% + \edef\tikzumlComponent@parent{\tikzumlComponent@parentold @@\tikzumlComponent@nameold}% + \else% + \def\tikzumlComponent@parent{}% + \fi% + % + \stepcounter{tikzumlComponentLevel}% + % + \pgfkeys{/tikzuml/component/.cd, x/.initial=\tikzumlDefaultX, y/.initial=\tikzumlDefaultX, width/.initial=\tikzumlComponentDefaultWidth, name/.initial={},% + draw/.initial=\tikzumlDefaultDrawColor, fill/.initial=\tikzumlComponentDefaultFillColor,% + text/.initial=\tikzumlDefaultTextColor, style/.style={},% + no coords/.is if=tikzumlcomponentWithoutCoords,% + no coords=false,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{above}\OR% + \equal{\keyname}{left}\OR% + \equal{\keyname}{below}\OR% + \equal{\keyname}{right}\OR% + \equal{\keyname}{above left}\OR% + \equal{\keyname}{above right}\OR% + \equal{\keyname}{below left}\OR% + \equal{\keyname}{below right}}{% + \IfSubStr{\keyvalue}{ of }{% + \pgfkeys{/tikzuml/component/.cd, no coords}% + }{}% + }{}% + \ifx\keyvalue\pgfkeysnovalue% + \pgfkeys{/tikzuml/component/.cd, style/.append style/.expand once={\keyname}}% + \else% + \pgfkeys{/tikzuml/component/.cd, style/.append style/.expand twice={\expandafter\keyname\expandafter=\keyvalue}}% + \fi% + %\errmessage{TIKZUML ERROR : in umlcomponent, invalid option \keyname}% + }% + }% + \pgfkeys{/tikzuml/component/.cd, #1}% + \pgfkeys{/tikzuml/component/.cd, x/.get=\tikzumlComponentXShift, y/.get=\tikzumlComponentYShift,% + width/.get=\tikzumlComponentMinimumWidth, name/.get=\tikzumlComponentName,% + draw/.get=\tikzumlComponentDrawColor, fill/.get=\tikzumlComponentFillColor,% + text/.get=\tikzumlComponentTextColor% + }% + % + \ifthenelse{\equal{\tikzumlComponentName}{}}{% + \edef\tikzumlComponent@name{#2}% + }{% + \edef\tikzumlComponent@name{\tikzumlComponentName}% + }% + % + \begingroup% + \def\_{@}\edef\x{\endgroup% + \def\noexpand\tikzumlComponent@fitname{\tikzumlComponent@name}}\x% + % + \let\tikzumlComponent@nodeNameold\tikzumlComponent@nodeName% + \def\tikzumlComponent@caption{#2}% + % + \begingroup% + \def\_{_}\edef\x{\endgroup% + \def\noexpand\tikzumlComponent@nodeName{\tikzumlComponent@name}}\x% + % + \expandafter\gdef\csname tikzumlComponentFit\tikzumlComponent@parent @@\tikzumlComponent@fitname\endcsname{}% + % + \setcounter{tikzumlComponentSubComponentNum}{0}% + % + \begin{scope}[xshift=\tikzumlComponentXShift cm, yshift=\tikzumlComponentYShift cm]% +}{% + \addtocounter{tikzumlComponentLevel}{-1}% + \begin{pgfonlayer}{component\thetikzumlComponentLevel}% + % + % if contains nothing, one define a fictive node to enable the fit option + \ifnum\c@tikzumlComponentSubComponentNum=0% + \iftikzumlcomponentWithoutCoords% + \node[inner ysep=0.5ex, minimum width=\tikzumlComponentMinimumWidth, font=\tikzumlDefaultFont, /tikzuml/component/style] (\tikzumlComponent@nodeName-root) {\phantom{\tikzumlComponent@nodeName}};% + \else% + \node[inner ysep=0.5ex, minimum width=\tikzumlComponentMinimumWidth, font=\tikzumlDefaultFont, /tikzuml/component/style] (\tikzumlComponent@nodeName-root) at (0,0) {\phantom{\tikzumlComponent@nodeName}};% + \fi% + \expandafter\xdef\csname tikzumlComponentFit\tikzumlComponent@parent @@\tikzumlComponent@fitname\endcsname{(\tikzumlComponent@nodeName-root)}% + \fi% + % + \ifnum\c@tikzumlComponentLevel>0% + \def\tikzumlComponentFitTmp{\csname tikzumlComponentFit\tikzumlComponent@parent\endcsname}% + \expandafter\xdef\csname tikzumlComponentFit\tikzumlComponent@parent\endcsname{\tikzumlComponentFitTmp (\tikzumlComponent@nodeName-body) (\tikzumlComponent@nodeName-caption)}% + \stepcounter{tikzumlComponentSubComponentNum}% + \fi% + % + \node[inner sep=2ex, font=\tikzumlDefaultFont, fit = \csname tikzumlComponentFit\tikzumlComponent@parent @@\tikzumlComponent@fitname\endcsname] (\tikzumlComponent@nodeName-body) {};% + \node[above, font=\tikzumlDefaultFont] (\tikzumlComponent@nodeName-captiontmp) at (\tikzumlComponent@nodeName-body.north) {\tikzumlComponent@caption};% + \node (\tikzumlComponent@nodeName-logotmp) at (\tikzumlComponent@nodeName-captiontmp.north -| \tikzumlComponent@nodeName-body.east) {\picturedcomponent{draw=\tikzumlComponentDrawColor, fill=\tikzumlComponentFillColor, font=\tikzumlDefaultFont} };% + \node[draw=\tikzumlComponentDrawColor, fill=\tikzumlComponentFillColor, name=\tikzumlComponent@nodeName, /tikzuml/component/style, fit=(\tikzumlComponent@nodeName-body) (\tikzumlComponent@nodeName-captiontmp)] {};% + \node[font=\tikzumlDefaultFont] (\tikzumlComponent@nodeName-caption) at (\tikzumlComponent@nodeName-captiontmp) {\tikzumlComponent@caption};% + \draw (\tikzumlComponent@nodeName-caption.north -| \tikzumlComponent@nodeName.east) node[font=\tikzumlDefaultFont, xshift=-1ex, below=-1ex, name=\tikzumlComponent@nodeName-logo] {\picturedcomponent{draw=\tikzumlComponentDrawColor, fill=\tikzumlComponentFillColor, font=\tikzumlDefaultFont} };% + \draw (\tikzumlComponent@nodeName-caption.south -| \tikzumlComponent@nodeName.north west) -- (\tikzumlComponent@nodeName-caption.south -| \tikzumlComponent@nodeName.north east);% + \coordinate (\tikzumlComponent@nodeName-west-port) at (\tikzumlComponent@nodeName.west); + \coordinate (\tikzumlComponent@nodeName-east-port) at (\tikzumlComponent@nodeName.east); + \coordinate (\tikzumlComponent@nodeName-south-port) at (\tikzumlComponent@nodeName.south); + \coordinate (\tikzumlComponent@nodeName-north-port) at (\tikzumlComponent@nodeName.north); + \end{pgfonlayer}% + \end{scope}% + % + % add to fit + \ifnum\c@tikzumlPackageLevel>0% + \edef\tikzumlPackageFitOld{\csname tikzumlPackageFit\tikzumlPackage@parent @@\tikzumlPackage@fitname\endcsname}% + \expandafter\xdef\csname tikzumlPackageFit\tikzumlPackage@parent @@\tikzumlPackage@fitname\endcsname{\tikzumlPackageFitOld (\tikzumlComponent@nodeName)}% + \stepcounter{tikzumlPackageClassNum}% + \fi% +}% +% +% shortcut for empty component +\newcommand{\umlbasiccomponent}[2][]{\begin{umlcomponent}[#1]{#2} \end{umlcomponent}}% +% +\newcommand{\umlrequiredinterface}[2][]{% + \def\tikzumlInterfaceWithPort{tikzumlFalse}% + \pgfkeys{/tikzuml/requiredinterfacerelation/.cd, interface/.initial={}, distance/.initial=\tikzumlRequiredInterfaceDefaultDistance,% + name/.initial=tikzumlEmpty, width/.initial=\tikzumlRequiredInterfaceDefaultWidth,% + padding/.initial=\tikzumlRequiredInterfaceDefaultPadding,% + draw/.initial=\tikzumlDefaultDrawColor, fill/.initial=\tikzumlComponentDefaultFillColor,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{with port}}{% + \def\tikzumlInterfaceWithPort{tikzumlTrue}% + }{}% + }% + }% + \pgfkeys{/tikzuml/requiredinterfacerelation/.cd, #1}% + \pgfkeys{/tikzuml/requiredinterfacerelation/.cd, interface/.get=\tikzumlRequiredInterfaceLabel,% + distance/.get=\tikzumlRequiredInterfaceDistance,% + name/.get=\tikzumlRequiredInterfaceName,% + width/.get=\tikzumlRequiredInterfaceWidth,% + padding/.get=\tikzumlRequiredInterfacePadding,% + draw/.get=\tikzumlRequiredInterfaceDrawColor,% + fill/.get=\tikzumlRequiredInterfaceFillColor% + }% + % + \ifthenelse{\equal{\tikzumlRequiredInterfaceName}{tikzumlEmpty}}{% + \edef\tikzumlRequiredInterface@interfacename{#2-east-interface}% + \edef\tikzumlRequiredInterface@portname{#2-east-port}% + \edef\tikzumlRequiredInterface@paddingname{#2-east-padding}% + }{% + \edef\tikzumlRequiredInterface@interfacename{\tikzumlRequiredInterfaceName}% + \edef\tikzumlRequiredInterface@portname{\tikzumlRequiredInterfaceName-port}% + \edef\tikzumlRequiredInterface@paddingname{\tikzumlRequiredInterfaceName-padding}% + }% + % + \edef\tikzumlRequiredInterface@name{#2}% + % + \begingroup% + \def\_{_}\edef\x{\endgroup% + \def\noexpand\tikzumlRequiredInterface@nodeName{\tikzumlRequiredInterface@name}}\x% + % + \begingroup% + \def\_{_}\edef\x{\endgroup% + \def\noexpand\tikzumlRequiredInterface@interfacenodeName{\tikzumlRequiredInterface@interfacename}}\x% + % + \begingroup% + \def\_{_}\edef\x{\endgroup% + \def\noexpand\tikzumlRequiredInterface@portnodeName{\tikzumlRequiredInterface@portname}}\x% + % + \begingroup% + \def\_{_}\edef\x{\endgroup% + \def\noexpand\tikzumlRequiredInterface@paddingnodeName{\tikzumlRequiredInterface@paddingname}}\x% + % + \ifthenelse{\equal{\tikzumlInterfaceWithPort}{tikzumlTrue}}{% + \node[inner sep=0.5*\tikzumlRequiredInterfaceWidth, rectangle, draw=\tikzumlRequiredInterfaceDrawColor, fill=\tikzumlRequiredInterfaceFillColor] (\tikzumlRequiredInterface@portnodeName) at (\tikzumlRequiredInterface@nodeName.east) {};% + }{% + \node[inner sep=0] (\tikzumlRequiredInterface@nodeName-east-port) at (\tikzumlRequiredInterface@nodeName.east) {};% + }% + \begin{scope}% + \draw (\tikzumlRequiredInterface@nodeName)+(\tikzumlRequiredInterfaceDistance,0) node[inner sep=0, text width=\tikzumlRequiredInterfaceWidth, circle, name=\tikzumlRequiredInterface@interfacenodeName-tmp] {};% + \clip (\tikzumlRequiredInterface@interfacenodeName-tmp.north) rectangle (\tikzumlRequiredInterface@interfacenodeName-tmp.south -| \tikzumlRequiredInterface@interfacenodeName-tmp.west);% + \node[inner sep=0, text width=\tikzumlRequiredInterfaceWidth, circle, draw=\tikzumlRequiredInterfaceDrawColor] (\tikzumlRequiredInterface@interfacenodeName) at (\tikzumlRequiredInterface@interfacenodeName-tmp) {};% + \end{scope}% + \node[above] at (\tikzumlRequiredInterface@interfacenodeName.north) {\tikzumlRequiredInterfaceLabel};% + % + \umlrelation[style={tikzuml connector style}, #1]{\tikzumlRequiredInterface@portnodeName}{\tikzumlRequiredInterface@interfacenodeName}% + % + \draw (\tikzumlRequiredInterface@interfacenodeName)+(\tikzumlRequiredInterfacePadding,0) node[name=\tikzumlRequiredInterface@paddingnodeName] {};% + % + % add to fit + \ifnum\c@tikzumlComponentLevel>0% + \def\tikzumlComponentFitTmp{\csname tikzumlComponentFit\tikzumlComponent@parent @@\tikzumlComponent@fitname\endcsname}% + \expandafter\xdef\csname tikzumlComponentFit\tikzumlComponent@parent @@\tikzumlComponent@fitname\endcsname{\tikzumlComponentFitTmp (\tikzumlRequiredInterface@paddingnodeName) (\tikzumlRequiredInterface@portnodeName) }% + \fi% +}% +% +\newcommand{\umlprovidedinterface}[2][]{% + \def\tikzumlInterfaceWithPort{tikzumlFalse}% + \pgfkeys{/tikzuml/providedinterfacerelation/.cd, interface/.initial={}, distance/.initial=\tikzumlProvidedInterfaceDefaultDistance,% + name/.initial=tikzumlEmpty, width/.initial=\tikzumlProvidedInterfaceDefaultWidth,% + padding/.initial=\tikzumlProvidedInterfaceDefaultPadding,% + draw/.initial=\tikzumlDefaultDrawColor, fill/.initial=\tikzumlComponentDefaultFillColor,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{with port}}{% + \def\tikzumlInterfaceWithPort{tikzumlTrue}% + }{}% + }% + }% + \pgfkeys{/tikzuml/providedinterfacerelation/.cd, #1}% + \pgfkeys{/tikzuml/providedinterfacerelation/.cd, interface/.get=\tikzumlProvidedInterfaceLabel,% + distance/.get=\tikzumlProvidedInterfaceDistance,% + name/.get=\tikzumlProvidedInterfaceName,% + width/.get=\tikzumlProvidedInterfaceWidth,% + padding/.get=\tikzumlProvidedInterfacePadding,% + draw/.get=\tikzumlProvidedInterfaceDrawColor,% + fill/.get=\tikzumlProvidedInterfaceFillColor% + }% + % + \ifthenelse{\equal{\tikzumlProvidedInterfaceName}{tikzumlEmpty}}{% + \edef\tikzumlProvidedInterface@interfacename{#2-west-interface}% + \edef\tikzumlProvidedInterface@portname{#2-west-port}% + \edef\tikzumlProvidedInterface@paddingname{#2-west-padding}% + }{% + \edef\tikzumlProvidedInterface@interfacename{\tikzumlProvidedInterfaceName}% + \edef\tikzumlProvidedInterface@portname{\tikzumlProvidedInterfaceName-port}% + \edef\tikzumlProvidedInterface@paddingname{\tikzumlProvidedInterfaceName-padding}% + }% + % + \edef\tikzumlProvidedInterface@name{#2}% + % + \begingroup% + \def\_{_}\edef\x{\endgroup% + \def\noexpand\tikzumlProvidedInterface@nodeName{\tikzumlProvidedInterface@name}}\x% + % + \begingroup% + \def\_{_}\edef\x{\endgroup% + \def\noexpand\tikzumlProvidedInterface@interfacenodeName{\tikzumlProvidedInterface@interfacename}}\x% + % + \begingroup% + \def\_{_}\edef\x{\endgroup% + \def\noexpand\tikzumlProvidedInterface@portnodeName{\tikzumlProvidedInterface@portname}}\x% + % + \begingroup% + \def\_{_}\edef\x{\endgroup% + \def\noexpand\tikzumlProvidedInterface@paddingnodeName{\tikzumlProvidedInterface@paddingname}}\x% + % + \ifthenelse{\equal{\tikzumlInterfaceWithPort}{tikzumlTrue}}{% + \node[inner sep=0.5*\tikzumlProvidedInterfaceWidth, rectangle, draw=\tikzumlProvidedInterfaceDrawColor, fill=\tikzumlProvidedInterfaceFillColor] (\tikzumlProvidedInterface@portnodeName) at (\tikzumlProvidedInterface@nodeName.west) {};% + }{% + \node[inner sep=0] (\tikzumlProvidedInterface@portnodeName) at (\tikzumlProvidedInterface@nodeName.west) {};% + }% + \draw (\tikzumlProvidedInterface@nodeName)+(-\tikzumlProvidedInterfaceDistance,0) node[inner sep=0, text width=\tikzumlProvidedInterfaceWidth, circle, draw=\tikzumlProvidedInterfaceDrawColor, fill=\tikzumlProvidedInterfaceFillColor, name=\tikzumlProvidedInterface@interfacenodeName] {};% + \node[above] at (\tikzumlProvidedInterface@interfacenodeName.north) + {\tikzumlProvidedInterfaceLabel};% + % + \umlrelation[style={tikzuml connector style}, #1]{\tikzumlProvidedInterface@portnodeName}{\tikzumlProvidedInterface@interfacenodeName}% + % + \draw (\tikzumlProvidedInterface@interfacenodeName)+(-\tikzumlProvidedInterfacePadding,0) node[name=\tikzumlProvidedInterface@paddingnodeName] {};% + % + % add to fit + \ifnum\thetikzumlComponentLevel>0% + \def\tikzumlComponentFitTmp{\csname tikzumlComponentFit\tikzumlComponent@parent @@\tikzumlComponent@fitname\endcsname}% + \expandafter\xdef\csname tikzumlComponentFit\tikzumlComponent@parent @@\tikzumlComponent@fitname\endcsname{\tikzumlComponentFitTmp (\tikzumlProvidedInterface@paddingnodeName) (\tikzumlProvidedInterface@portnodeName) }% + \fi% +}% +% +\newlength{\tikzuml@AC@xa}% +\newlength{\tikzuml@AC@ya}% +\newlength{\tikzuml@AC@xb}% +\newlength{\tikzuml@AC@yb}% +\newlength{\tikzuml@AC@xi}% +\newlength{\tikzuml@AC@yi}% +\newlength{\tikzuml@AC@xic}% +\newlength{\tikzuml@AC@yic}% +\newlength{\tikzuml@AC@xio}% +\newlength{\tikzuml@AC@yio}% +\newlength{\tikzuml@AC@AB}% +\newlength{\tikzuml@AC@lambda}% +\newlength{\tikzuml@AC@xtrc}% +\newlength{\tikzuml@AC@ytrc}% +\newlength{\tikzuml@AC@xtlc}% +\newlength{\tikzuml@AC@ytlc}% +\newlength{\tikzuml@AC@xblc}% +\newlength{\tikzuml@AC@yblc}% +\newlength{\tikzuml@AC@xbrc}% +\newlength{\tikzuml@AC@ybrc}% +\newlength{\tikzuml@AC@middleArm}% +% +\newcommand{\umlassemblyconnectorsymbol}[2]{% + \ifthenelse{\NOT\equal{\tikzumlAssemblyConnectorLabel}{}}{% + \edef\tikzuml@ACStart@name{#1}% + \edef\tikzuml@ACEnd@name{#2}% + \edef\tikzuml@AC@width{\tikzumlAssemblyConnectorWidth}% + % + \begingroup% + \def\_{_}\edef\x{\endgroup% + \def\noexpand\tikzuml@ACStart@nodeName{\tikzuml@ACStart@name}}\x% + % + \begingroup% + \def\_{_}\edef\x{\endgroup% + \def\noexpand\tikzuml@ACEnd@nodeName{\tikzuml@ACEnd@name}}\x% + % + \begingroup% + \def\_{_}\edef\x{\endgroup% + \def\noexpand\tikzuml@ACInterface@nodeName{\tikzumlAssemblyConnectorSymbolName}}\x% + % + \pgfextractx{\tikzuml@AC@xa}{\pgfpointanchor{\tikzuml@ACStart@nodeName}{\tikzumlAssemblyConnectorStartAnchor}}% + \pgfextracty{\tikzuml@AC@ya}{\pgfpointanchor{\tikzuml@ACStart@nodeName}{\tikzumlAssemblyConnectorStartAnchor}}% + \pgfextractx{\tikzuml@AC@xb}{\pgfpointanchor{\tikzuml@ACEnd@nodeName}{\tikzumlAssemblyConnectorEndAnchor}}% + \pgfextracty{\tikzuml@AC@yb}{\pgfpointanchor{\tikzuml@ACEnd@nodeName}{\tikzumlAssemblyConnectorEndAnchor}}% + \pgfmathsetlength{\tikzuml@AC@xi}{0.5*\tikzuml@AC@xa+0.5*\tikzuml@AC@xb}% + \pgfmathsetlength{\tikzuml@AC@yi}{0.5*\tikzuml@AC@ya+0.5*\tikzuml@AC@yb}% + \pgfmathsetlength{\tikzuml@AC@AB}{veclen(\tikzuml@AC@xa-\tikzuml@AC@xb,\tikzuml@AC@ya-\tikzuml@AC@yb)}% + \pgfmathsetlength{\tikzuml@AC@lambda}{0.25*\tikzuml@AC@width/\tikzuml@AC@AB}% + \pgfmathsetlength{\tikzuml@AC@xic}{\tikzuml@AC@xi-\tikzuml@AC@lambda*(\tikzuml@AC@xb-\tikzuml@AC@xa)}% + \pgfmathsetlength{\tikzuml@AC@yic}{\tikzuml@AC@yi-\tikzuml@AC@lambda*(\tikzuml@AC@yb-\tikzuml@AC@ya)}% + \pgfmathsetlength{\tikzuml@AC@xio}{\tikzuml@AC@xi+\tikzuml@AC@lambda*(\tikzuml@AC@xb-\tikzuml@AC@xa)}% + \pgfmathsetlength{\tikzuml@AC@yio}{\tikzuml@AC@yi+\tikzuml@AC@lambda*(\tikzuml@AC@yb-\tikzuml@AC@ya)}% + \node[inner sep=0.5*\tikzuml@AC@width] (\tikzuml@ACInterface@nodeName-interface) at (\tikzuml@AC@xi,\tikzuml@AC@yi) {};% + \node[inner sep=0, text width=\tikzuml@AC@width, circle, draw=\tikzumlAssemblyConnectorDrawColor, fill=\tikzumlAssemblyConnectorFillColor] (\tikzuml@ACInterface@nodeName-io) at (\tikzuml@AC@xio,\tikzuml@AC@yio) {};% + \begin{scope}% + \pgfmathsetlength{\tikzuml@AC@xtrc}{\tikzuml@AC@xic-2*\tikzuml@AC@lambda*(\tikzuml@AC@yb-\tikzuml@AC@ya)}% + \pgfmathsetlength{\tikzuml@AC@ytrc}{\tikzuml@AC@yic+2*\tikzuml@AC@lambda*(\tikzuml@AC@xb-\tikzuml@AC@xa)}% + \pgfmathsetlength{\tikzuml@AC@xbrc}{\tikzuml@AC@xic+2*\tikzuml@AC@lambda*(\tikzuml@AC@yb-\tikzuml@AC@ya)}% + \pgfmathsetlength{\tikzuml@AC@ybrc}{\tikzuml@AC@yic-2*\tikzuml@AC@lambda*(\tikzuml@AC@xb-\tikzuml@AC@xa)}% + \pgfmathsetlength{\tikzuml@AC@xtlc}{\tikzuml@AC@xic-3*\tikzuml@AC@lambda*(\tikzuml@AC@yb-\tikzuml@AC@ya+\tikzuml@AC@xb-\tikzuml@AC@xa)}% + \pgfmathsetlength{\tikzuml@AC@ytlc}{\tikzuml@AC@yic+3*\tikzuml@AC@lambda*(\tikzuml@AC@xb-\tikzuml@AC@xa+\tikzuml@AC@ya-\tikzuml@AC@yb)}% + \pgfmathsetlength{\tikzuml@AC@xblc}{\tikzuml@AC@xic+3*\tikzuml@AC@lambda*(\tikzuml@AC@yb-\tikzuml@AC@ya+\tikzuml@AC@xa-\tikzuml@AC@xb)}% + \pgfmathsetlength{\tikzuml@AC@yblc}{\tikzuml@AC@yic-3*\tikzuml@AC@lambda*(\tikzuml@AC@xb-\tikzuml@AC@xa+\tikzuml@AC@yb-\tikzuml@AC@ya)}% + \coordinate (\tikzuml@ACInterface@nodeName-trc) at (\tikzuml@AC@xtrc,\tikzuml@AC@ytrc);% + \coordinate (\tikzuml@ACInterface@nodeName-brc) at (\tikzuml@AC@xbrc,\tikzuml@AC@ybrc);% + \coordinate (\tikzuml@ACInterface@nodeName-tlc) at (\tikzuml@AC@xtlc,\tikzuml@AC@ytlc);% + \coordinate (\tikzuml@ACInterface@nodeName-blc) at (\tikzuml@AC@xblc,\tikzuml@AC@yblc);% + \clip (\tikzuml@ACInterface@nodeName-trc) -- (\tikzuml@ACInterface@nodeName-tlc) -- (\tikzuml@ACInterface@nodeName-blc) -- (\tikzuml@ACInterface@nodeName-brc) -- cycle;% + \node[inner sep=0, text width=\tikzuml@AC@width, circle, draw=\tikzumlAssemblyConnectorDrawColor, fill=\tikzumlAssemblyConnectorFillColor] (\tikzuml@ACInterface@nodeName-ic) at (\tikzuml@AC@xic,\tikzuml@AC@yic) {};% + \end{scope}% + \node[above, font=\tikzumlDefaultFont] at (\tikzuml@ACInterface@nodeName-interface.north) + {\tikzumlAssemblyConnectorLabel};% + }{}% +}% +% +\newcommand{\umlassemblyconnector}[3][]{% + \def\tikzumlAssemblyConnectorWithPort{tikzumlFalse}% + \def\tikzumlAssemblyConnectorFirstArm{tikzumlFalse}% + \def\tikzumlAssemblyConnectorSecondArm{tikzumlFalse}% + \def\tikzumlAssemblyConnectorMiddleArm{tikzumlFalse}% + \def\tikzumlAssemblyConnectorLastArm{tikzumlFalse}% + \pgfkeys{/tikzuml/assemblyconnectorrelation/.cd, geometry/.initial=--, interface/.initial={},% + arm1/.initial={auto}, arm2/.initial={auto},% + name/.initial=connector-\thetikzumlConnectorNum, width/.initial=1em,% + anchor1/.initial={}, anchor2/.initial={},% + draw/.initial=\tikzumlDefaultDrawColor,% + fill assembly connector/.initial=\tikzumlAssemblyConnectorDefaultFillColor,% + fill port/.initial=\tikzumlPortDefaultFillColor,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{with port}}{% + \def\tikzumlAssemblyConnectorWithPort{tikzumlTrue}% + }{% + \ifthenelse{\equal{\keyname}{first arm}}{% + \def\tikzumlAssemblyConnectorFirstArm{tikzumlTrue}% + }{% + \ifthenelse{\equal{\keyname}{second arm}}{% + \def\tikzumlAssemblyConnectorSecondArm{tikzumlTrue}% + }{% + \ifthenelse{\equal{\keyname}{middle arm}}{% + \def\tikzumlAssemblyConnectorMiddleArm{tikzumlTrue}% + }{% + \ifthenelse{\equal{\keyname}{last arm}}{% + \def\tikzumlAssemblyConnectorLastArm{tikzumlTrue}% + }{% + }% + }% + }% + }% + }% + }% + }% + \pgfkeys{/tikzuml/assemblyconnectorrelation/.cd, #1}% + \pgfkeys{/tikzuml/assemblyconnectorrelation/.cd, geometry/.get=\tikzumlAssemblyConnectorGeometry,% + name/.get=\tikzumlAssemblyConnectorName,% + interface/.get=\tikzumlAssemblyConnectorLabel,% + width/.get=\tikzumlAssemblyConnectorWidth,% + arm1/.get=\tikzumlAssemblyConnectorStartArm,% + arm2/.get=\tikzumlAssemblyConnectorEndArm,% + anchor1/.get=\tikzumlAssemblyConnectorStartAnchorTmp,% + anchor2/.get=\tikzumlAssemblyConnectorEndAnchorTmp,% + draw/.get=\tikzumlAssemblyConnectorDrawColor,% + fill assembly connector/.get=\tikzumlAssemblyConnectorFillColor,% + fill port/.get=\tikzumlAssemblyConnectorPortFillColor% + }% + % + \edef\tikzumlAssemblyConnectorStart@name{#2}% + \edef\tikzumlAssemblyConnectorEnd@name{#3}% + % + \begingroup% + \def\_{_}\edef\x{\endgroup% + \def\noexpand\tikzumlAssemblyConnectorStart@nodeName{\tikzumlAssemblyConnectorStart@name}}\x% + % + \begingroup% + \def\_{_}\edef\x{\endgroup% + \def\noexpand\tikzumlAssemblyConnectorEnd@nodeName{\tikzumlAssemblyConnectorEnd@name}}\x% + % + \begingroup% + \def\_{_}\edef\x{\endgroup% + \def\noexpand\tikzumlAssemblyConnectorLabel@nodeName{\tikzumlAssemblyConnectorLabel}}\x% + % + \pgfextractx{\tikzuml@AC@xa}{\pgfpointanchor{\tikzumlAssemblyConnectorStart@nodeName}{center}}% + \pgfextracty{\tikzuml@AC@ya}{\pgfpointanchor{\tikzumlAssemblyConnectorStart@nodeName}{center}}% + \pgfextractx{\tikzuml@AC@xb}{\pgfpointanchor{\tikzumlAssemblyConnectorEnd@nodeName}{center}}% + \pgfextracty{\tikzuml@AC@yb}{\pgfpointanchor{\tikzumlAssemblyConnectorEnd@nodeName}{center}}% + % + \ifthenelse{\equal{\tikzumlAssemblyConnectorGeometry}{--}}{% + \ifthenelse{\tikzuml@AC@xb>\tikzuml@AC@xa}{% + \def\tikzumlAssemblyConnectorStartAnchor{east}% + \def\tikzumlAssemblyConnectorEndAnchor{west}% + }{% + \def\tikzumlAssemblyConnectorStartAnchor{west}% + \def\tikzumlAssemblyConnectorEndAnchor{east}% + } + }{}% + % + \ifthenelse{\equal{\tikzumlAssemblyConnectorGeometry}{-|}}{% + \ifthenelse{\tikzuml@AC@xb>\tikzuml@AC@xa}{% + \def\tikzumlAssemblyConnectorStartAnchor{east}% + }{% + \def\tikzumlAssemblyConnectorStartAnchor{west}% + } + \ifthenelse{\tikzuml@AC@yb>\tikzuml@AC@ya}{% + \def\tikzumlAssemblyConnectorEndAnchor{south}% + }{% + \def\tikzumlAssemblyConnectorEndAnchor{north}% + } + }{}% + % + \ifthenelse{\equal{\tikzumlAssemblyConnectorGeometry}{|-}}{% + \ifthenelse{\tikzuml@AC@xb>\tikzuml@AC@xa}{% + \def\tikzumlAssemblyConnectorEndAnchor{west}% + }{% + \def\tikzumlAssemblyConnectorEndAnchor{east}% + } + \ifthenelse{\tikzuml@AC@yb>\tikzuml@AC@ya}{% + \def\tikzumlAssemblyConnectorStartAnchor{north}% + }{% + \def\tikzumlAssemblyConnectorStartAnchor{south}% + } + }{}% + % + \ifthenelse{\equal{\tikzumlAssemblyConnectorGeometry}{-|-}}{% + \ifthenelse{\equal{\tikzumlAssemblyConnectorStartArm}{auto}}{% + \ifthenelse{\equal{\tikzumlAssemblyConnectorEndArm}{auto}}{% + \pgfmathsetlength{\tikzuml@AC@middleArm}{0.5 * \tikzuml@AC@xa + 0.5 * \tikzuml@AC@xb}% + }{% + \pgfmathsetlength{\tikzuml@AC@middleArm}{\tikzuml@AC@xb+\tikzumlAssemblyConnectorEndArm}% + }% + }{% + \pgfmathsetlength{\tikzuml@AC@middleArm}{\tikzuml@AC@xa+\tikzumlAssemblyConnectorStartArm}% + }% + \pgfmathparse{\tikzuml@AC@middleArm>\tikzuml@AC@xa} + \pgfmathparse{\tikzuml@AC@middleArm>\tikzuml@AC@xb} + \ifthenelse{\lengthtest{\tikzuml@AC@middleArm>\tikzuml@AC@xa}}{% + \def\tikzumlAssemblyConnectorStartAnchor{east}% + }{% + \def\tikzumlAssemblyConnectorStartAnchor{west}% + } + \ifthenelse{\lengthtest{\tikzuml@AC@middleArm>\tikzuml@AC@xb}}{% + \def\tikzumlAssemblyConnectorEndAnchor{east}% + }{% + \def\tikzumlAssemblyConnectorEndAnchor{west}% + } + }{}% + % + \ifthenelse{\equal{\tikzumlAssemblyConnectorGeometry}{|-|}}{% + \ifthenelse{\equal{\tikzumlAssemblyConnectorStartArm}{auto}}{% + \ifthenelse{\equal{\tikzumlAssemblyConnectorEndArm}{auto}}{% + \pgfmathsetlength{\tikzuml@AC@middleArm}{0.5 * \tikzuml@AC@ya + 0.5 * \tikzuml@AC@yb}% + }{% + \pgfmathsetlength{\tikzuml@AC@middleArm}{\tikzuml@AC@yb+\tikzumlAssemblyConnectorEndArm}% + }% + }{% + \pgfmathsetlength{\tikzuml@AC@middleArm}{\tikzuml@AC@ya+\tikzumlAssemblyConnectorStartArm}% + }% + \ifthenelse{\tikzuml@AC@middleArm>\tikzuml@AC@ya}{% + \def\tikzumlAssemblyConnectorStartAnchor{north}% + }{% + \def\tikzumlAssemblyConnectorStartAnchor{south}% + } + \ifthenelse{\tikzuml@AC@middleArm>\tikzuml@AC@yb}{% + \def\tikzumlAssemblyConnectorEndAnchor{north}% + }{% + \def\tikzumlAssemblyConnectorEndAnchor{south}% + } + }{}% + % + \ifthenelse{\equal{\tikzumlAssemblyConnectorStartAnchorTmp}{}}{% + }{% + \def\tikzumlAssemblyConnectorStartAnchor{\tikzumlAssemblyConnectorStartAnchorTmp}% + }% + \ifthenelse{\equal{\tikzumlAssemblyConnectorEndAnchorTmp}{}}{% + }{% + \def\tikzumlAssemblyConnectorEndAnchor{\tikzumlAssemblyConnectorEndAnchorTmp}% + }% + % + \node[inner sep=0] (\tikzumlAssemblyConnectorStart@nodeName-\tikzumlAssemblyConnectorLabel@nodeName-port-tmp) at (\tikzumlAssemblyConnectorStart@nodeName.\tikzumlAssemblyConnectorStartAnchor) {};% + \node[inner sep=0] (\tikzumlAssemblyConnectorEnd@nodeName-\tikzumlAssemblyConnectorLabel@nodeName-port-tmp) at (\tikzumlAssemblyConnectorEnd@nodeName.\tikzumlAssemblyConnectorEndAnchor) {};% + % + \umlrelation[style={tikzuml connector style}, #1]{\tikzumlAssemblyConnectorStart@nodeName-\tikzumlAssemblyConnectorLabel@nodeName-port-tmp}{\tikzumlAssemblyConnectorEnd@nodeName-\tikzumlAssemblyConnectorLabel@nodeName-port-tmp}% + % + \ifthenelse{\equal{\tikzumlAssemblyConnectorWithPort}{tikzumlTrue}}{% + \node[inner sep=0.5*\tikzumlAssemblyConnectorWidth, rectangle, draw=\tikzumlAssemblyConnectorDrawColor, fill=\tikzumlAssemblyConnectorPortFillColor] (\tikzumlAssemblyConnectorStart@nodeName-\tikzumlAssemblyConnectorLabel@nodeName-port) at (\tikzumlAssemblyConnectorStart@nodeName-\tikzumlAssemblyConnectorLabel@nodeName-port-tmp) {};% + \node[inner sep=0.5*\tikzumlAssemblyConnectorWidth, rectangle, draw=\tikzumlAssemblyConnectorDrawColor, fill=\tikzumlAssemblyConnectorPortFillColor] (\tikzumlAssemblyConnectorEnd@nodeName-\tikzumlAssemblyConnectorLabel@nodeName-port) at (\tikzumlAssemblyConnectorEnd@nodeName-\tikzumlAssemblyConnectorLabel@nodeName-port-tmp) {};% + }{% + \node[inner sep=0] (\tikzumlAssemblyConnectorStart@nodeName-\tikzumlAssemblyConnectorLabel@nodeName-port) at (\tikzumlAssemblyConnectorStart@nodeName.\tikzumlAssemblyConnectorStartAnchor) {};% + \node[inner sep=0] (\tikzumlAssemblyConnectorEnd@nodeName-\tikzumlAssemblyConnectorLabel@nodeName-port) at (\tikzumlAssemblyConnectorEnd@nodeName.\tikzumlAssemblyConnectorEndAnchor) {};% + }% + % + \addtocounter{tikzumlRelationNum}{-1}% + \ifthenelse{\equal{\tikzumlAssemblyConnectorName}{connector-\thetikzumlConnectorNum}}{% + \edef\tikzumlAssemblyConnectorName{relation-\thetikzumlRelationNum}% + \edef\tikzumlAssemblyConnectorSymbolName{\tikzumlAssemblyConnectorLabel@nodeName}% + }{% + \edef\tikzumlAssemblyConnectorSymbolName{\tikzumlAssemblyConnectorName}% + }% + % + \stepcounter{tikzumlRelationNum}% + % + \ifthenelse{\equal{\tikzumlAssemblyConnectorGeometry}{--}}{% + \umlassemblyconnectorsymbol{\tikzumlAssemblyConnectorStart@nodeName}{\tikzumlAssemblyConnectorEnd@nodeName}% + }{% + \ifthenelse{\equal{\tikzumlAssemblyConnectorGeometry}{-|}}{% + \ifthenelse{\equal{\tikzumlAssemblyConnectorFirstArm}{tikzumlTrue}}{% + \umlassemblyconnectorsymbol{\tikzumlAssemblyConnectorStart@nodeName}{\tikzumlAssemblyConnectorName-2}% + }{% + \umlassemblyconnectorsymbol{\tikzumlAssemblyConnectorName-2}{\tikzumlAssemblyConnectorEnd@nodeName}% + }% + }{% + \ifthenelse{\equal{\tikzumlAssemblyConnectorGeometry}{|-}}{% + \ifthenelse{\equal{\tikzumlAssemblyConnectorFirstArm}{tikzumlTrue}}{% + \umlassemblyconnectorsymbol{\tikzumlAssemblyConnectorStart@nodeName}{\tikzumlAssemblyConnectorName-2}% + }{% + \umlassemblyconnectorsymbol{\tikzumlAssemblyConnectorName-2}{\tikzumlAssemblyConnectorEnd@nodeName}% + }% + }{% + \ifthenelse{\equal{\tikzumlAssemblyConnectorGeometry}{-|-}}{% + \ifthenelse{\equal{\tikzumlAssemblyConnectorFirstArm}{tikzumlTrue}}{% + \umlassemblyconnectorsymbol{\tikzumlAssemblyConnectorStart@nodeName}{\tikzumlAssemblyConnectorName-2}% + }{% + \ifthenelse{\equal{\tikzumlAssemblyConnectorLastArm}{tikzumlTrue}}{% + \umlassemblyconnectorsymbol{\tikzumlAssemblyConnectorName-4}{\tikzumlAssemblyConnectorEnd@nodeName}% + }{% + \umlassemblyconnectorsymbol{\tikzumlAssemblyConnectorName-2}{\tikzumlAssemblyConnectorName-4}% + }% + }% + }{% + \ifthenelse{\equal{\tikzumlAssemblyConnectorGeometry}{|-|}}{% + \ifthenelse{\equal{\tikzumlAssemblyConnectorFirstArm}{tikzumlTrue}}{% + \umlassemblyconnectorsymbol{\tikzumlAssemblyConnectorStart@nodeName}{\tikzumlAssemblyConnectorName-2}% + }{% + \ifthenelse{\equal{\tikzumlAssemblyConnectorLastArm}{tikzumlTrue}}{% + \umlassemblyconnectorsymbol{\tikzumlAssemblyConnectorName-4}{\tikzumlAssemblyConnectorEnd@nodeName}% + }{% + \umlassemblyconnectorsymbol{\tikzumlAssemblyConnectorName-2}{\tikzumlAssemblyConnectorName-4}% + }% + }% + }{}% + }% + }% + }% + }% + \stepcounter{tikzumlConnectorNum}% +}% +% +% shortcuts of \umlassemblyconnector +\newcommand{\umlHVassemblyconnector}[3][]{% + \pgfkeys{/tikzuml/HVassemblyconnector/.cd,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{geometry}}{% + \errmessage{TIKZUML ERROR : in umlHVassemblyconnector, forbidden option geometry}% + }{}% + }}% + \pgfkeys{/tikzuml/HVassemblyconnector/.cd, #1}% + \umlassemblyconnector[geometry=-|, #1]{#2}{#3}% +}% +% +\newcommand{\umlVHassemblyconnector}[3][]{% + \pgfkeys{/tikzuml/VHassemblyconnector/.cd,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{geometry}}{% + \errmessage{TIKZUML ERROR : in umlVHassemblyconnector, forbidden option geometry}% + }{}% + }% + }% + \pgfkeys{/tikzuml/VHassemblyconnector/.cd, #1}% + \umlassemblyconnector[geometry=|-, #1]{#2}{#3}% +}% +% +\newcommand{\umlHVHassemblyconnector}[3][]{% + \pgfkeys{/tikzuml/HVHassemblyconnector/.cd,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{geometry}}{% + \errmessage{TIKZUML ERROR : in umlHVHassemblyconnector, forbidden option geometry}% + }{}% + }}% + \pgfkeys{/tikzuml/HVHassemblyconnector/.cd, #1}% + \umlassemblyconnector[geometry=-|-, #1]{#2}{#3}% +}% +% +\newcommand{\umlVHVassemblyconnector}[3][]{% + \pgfkeys{/tikzuml/VHVassemblyconnector/.cd,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{geometry}}{% + \errmessage{TIKZUML ERROR : in umlVHVassemblyconnector, forbidden option geometry}% + }{}% + }}% + \pgfkeys{/tikzuml/VHVassemblyconnector/.cd, #1}% + \umlassemblyconnector[geometry=|-|, #1]{#2}{#3}% +}% +% +\newcommand{\umlport}[3][]{% + \pgfkeys{/tikzuml/port/.cd, draw/.initial=\tikzumlDefaultDrawColor, fill/.initial=\tikzumlPortDefaultFillColor,% + width/.initial=\tikzumlPortDefaultWidth,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \errmessage{TIKZUML ERROR : in umlport forbidden option \keyname}% + }% + }% + \pgfkeys{/tikzuml/port/.cd, #1}% + \pgfkeys{/tikzuml/port/.cd, width/.get=\tikzumlPortWidth,% + draw/.get=\tikzumlPortDrawColor, fill/.get=\tikzumlPortFillColor}% + \edef\tikzumlPort@name{#2}% + \edef\tikzumlPort@anchor{#3}% + \begingroup% + \def\_{_}\edef\x{\endgroup% + \def\noexpand\tikzumlPort@nodeName{\tikzumlPort@name}}\x% + % + \node[inner sep=0.5*\tikzumlPortWidth, rectangle, draw=\tikzumlPortDrawColor, fill=\tikzumlPortFillColor] (\tikzumlPort@nodeName-\tikzumlPort@anchor-port) at (\tikzumlPort@nodeName.\tikzumlPort@anchor) {};% +}% +% +\newcommand{\umldelegateconnector}[3][]{% + \def\tikzumlDelegateConnectorWithStartPort{tikzumlFalse}% + \def\tikzumlDelegateConnectorWithEndPort{tikzumlFalse}% + \pgfkeys{/tikzuml/delegateconnector/.cd, + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{stereo}}{% + \errmessage{TIKZUML ERROR : in umldelegateconnector, forbidden option stereo}% + }{}% + }}% + \pgfkeys{/tikzuml/delegateconnector/.cd, #1}% + \umlrelation[style={tikzuml connector style}, stereo=delegate, #1]{#2}{#3}% +}% +% +% shortcuts of \umldelegateconnector +\newcommand{\umlHVdelegateconnector}[3][]{% + \pgfkeys{/tikzuml/HVdelegateconnector/.cd,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{geometry}\OR\equal{\keyname}{stereo}}{% + \errmessage{TIKZUML ERROR : in umlHVdelegateconnector, forbidden option \keyname}% + }{}% + }}% + \pgfkeys{/tikzuml/HVdelegateconnector/.cd, #1}% + \umldelegateconnector[geometry=-|, #1]{#2}{#3}% +}% +% +\newcommand{\umlVHdelegateconnector}[3][]{% + \pgfkeys{/tikzuml/VHdelegateconnector/.cd,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{geometry}\OR\equal{\keyname}{stereo}}{% + \errmessage{TIKZUML ERROR : in umlVHdelegateconnector, forbidden option \keyname}% + }{}% + }}% + \pgfkeys{/tikzuml/VHdelegateconnector/.cd, #1}% + \umldelegateconnector[geometry=|-, #1]{#2}{#3}% +}% +% +\newcommand{\umlHVHdelegateconnector}[3][]{% + \pgfkeys{/tikzuml/HVHdelegateconnector/.cd,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{geometry}\OR\equal{\keyname}{stereo}}{% + \errmessage{TIKZUML ERROR : in umlHVHdelegateconnector, forbidden option \keyname}% + }{}% + }}% + \pgfkeys{/tikzuml/HVHdelegateconnector/.cd, #1}% + \umldelegateconnector[geometry=-|-, #1]{#2}{#3}% +}% +% +\newcommand{\umlVHVdelegateconnector}[3][]{% + \pgfkeys{/tikzuml/VHVdelegateconnector/.cd,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{geometry}\OR\equal{\keyname}{stereo}}{% + \errmessage{TIKZUML ERROR : in umlVHVdelegateconnector, forbidden option \keyname}% + }{}% + }}% + \pgfkeys{/tikzuml/VHVdelegateconnector/.cd, #1}% + \umldelegateconnector[geometry=|-|, #1]{#2}{#3}% +}% +%%% End of tikz-uml.sty +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \ No newline at end of file diff --git a/00-pflichtenheft/titlepage.tex b/00-pflichtenheft/titlepage.tex new file mode 100644 index 0000000..a1ddb42 --- /dev/null +++ b/00-pflichtenheft/titlepage.tex @@ -0,0 +1,75 @@ +%% Encoding: UTF-8 %% + +%% titlepage.tex + +\def\usesf{} +\let\usesf\sffamily % diese Zeile auskommentieren für normalen TeX Font + +\begin{titlepage} + +\setlength{\unitlength}{1pt} +\begin{picture}(00,0)(70,770) + \includegraphics[width=\paperwidth]{assets/KIT_Deckblatt.pdf} +\end{picture} + +\thispagestyle{empty} + +\begin{center} +\hbox{} +\vfill +\includegraphics[width=.5\textwidth]{assets/logo.pdf} +\vskip 1cm +{\usesf + {\huge\bfseries PSE\textsuperscript{2} - Podcast Synchronisation \\ + made Efficient\\ + Pflichtenheft \par} +\vskip 1.8cm +{\Large Wintersemester 2022/2023\\} +%von\\[2mm] +\vskip 1.5cm + +% {\large\bfseries Vorname Nachname\\} +% \vskip 1.2cm +Praxis der Softwareentwicklung \\ +Prof. Dr.-Ing. Gregor Snelting \\ +Fakultät für Informatik\\ +Karlsruher Institut für Technologie\\ +\vskip 1.5cm +\begin{tabular}{p{20mm}l} +Autoren: +& Daniel Hönlinger \\ +& Gero Beckmann \\ +& Immanuel Reitz \\ +& Julius Friesen \\ +& Lukas Schmidheissler \\ +\\ +Betreuer: & M.Sc. Hans-Peter Lehmann \\ + & M.Sc. Daniel Seemaier +\end{tabular} +} +\end{center} +\vfill + +%\begin{textblock}{10}[0,0](4,15) +% \includegraphics[width=.3\textwidth]{logos/logo.pdf} +%\end{textblock} + +% \begin{textblock}{8}[0,0](14,14) +% \includegraphics[width=.3\textwidth]{logos/KASTEL_logo.pdf} +% \end{textblock} + +\end{titlepage} + +% \thispagestyle{empty} +% \ \vfill +% \begin{flushleft} +% Copyright $\copyright$ ITI und Verfasser 201?\\ +% \ \\ +% Institut für Theoretische Informatik +% Fakultät für Informatik\\ +% Karlsruher Institut für Technologie\\ +% Am Fasanengarten 5\\ +% 76131 Karlsruhe +% \end{flushleft} +% \newpage + diff --git a/01-pflichtenheft-kolloquium/.gitignore b/01-pflichtenheft-kolloquium/.gitignore new file mode 100644 index 0000000..87ec682 --- /dev/null +++ b/01-pflichtenheft-kolloquium/.gitignore @@ -0,0 +1,302 @@ +## Core latex/pdflatex auxiliary files: +*.aux +*.lof +*.log +*.lot +*.fls +*.out +*.toc +*.fmt +*.fot +*.cb +*.cb2 +.*.lb + +## Intermediate documents: +*.dvi +*.xdv +*-converted-to.* +# these rules might exclude image files for figures etc. +# *.ps +# *.eps +*.pdf +!assets/*.pdf + +## Generated if empty string is given at "Please type another file name for output:" +.pdf + +## Bibliography auxiliary files (bibtex/biblatex/biber): +*.bbl +*.bcf +*.blg +*-blx.aux +*-blx.bib +*.run.xml + +## Build tool auxiliary files: +*.fdb_latexmk +*.synctex +*.synctex(busy) +*.synctex.gz +*.synctex.gz(busy) +*.pdfsync + +## Build tool directories for auxiliary files +# latexrun +latex.out/ + +## Auxiliary and intermediate files from other packages: +# algorithms +*.alg +*.loa + +# achemso +acs-*.bib + +# amsthm +*.thm + +# beamer +*.nav +*.pre +*.snm +*.vrb + +# changes +*.soc + +# comment +*.cut + +# cprotect +*.cpt + +# elsarticle (documentclass of Elsevier journals) +*.spl + +# endnotes +*.ent + +# fixme +*.lox + +# feynmf/feynmp +*.mf +*.mp +*.t[1-9] +*.t[1-9][0-9] +*.tfm + +#(r)(e)ledmac/(r)(e)ledpar +*.end +*.?end +*.[1-9] +*.[1-9][0-9] +*.[1-9][0-9][0-9] +*.[1-9]R +*.[1-9][0-9]R +*.[1-9][0-9][0-9]R +*.eledsec[1-9] +*.eledsec[1-9]R +*.eledsec[1-9][0-9] +*.eledsec[1-9][0-9]R +*.eledsec[1-9][0-9][0-9] +*.eledsec[1-9][0-9][0-9]R + +# glossaries +*.acn +*.acr +*.glg +*.glo +*.gls +*.glsdefs +*.lzo +*.lzs +*.slg +*.slo +*.sls + +# uncomment this for glossaries-extra (will ignore makeindex's style files!) +*.ist + +# gnuplot +*.gnuplot +*.table + +# gnuplottex +*-gnuplottex-* + +# gregoriotex +*.gaux +*.glog +*.gtex + +# htlatex +*.4ct +*.4tc +*.idv +*.lg +*.trc +*.xref + +# hyperref +*.brf + +# knitr +*-concordance.tex +# TODO Uncomment the next line if you use knitr and want to ignore its generated tikz files +# *.tikz +*-tikzDictionary + +# listings +*.lol + +# luatexja-ruby +*.ltjruby + +# makeidx +*.idx +*.ilg +*.ind + +# minitoc +*.maf +*.mlf +*.mlt +*.mtc[0-9]* +*.slf[0-9]* +*.slt[0-9]* +*.stc[0-9]* + +# minted +_minted* +*.pyg + +# morewrites +*.mw + +# newpax +*.newpax + +# nomencl +*.nlg +*.nlo +*.nls + +# pax +*.pax + +# pdfpcnotes +*.pdfpc + +# sagetex +*.sagetex.sage +*.sagetex.py +*.sagetex.scmd + +# scrwfile +*.wrt + +# svg +svg-inkscape/ + +# sympy +*.sout +*.sympy +sympy-plots-for-*.tex/ + +# pdfcomment +*.upa +*.upb + +# pythontex +*.pytxcode +pythontex-files-*/ + +# tcolorbox +*.listing + +# thmtools +*.loe + +# TikZ & PGF +*.dpth +*.md5 +*.auxlock + +# titletoc +*.ptc + +# todonotes +*.tdo + +# vhistory +*.hst +*.ver + +# easy-todo +*.lod + +# xcolor +*.xcp + +# xmpincl +*.xmpi + +# xindy +*.xdy + +# xypic precompiled matrices and outlines +*.xyc +*.xyd + +# endfloat +*.ttt +*.fff + +# Latexian +TSWLatexianTemp* + +## Editors: +# WinEdt +*.bak +*.sav + +# Texpad +.texpadtmp + +# LyX +*.lyx~ + +# Kile +*.backup + +# gummi +.*.swp + +# KBibTeX +*~[0-9]* + +# TeXnicCenter +*.tps + +# auto folder when using emacs and auctex +./auto/* +*.el + +# expex forward references with \gathertags +*-tags.tex + +# standalone packages +*.sta + +# Makeindex log files +*.lpz + +# xwatermark package +*.xwm + +# REVTeX puts footnotes in the bibliography by default, unless the nofootinbib +# option is specified. Footnotes are the stored in a file with suffix Notes.bib. +# Uncomment the next line to have this generated file ignored. +#*Notes.bib diff --git a/01-pflichtenheft-kolloquium/.gitlab-ci.yml b/01-pflichtenheft-kolloquium/.gitlab-ci.yml new file mode 100644 index 0000000..427b043 --- /dev/null +++ b/01-pflichtenheft-kolloquium/.gitlab-ci.yml @@ -0,0 +1,15 @@ +image: texlive/texlive + +pages: + script: + - mkdir public + - make + - mv *.pdf public + artifacts: + paths: + - public + rules: + - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH + + + diff --git a/01-pflichtenheft-kolloquium/CHANGELOG.md b/01-pflichtenheft-kolloquium/CHANGELOG.md new file mode 100644 index 0000000..7671a41 --- /dev/null +++ b/01-pflichtenheft-kolloquium/CHANGELOG.md @@ -0,0 +1,34 @@ +# Changelog +Alle Änderungen an diesem Projekt werden in dieser Datei dokumentiert. +Die Versionsnummern folgt der Syntax in `sdqbeamer.cls`. + +## [2022-05-03 v3.1.3] +- Die Breite des Gruppennamens in der Fußzeile kann nun über `\groupnamewidth{}` gesteuert werden +- FIX: zweizeilige Fußzeilen haben nun gleichmäßigen vertikalen Abstand (Issue #16 in Gitlab) + +## [2021-08-10 v3.1.2] +- FIX: framesubtitle wird nun angezeigt (Issue #6 in Gitlab) + +## [2020-12-08 v3.1.1] +- FIX: Titelbild (Issue #4 in Gitlab) + +## [2020-12-07 v3.1] +- Umgebung ``contentblock`` (farbloser Block mit fetter Überschrift) hinzugefügt +- Farbboxen (``greenblock``, ``blueblock``, …) hinzugefügt +- Abstufungen der KIT-Farben in 10er-Schritten entsprechend der Gestaltungsrichtlinien eingeführt +- FIX: Navigationspunkte für Subsections in eine Zeile gesetzt, um vertikal Platz zu sparen +- FIX: ``inputenc`` an den Anfang von ``sdqbeamer.cls`` verschoben + +## [2020-11-16 v3.0] +- Seitenformat 16:10 hinzugefügt +- Umstellung auf KIT-Design vom 1. August 2020 + - Anpassung auf neues Farbschema und Maße + - neues Titelbild aus der KIT-Bildwelt +- Neue Optionen: + - durch `smallfoot` und `bigfoot` kann die Schriftgröße der Fußzeile gesteuert werden + - durch `navbarkit` kann eine Fußzeile nach KIT-Vorgaben erzwungen werden +- Deutsch (`de`) ist nun die Standard-Option +- Ordner `templates` wurde gelöscht und die Inhalte in `sdqbeamer.cls` integriert +- Globale Größe auf 10 pt verringert (vorher: 11 pt), da der beschreibbare Bereich im Vergleich zur 2009er Version kleiner geworden ist +- SDQ-spezifische Logos und Titelbilder entfernt. Diese sind ab sofort im Branch »sdq« verfügbar. +- Fix: Zeilenumbruch bei Titel in der Fußzeile repariert \ No newline at end of file diff --git a/01-pflichtenheft-kolloquium/Makefile b/01-pflichtenheft-kolloquium/Makefile new file mode 100644 index 0000000..65ba963 --- /dev/null +++ b/01-pflichtenheft-kolloquium/Makefile @@ -0,0 +1,10 @@ +MAIN = presentation.tex +FLAGS = -pdf + +all: + latexmk $(FLAGS) $(MAIN) +dev: + latexmk $(FLAGS) -pvc $(MAIN) +clean: + latexmk -C + diff --git a/01-pflichtenheft-kolloquium/README.md b/01-pflichtenheft-kolloquium/README.md new file mode 100644 index 0000000..6a5e9ea --- /dev/null +++ b/01-pflichtenheft-kolloquium/README.md @@ -0,0 +1,118 @@ +LaTeX-Vorlage für Präsentationen +================================ + +Das vorliegende Paket dient als Vorlage für Präsentationen im [Corporate Design des KIT](https://intranet.kit.edu/gestaltungsrichtlinien.php) (Fassung vom 1. August 2020). + +Es wird an der Forschungsgruppe [DSiS](https://dsis.kastel.kit.edu) an der KIT-Fakultät für Informatik entwickelt und basiert auf [LaTeX Beamer](https://ctan.org/pkg/beamer). + +Autor: [Dr.-Ing. Erik Burger](https://dsis.kastel.kit.edu/staff_erik_burger.php) +mit Beiträgen von Christian Hammer, Klaus Krogmann und Maximilian Schambach + +Siehe https://sdqweb.ipd.kit.edu/wiki/Dokumentvorlagen + +Hinweise, Verbesserungsvorschläge +================================= + +Bitte verwenden Sie das [Issue-Tracking-System von Gitlab](https://git.scc.kit.edu/i43/dokumentvorlagen/praesentationen/beamer/-/issues), um auf Probleme mit der Vorlage hinzuweisen oder Erweiterungswünsche zu äußern. Sie können gerne auch eine Änderung per Merge-Request vorschlagen. + +Verwendung +========== + +Optionen der Dokumentklasse `sdqbeamer` +----------------------------------------- +Durch die folgenden Optionen kann das Seitenverhältnis der Folien bestimmt werden: + +| Seitenverhältnis | Option | +| ---------------- | ------------------- | +| 16:9 | `16:9` (Standard) | +| 16:10 | `16:10` | +| 4:3 | `4:3` | + +Die Schriftgröße in der Fußzeile ist standardmäßig größer gewählt, als in den Gestaltungsrichtlinien vorgegeben. Diese Vorgabe kann durch die Option `smallfoot` erzwungen werden. + +| Schriftgröße Fußzeile | Option | +| ----------------------| -------------------- | +| etwas größer (12pt) | `bigfoot` (Standard) | +| KIT-Vorgabe (9pt) | `smallfoot` | + +Die Plazierung der Navigationsleiste kann durch folgende Optionen beeinflußt werden: + +| Position | Option | Bemerkung | +| ------------------------ | ---------------- | ------------------------------------------ | +| oberhalb der Trennlinie | `navbarinline` | Standard | +| unterhalb der Trennlinie | `navbarinfooter` | keine Subsection-Punkte, Größe `smallfoot` | +| Seitenleiste links | `navbarside` | keine Subsection-Punkte | +| keine Navigationsleiste | `navbaroff` | | +| KIT-Vorgabe | `navbarkit` | entspricht `navbaroff` und `smallfoot` | + +Als Sprache sind Deutsch und Englisch verfügbar. Durch die Sprachwahl werden automatisch die passenden Logos und Formate (z.B. Datum) gewählt. + +| Sprache | | +| -------- |---------------- | +| Deutsch | `de` (Standard) | +| Englisch | `en` | + +Beispiel: `\documentclass[de,16:9,navbarinline]{sdqbeamer}` + +Titelbild +--------- + +Das Bild auf der Titelfolie kann mit dem Befehl + +`\titleimage{myimage}` (ohne Dateiendung) + +gesetzt werden. Um ein eigenes Bild zu verwenden, bitte die Datei (z.B. `myimage.jpg`) ins `logos/`-Verzeichnis legen und den Befehl anpassen. Mitgeliefert wird ein generisches Bild aus der KIT-Bildwelt (https://intranet.kit.edu/gestaltungsrichtlinien.php) in der Datei `logos/banner_2020_kit.jpg`. Falls kein Titelbild eingefügt werden soll, bitte `\titleimage{}` setzen. + +Für 16:9-Folien sollte das Verhältnis des Bildes 160:37 betragen, für 4:3-Folien 63:20. Es können auch breitere Bilder verwendet werden, da das Titelbild auf die Höhe des Rahmens skaliert und zentriert wird. + +Logo und Name Abteilung/KIT-Fakultät/Institut +--------------------------------------------- + +Das Logo rechts oben auf der Titelfolie kann mit dem folgenden Befehl gesetzt werden: + +`\grouplogo{mylogo}` (ohne Dateiendung) + +Um ein eigenes Logo zu verwenden, bitte die Datei (z.B. `mylogo.pdf`) in das Verzeichnis `logos/` legen und den Befehl anpassen. Falls kein Logo eingefügt werden soll, bitte `\grouplogo{}` setzen. + +Der Gruppenname kann mit folgendem Befehl gesetzt werden: + +`\groupname{Software Design and Quality}` + +Der Gruppenname erscheint in der Fußzeile rechts unten. Lange Namen werden in zwei Zeilen umgebrochen. Falls der Gruppenname leer gelassen wird (`\groupname{}`), wird die volle Breite der Fußzeile für Autornamen und Titel verwendet. + +Die Standardbreite des Gruppennamens sind 50 mm. Sie kann mit + +`\groupnamewidth{80mm}` + +verändert werden, wodurch sich auch die Breite des Textfeldes mit Autor und Titel entsprechend ändert. Umbrüche sind mit `\\` möglich. Statt zweizeiliger Fußzeilen empfiehlt sich eventuell die Option `smallfoot`. + +LaTeX allgemein +--------------- +Siehe https://sdqweb.ipd.kit.edu/wiki/LaTeX + +Dateistruktur +============ +`presentation.tex` +------------------ +Hauptdatei des LaTeX-Dokuments. + +`presentation.bib` +------------- +Beispieldatei für BibTeX-Referenzen +https://sdqweb.ipd.kit.edu/wiki/BibTeX-Literaturlisten + +`sdqbeamer.cls` +----------------- +Dokumentklasse für Präsentationen im KIT-Design. + +`logos/` +-------- +In diesem Verzeichnis befinden das KIT-Logo als PDF sowie das Hintergrundbild der Titelfolie als JPG. + +`CHANGELOG.md` +-------------- +Dokumentation der Änderungen in den jeweiligen Versionen. + +`README.md` +----------- +Dieser Text. diff --git a/01-pflichtenheft-kolloquium/logos/banner_2020_kit.jpg b/01-pflichtenheft-kolloquium/logos/banner_2020_kit.jpg new file mode 100644 index 0000000..70ae1d0 Binary files /dev/null and b/01-pflichtenheft-kolloquium/logos/banner_2020_kit.jpg differ diff --git a/01-pflichtenheft-kolloquium/notizen b/01-pflichtenheft-kolloquium/notizen new file mode 100644 index 0000000..70ac63b --- /dev/null +++ b/01-pflichtenheft-kolloquium/notizen @@ -0,0 +1,40 @@ +Titelseite: +- Begrüßung + +Einführung: +- Podcast: RSS-Feed, Episoden, Audio/Video +- Podcatcher: lokale Verwaltung von Podcasts, API Unterstützung, +Abspielen von Episoden +- Synchronisationsserver (das soll unser Produkt werden): Hörfortschritte, +Abonnements, Discovery (bei AntennaPod z.B. iTunes) + +Zielsetzung: + +Synchronisation (die Art, die wir bei uns anwenden): +- alle Aktionen werden auf den Server und infolge dessen auf alle Podcatcher +übertragen + +Features: +- Synchronisation: Abos, Hörfortschritt +- Weboberfläche: Aboliste, Zuletzt gehört +- Account-Verwaltung: Registrieren, Anmelden, Passwort ändern/zurücksetzen, +Account löschen, Daten importieren/exportieren + +UI-Journey: +- login.html: + - Sprache ändern + - OAuth (kann) + - Registrieren (muss): neues Fenster, E-Mail + 2-mal Passwort (sicher?), + vergeben? -> Fehler / Bestätigungslink per E-Mail (gültig 24h) + - Anmelden (muss): E-Mail + Passwort eingeben, Login merken, + login, Fehlermeldung oder Dashboard + - Passwort vergessen: neues Fenster, E-Mail eingeben, falls Account wird + Link versendet, 24h gültig, nach betätigen PW zweimal eingeben & bestätigen + (Anforderungen) + +- podcasts.html: Abonnements, Eisoden, Hörfortschritte + +- listening.html: Zuletzt gehörte Episoden, Hörfortschritt, Wann gehört + +- settings.html: PW ändern, Gpodder Import, pers. Daten imp./exp., + Account löschen diff --git a/01-pflichtenheft-kolloquium/presentation.tex b/01-pflichtenheft-kolloquium/presentation.tex new file mode 100644 index 0000000..2f65a95 --- /dev/null +++ b/01-pflichtenheft-kolloquium/presentation.tex @@ -0,0 +1,60 @@ +%% Beispiel-Präsentation mit LaTeX Beamer im KIT-Design +%% entsprechend den Gestaltungsrichtlinien vom 1. August 2020 +%% +%% Siehe https://sdqweb.ipd.kit.edu/wiki/Dokumentvorlagen + +%% Beispiel-Präsentation +\documentclass{sdqbeamer} + +\usepackage{tikz} +\usetikzlibrary{positioning} +\usetikzlibrary{calc} +\usetikzlibrary{arrows} +\usepackage{multicol} +\usepackage{fontawesome5} +\usepackage{tikz} +\usepackage{tikz-uml} +\usetikzlibrary{positioning, arrows, chains} +\usetikzlibrary[calc] + + +%% Titelbild +\titleimage{banner_2020_kit} + +%% Gruppenlogo +\grouplogo{kitlogo_de_rgb} + +%% Gruppenname und Breite (Standard: 50 mm) +\groupname{Praxis der Softwareentwicklung} +%\groupnamewidth{50mm} + +% Beginn der Präsentation + +\title[Kolloquium Pflichtenheft]{ + PSE\textsuperscript{2} - Podcast Synchronisation made Efficient +} +\subtitle{Kolloquium Pflichtenheft} +\author[Julius Friesen]{Julius Friesen} + +\date[06.\,12.\,2022]{06. Dezember 2022} + +\begin{document} + +%Titelseite +\KITtitleframe + +% Folie Einführung +\include{slides/einführung} + +% Folie Zielsetzung +\include{slides/zielsetzung} + +% TODO: Folie Synchronisation +\include{slides/synchronisation} + +% TODO: Folie Features +\include{slides/features} + +% TODO: User-Journey + +\end{document} diff --git a/01-pflichtenheft-kolloquium/sdqbeamer.cls b/01-pflichtenheft-kolloquium/sdqbeamer.cls new file mode 100644 index 0000000..8f0433b --- /dev/null +++ b/01-pflichtenheft-kolloquium/sdqbeamer.cls @@ -0,0 +1,954 @@ +%% Vorlage für Präsentationen mit LaTeX Beamer im KIT-Design +%% entsprechend den Gestaltungsrichtlinien vom 1. August 2020 +%% +%% Siehe https://sdqweb.ipd.kit.edu/wiki/Dokumentvorlagen + + +\NeedsTeXFormat{LaTeX2e} +\ProvidesClass{sdqbeamer}[2022-05-03 v3.1.3 SDQ Beamer class] + +\RequirePackage[utf8]{inputenc} +\RequirePackage[T1]{fontenc} + +\newif\ifsectionnavigation +\newif\ifnavbarinfoot +\newif\ifnavbarinline +\newif\ifnavbarside +\newif\iffourtothree +\newif\ifsixteentonine +\newif\ifsixteentoten +\newif\ifgerman +\newif\ifsmallfooterfont + +\def\kitslogan#1{\def\@kitslogan{#1}} +\def\kitlogo#1{\def\@kitlogo{#1}} +\def\groupname#1{\def\@groupname{#1}} +\def\groupnamewidth#1{\def\@groupnamewidth{#1}} + +% siehe README.md +\DeclareOption{de}{ + \kitslogan{KIT -- Die Forschungsuniversität in der Helmholtz-Gemeinschaft} + \kitlogo{pse_logo} + \germantrue + \PassOptionsToPackage{autostyle}{csquotes} + } +\DeclareOption{en}{ + \kitslogan{KIT -- The Research University in the Helmholtz Association} + \kitlogo{kitlogo_en_rgb} + \germanfalse + } +\DeclareOption{4:3}{ + \fourtothreetrue + \sixteentoninefalse + \sixteentotenfalse +} +\DeclareOption{16:9}{ + \fourtothreefalse + \sixteentoninetrue + \sixteentotenfalse +} +\DeclareOption{16:10}{ + \fourtothreefalse + \sixteentoninefalse + \sixteentotentrue +} +\DeclareOption{navbarside}{ + \sectionnavigationtrue + \navbarsidetrue + \navbarinlinefalse + \navbarinfootfalse +} +\DeclareOption{navbarinline}{ + \sectionnavigationtrue + \navbarsidefalse + \navbarinlinetrue + \navbarinfootfalse +} +\DeclareOption{navbarinfooter}{ + \sectionnavigationtrue + \navbarsidefalse + \navbarinlinefalse + \navbarinfoottrue +} +\DeclareOption{navbaroff}{ + \sectionnavigationfalse +} +\DeclareOption{navbarkit}{ + \sectionnavigationfalse + \smallfooterfonttrue +} +\DeclareOption{smallfoot}{ + \smallfooterfonttrue +} +\DeclareOption{bigfoot}{ + \smallfooterfontfalse +} + +\ExecuteOptions{de,16:9,navbarinline,bigfoot} + +\DeclareOption*{\PassOptionsToClass{\CurrentOption}{beamer}} + +\ProcessOptions\relax + +\LoadClass[10pt,utf8]{beamer} + +% Babel-Paket wird nur bei deutscher Sprache benötigt +\ifgerman + \RequirePackage[ngerman]{babel} +\fi +\RequirePackage{csquotes} +\RequirePackage{hyperref} +\RequirePackage[absolute,overlay]{textpos} + +%% --------------- +%% | Typographie | +%% --------------- + +\RequirePackage{microtype} + +\RequirePackage[scaled=.92]{helvet} +\RequirePackage[scaled=.78]{beramono} +\RequirePackage{libertineRoman} + +\setbeamerfont{title}{series=\bfseries,size=\Large} +\setbeamerfont{frametitle}{series=\bfseries,size=\Large} +\setbeamerfont{framesubtitle}{series=\bfseries,size=\normalsize} + +%% ----------------- +%% | Folien-Layout | +%% ----------------- + +% Seitenverhältnis +% +% Die Folien sind auf die Standardhöhe in LaTeX Beamer (9,6 cm) normiert. +% Die Maße der KIT-Gestaltungsrichtlinien (Folienhöhe 14,3 cm) wurden durch +% den Quotienten 1,5 geteilt. + +\RequirePackage{geometry} +\iffourtothree + \geometry{papersize={12.8cm,9.6cm}} +\fi +\ifsixteentoten + \geometry{papersize={15.36cm,9.6cm}} +\fi +\ifsixteentonine + \geometry{papersize={17.07cm,9.6cm}} +\fi + +% Ränder laut Gestaltungsrichtlinen; 3 mm -> 2 mm, 11 mm -> 7,3 mm +\newlength{\kitoutermargin} +\setlength{\kitoutermargin}{2mm} +\newlength{\kitinnermargin} +\setlength{\kitinnermargin}{7.3mm} +\newlength{\kitbottommargin} +\setlength{\kitbottommargin}{\kitinnermargin} + +% Ränder außen +\setbeamersize{text margin left=\kitinnermargin,text margin right=\kitinnermargin} + +% keine Navigationssymbole +\setbeamertemplate{navigation symbols}{} +\setbeamercovered{invisible} +\useinnertheme{rounded} +\beamer@compresstrue % Miniframes (Navigations-Punkte) für Subsections immer in einer Zeile, ohne Umbrüche + +% Folientitel +\setbeamertemplate{frametitle}{ + \ifbeamer@plainframe\else% + % Unterkante Titeltext: 22,5 mm -> 15 mm von Seitenkopf + \begin{textblock*}{\dimexpr\paperwidth-30mm-2\kitinnermargin}[0,1](\kitinnermargin,15mm)% + \usebeamerfont{frametitle}\insertframetitle% + \ifx\insertframesubtitle\@empty\else\\[.1em]\fi + \usebeamerfont{framesubtitle}\insertframesubtitle% + \end{textblock*}% + \begin{textblock*}{20mm}[1,1](\dimexpr\paperwidth-\kitinnermargin\relax,15mm)%j + \includegraphics[width=20mm]{logos/\@kitlogo}% + \end{textblock*}% + \fi +% Rand oben (ergibt Beginn des Textes bei 34 mm -> 22,7 mm) + \vspace {18mm} +} + +%% Fußzeile +\newlength{\kitbottom} + % Navbar in Footer: Schmale Fußzeile +\ifnavbarinfoot + % Bei Navbar in Footer immer kleiner Font in der Fußzeile + \setbeamerfont{footer}{size=\fontsize{6pt}{7.2pt}\selectfont} + \setlength{\kitbottom}{4mm} +\else + % Ansonsten kleiner Text nur, wenn "smallfoot" gewählt + \ifsmallfooterfont + \setbeamerfont{footer}{size=\fontsize{6pt}{7.2pt}\selectfont} + \else + \setbeamerfont{footer}{size=\scriptsize} + \fi + \setlength{\kitbottom}{\kitbottommargin} +\fi +\setbeamerfont{page number in head/foot}{series=\bfseries} + +\newlength{\kitbottomdepth} +\newlength{\kitbottomheight} +\newlength{\kitfootergroupwidth} + +\setbeamertemplate{footline}{% +\setlength{\kitbottomdepth}{\dimexpr.5\kitbottom-.5em\relax}% +\setlength{\kitbottomheight}{\dimexpr.5\kitbottom+.5em\relax}% + %% die "%" am Ende sind nötig, damit keine Abstände eingefügt werden + % + % Falls kein Gruppenname angegeben, die ganze Breite der Fußzeile für den Titel nutzen. + \ifdefined\@groupname% + \ifx\@groupname\empty% + \setlength{\kitfootergroupwidth}{0mm}% + \else% + % Falls die Breite des Gruppenlogos definiert ist, diese nehmen, sonst 50 mm + \ifdefined\@groupnamewidth% + \setlength{\kitfootergroupwidth}{\@groupnamewidth}% + \else% + \setlength{\kitfootergroupwidth}{50mm}% + \fi% + \fi% + \else + \setlength{\kitfootergroupwidth}{0mm}% + \fi + \usebeamerfont{footer}% + \ifsectionnavigation% + % Option "navbarinline" + \ifnavbarinline% + \begin{beamercolorbox}[wd=\paperwidth, leftskip=2mm, rightskip=2mm]{} + \insertnavigation{\dimexpr\paperwidth-4mm\relax} + \vspace{1mm} + \end{beamercolorbox}% + \fi% + % Option "navbarinfooter" + \ifnavbarinfoot% + % Punkte für Subsections deaktivieren + \setbeamertemplate{mini frames}{}% + \begin{beamercolorbox}[wd=\paperwidth, leftskip=1mm, rightskip=1mm]{}% + \insertsectionnavigationhorizontal{\dimexpr\paperwidth-\kitoutermargin\relax}{}{} + \end{beamercolorbox}% + \fi% + \fi% + \leavevmode% + \begin{beamercolorbox}[wd=13mm, ht=\kitbottomheight, dp=\kitbottomdepth, leftskip=4mm]{} + \usebeamerfont{page number in head/foot}% + \strut\insertframenumber{}/\inserttotalframenumber% + \end{beamercolorbox}% + \begin{beamercolorbox}[wd=20mm, ht=\kitbottomheight, dp=\kitbottomdepth]{} + \usebeamerfont{date in head/foot}% + \strut\insertshortdate% + \end{beamercolorbox}% + % Die Boxen mit dem Titel und dem Gruppennamen sind vertikal zentriert, damit auch zweizeilige Texte schön aussehen + % Daher müssen sie um \kitbottomdepth nach unten verschoben werden + \raisebox{-\kitbottomdepth}{ + % Die Box hat daher auch Höhe \kitbottom und Tiefe 0mm + \begin{beamercolorbox}[wd=\dimexpr\paperwidth-37mm-\kitfootergroupwidth, ht=\kitbottom, dp=0mm]{}% + % Inhalt vertikal zentrieren; Anpassung um 1.5 pt, damit bei einzeiligem Inhalt genau die Baseline der Blöcke mit Seitenzahl und Datum getroffen wird + \vbox to\kitbottom{\vfill\vskip1.5pt% + \beamer@shortauthor\ifx\beamer@shortauthor\empty\else: \fi\beamer@shorttitle% + \vfill}% + \end{beamercolorbox}% + \ifdefined\@groupname% + \begin{beamercolorbox}[wd=\kitfootergroupwidth, ht=\kitbottom, dp=0mm, rightskip=\kitinnermargin]{} + \vbox to\kitbottom{\vfill\vskip1.5pt% + \raggedleft\@groupname% + \vfill}% + \end{beamercolorbox}% + \fi% + }% +} + +%% Option "navbarside" +\ifnavbarside +\useoutertheme[height=0cm,width=3.5cm,left]{sidebar} + +\setbeamerfont{title in sidebar}{family=\sffamily,series=\mdseries,size={\fontsize{10pt}{11pt}}} +\setbeamerfont{section in sidebar}{family=\sffamily,series=\mdseries,size={\fontsize{9pt}{9.9pt}}} +\setbeamerfont{subsection in sidebar}{family=\sffamily,series=\mdseries,size={\fontsize{8pt}{8.8pt}}} + +\setbeamertemplate{sidebar \beamer@sidebarside} + {\vskip1.5cm% + \hskip6.5mm% + \advance\beamer@sidebarwidth by -5mm% + \insertverticalnavigation{\beamer@sidebarwidth}% + }% +\fi + +%% Hintergrund +\usebackgroundtemplate{ + % Trennlinie nicht bei "plain"-Frames + \ifbeamer@plainframe\else\kitseparationline\fi +} + +% Trennlinie +\newcommand{\kitseparationline}{ + \begin{pgfpicture}{0mm}{0mm}{\paperwidth}{\paperheight} + \pgfsetstrokecolor{black!15} + \pgfsetlinewidth{.5pt} + \pgfpathmoveto{\pgfpoint{\kitoutermargin}{\kitinnermargin}} + \pgfpathlineto{\pgfpoint{\paperwidth-\kitoutermargin}{\kitinnermargin}} + \pgfusepath{stroke} + \end{pgfpicture}% +} + + +%% -------------- +%% | Titelseite | +%% -------------- + +\def\titleimage#1{\def\@titleimage{#1}} +\def\grouplogo#1{\def\@grouplogo{#1}} + +\newcommand{\KITtitleframe}{ + \begin{frame}[plain] + \titlepage + \end{frame} +} + +\newlength{\kittitleimageheight} +\setbeamertemplate{title page}{ + % From textpos documentation (https://ctan.org/pkg/textpos) + % + % \begin{textblock}{}[,](,) + % The coordinates and are fractions of the width and height of the text + % box, respectively, and state that the box is to be placed so that the reference point + % (,) within the box is to be placed at the point (,) on the page. + + % KIT-Logo + \begin{textblock*}{30mm}(\kitinnermargin,6.7mm) + \includegraphics[width=30mm]{logos/\@kitlogo} + \end{textblock*} + + % Gruppenlogo + \ifdefined\@grouplogo + \ifx\@grouplogo\empty \else + \begin{textblock*}{20mm}(\dimexpr\paperwidth-24mm\relax,6.7mm) + \includegraphics[width=20mm,height=20mm,keepaspectratio]{logos/\@grouplogo} + \end{textblock*} + \fi % falls \grouplogo{} aufgerufen wird, kein Gruppenlogo einbinden + \else + \begin{textblock*}{20mm}(\dimexpr\paperwidth-24mm\relax,6.7mm) + \colorbox{kit-purple100!20}{\parbox[t][12mm][c]{19mm}{\color{kit-purple100}\scriptsize\centering + \ifgerman + Bitte Logo über \texttt{\textbackslash grouplogo\{\}} festlegen. + \else + Please set a logo using \texttt{\textbackslash grouplogo\{\}}. + \fi + }} + \end{textblock*} + \fi + + % Titel + \begin{textblock*}{\dimexpr\paperwidth-8mm\relax}[0,.5](\kitinnermargin,28mm) + \usebeamerfont*{title}\inserttitle + \end{textblock*} + + % Untertitel + \begin{textblock*}{\dimexpr\paperwidth-8mm\relax}(\kitinnermargin,36mm) + \small\textbf{\insertsubtitle} + \end{textblock*} + + % Autor + \begin{textblock*}{\dimexpr\paperwidth-8mm\relax}(\kitinnermargin,41mm) + \small\insertauthor~\textbar~\insertdate + \end{textblock*} + + % Titelbild + \setlength{\kittitleimageheight}{40mm} + \begin{textblock*}{\paperwidth}(\kitoutermargin,\dimexpr\paperheight-\kitbottommargin-\kittitleimageheight) + \begin{pgfpicture}{0mm}{0mm}{\paperwidth}{\kittitleimageheight} + % Clipping-Pfad um titelbild + \pgfsetstrokecolor{black!15} + \pgfsetlinewidth{1pt} + \pgfsetcornersarced{\pgfpoint{3mm}{3mm}} + \pgfpathmoveto{\pgfpoint{\paperwidth-2\kitoutermargin}{0mm}} + \pgfpathlineto{\pgfpoint{\paperwidth-2\kitoutermargin}{\kittitleimageheight}} + \pgfsetcornersarced{\pgfpointorigin} + \pgfpathlineto{\pgfpoint{0mm}{\kittitleimageheight}} + \pgfsetcornersarced{\pgfpoint{3mm}{3mm}} + \pgfpathlineto{\pgfpointorigin} + \pgfsetcornersarced{\pgfpointorigin} + \pgfpathclose + \pgfusepath{stroke,clip} + \pgfsetstrokecolor{black} + + % Titelbild + \ifdefined\@titleimage + \ifx\@titleimage\empty \else% + \pgftext[at=\pgfpoint{.5\paperwidth}{0mm},center,bottom]{% + \includegraphics[height=40mm]{logos/\@titleimage} + } + \fi % Bei Aufruf von \titleimage{} leeren Rahmen anzeigen. + \else + \pgftext[at=\pgfpoint{.5\paperwidth}{.5\kittitleimageheight},center,base]{% + \colorbox{kit-purple100!20}{\parbox[c][\kittitleimageheight][c]{\paperwidth}{\color{kit-purple100}\centering Bitte Titelbild über \texttt{\textbackslash titleimage\{\}} festlegen. + }}% + } + \fi + + + \end{pgfpicture}% + \end{textblock*} + + + % KIT slogan + \begin{textblock*}{80mm}[0,.5](\kitoutermargin,\dimexpr\paperheight-.5\kitbottommargin) + \fontsize{5.5pt}{5.5pt}\selectfont\@kitslogan + \end{textblock*} + + \begin{textblock*}{30mm}[1,.5](\dimexpr\paperwidth-\kitoutermargin\relax,\dimexpr\paperheight-.5\kitbottommargin) + \fontsize{11pt}{11pt}\selectfont\bfseries\raggedleft% + {\href{https://www.kit.edu}{www.kit.edu}} + \end{textblock*} + +} +%% --------------- +%% | /Titelseite | +%% --------------- + +%% ---------- +%% | Farben | +%% ---------- +%% KIT-Farbschema + +% KIT color green : +\definecolor{kit-green}{RGB}{0, 150, 130} +\definecolor{kit-green100}{RGB}{0, 150, 130} +\definecolor{kit-green90}{rgb}{0.1, 0.6294, 0.5588} +\definecolor{kit-green80}{rgb}{0.2, 0.6706, 0.6078} +\definecolor{kit-green75}{rgb}{0.25, 0.6912, 0.6324} +\definecolor{kit-green70}{rgb}{0.3, 0.7118, 0.6569} +\definecolor{kit-green60}{rgb}{0.4, 0.7529, 0.7059} +\definecolor{kit-green50}{rgb}{0.5, 0.7941, 0.7549} +\definecolor{kit-green40}{rgb}{0.6, 0.8353, 0.8039} +\definecolor{kit-green30}{rgb}{0.7, 0.8765, 0.8529} +\definecolor{kit-green25}{rgb}{0.75, 0.8971, 0.8775} +\definecolor{kit-green20}{rgb}{0.8, 0.9176, 0.902} +\definecolor{kit-green15}{rgb}{0.85, 0.9382, 0.9265} +\definecolor{kit-green10}{rgb}{0.9, 0.9588, 0.951} +\definecolor{kit-green5}{rgb}{0.95, 0.9794, 0.9755} + +% KIT color blue: +\definecolor{kit-blue}{RGB}{70, 100, 170} +\definecolor{kit-blue100}{RGB}{70, 100, 170} +\definecolor{kit-blue90}{rgb}{0.3471, 0.4529, 0.7} +\definecolor{kit-blue80}{rgb}{0.4196, 0.5137, 0.7333} +\definecolor{kit-blue75}{rgb}{0.4559, 0.5441, 0.75} +\definecolor{kit-blue70}{rgb}{0.4922, 0.5745, 0.7667} +\definecolor{kit-blue60}{rgb}{0.5647, 0.6353, 0.8} +\definecolor{kit-blue50}{rgb}{0.6373, 0.6961, 0.8333} +\definecolor{kit-blue40}{rgb}{0.7098, 0.7569, 0.8667} +\definecolor{kit-blue30}{rgb}{0.7824, 0.8176, 0.9} +\definecolor{kit-blue25}{rgb}{0.8186, 0.848, 0.9167} +\definecolor{kit-blue20}{rgb}{0.8549, 0.8784, 0.9333} +\definecolor{kit-blue15}{rgb}{0.8912, 0.9088, 0.95} +\definecolor{kit-blue10}{rgb}{0.9275, 0.9392, 0.9667} +\definecolor{kit-blue5}{rgb}{0.9637, 0.9696, 0.9833} + +% KIT color red : +\definecolor{kit-red}{RGB}{162, 34, 35} +\definecolor{kit-red100}{RGB}{162, 34, 35} +\definecolor{kit-red90}{rgb}{0.6718, 0.22, 0.2235} +\definecolor{kit-red80}{rgb}{0.7082, 0.3067, 0.3098} +\definecolor{kit-red75}{rgb}{0.7265, 0.35, 0.3529} +\definecolor{kit-red70}{rgb}{0.7447, 0.3933, 0.3961} +\definecolor{kit-red60}{rgb}{0.7812, 0.48, 0.4824} +\definecolor{kit-red50}{rgb}{0.8176, 0.5667, 0.5686} +\definecolor{kit-red40}{rgb}{0.8541, 0.6533, 0.6549} +\definecolor{kit-red30}{rgb}{0.8906, 0.74, 0.7412} +\definecolor{kit-red25}{rgb}{0.9088, 0.7833, 0.7843} +\definecolor{kit-red20}{rgb}{0.9271, 0.8267, 0.8275} +\definecolor{kit-red15}{rgb}{0.9453, 0.87, 0.8706} +\definecolor{kit-red10}{rgb}{0.9635, 0.9133, 0.9137} +\definecolor{kit-red5}{rgb}{0.9818, 0.9567, 0.9569} + +% KIT color yellow : +\definecolor{kit-yellow}{RGB}{252, 229, 0} +\definecolor{kit-yellow100}{RGB}{252, 229, 0} +\definecolor{kit-yellow90}{rgb}{0.9894, 0.9082, 0.1} +\definecolor{kit-yellow80}{rgb}{0.9906, 0.9184, 0.2} +\definecolor{kit-yellow75}{rgb}{0.9912, 0.9235, 0.25} +\definecolor{kit-yellow70}{rgb}{0.9918, 0.9286, 0.3} +\definecolor{kit-yellow60}{rgb}{0.9929, 0.9388, 0.4} +\definecolor{kit-yellow50}{rgb}{0.9941, 0.949, 0.5} +\definecolor{kit-yellow40}{rgb}{0.9953, 0.9592, 0.6} +\definecolor{kit-yellow30}{rgb}{0.9965, 0.9694, 0.7} +\definecolor{kit-yellow25}{rgb}{0.9971, 0.9745, 0.75} +\definecolor{kit-yellow20}{rgb}{0.9976, 0.9796, 0.8} +\definecolor{kit-yellow15}{rgb}{0.9982, 0.9847, 0.85} +\definecolor{kit-yellow10}{rgb}{0.9988, 0.9898, 0.9} +\definecolor{kit-yellow5}{rgb}{0.9994, 0.9949, 0.95} + +% KIT color orange : +\definecolor{kit-orange}{RGB}{223, 155, 27} +\definecolor{kit-orange100}{RGB}{223, 155, 27} +\definecolor{kit-orange90}{rgb}{0.8871, 0.6471, 0.1953} +\definecolor{kit-orange80}{rgb}{0.8996, 0.6863, 0.2847} +\definecolor{kit-orange75}{rgb}{0.9059, 0.7059, 0.3294} +\definecolor{kit-orange70}{rgb}{0.9122, 0.7255, 0.3741} +\definecolor{kit-orange60}{rgb}{0.9247, 0.7647, 0.4635} +\definecolor{kit-orange50}{rgb}{0.9373, 0.8039, 0.5529} +\definecolor{kit-orange40}{rgb}{0.9498, 0.8431, 0.6424} +\definecolor{kit-orange30}{rgb}{0.9624, 0.8824, 0.7318} +\definecolor{kit-orange25}{rgb}{0.9686, 0.902, 0.7765} +\definecolor{kit-orange20}{rgb}{0.9749, 0.9216, 0.8212} +\definecolor{kit-orange15}{rgb}{0.9812, 0.9412, 0.8659} +\definecolor{kit-orange10}{rgb}{0.9875, 0.9608, 0.9106} +\definecolor{kit-orange5}{rgb}{0.9937, 0.9804, 0.9553} + +% KIT color lightgreen : +\definecolor{kit-lightgreen}{RGB}{140, 182, 60} +\definecolor{kit-lightgreen100}{RGB}{140, 182, 60} +\definecolor{kit-lightgreen90}{rgb}{0.5941, 0.7424, 0.3118} +\definecolor{kit-lightgreen80}{rgb}{0.6392, 0.771, 0.3882} +\definecolor{kit-lightgreen75}{rgb}{0.6618, 0.7853, 0.4265} +\definecolor{kit-lightgreen70}{rgb}{0.6843, 0.7996, 0.4647} +\definecolor{kit-lightgreen60}{rgb}{0.7294, 0.8282, 0.5412} +\definecolor{kit-lightgreen50}{rgb}{0.7745, 0.8569, 0.6176} +\definecolor{kit-lightgreen40}{rgb}{0.8196, 0.8855, 0.6941} +\definecolor{kit-lightgreen30}{rgb}{0.8647, 0.9141, 0.7706} +\definecolor{kit-lightgreen25}{rgb}{0.8873, 0.9284, 0.8088} +\definecolor{kit-lightgreen20}{rgb}{0.9098, 0.9427, 0.8471} +\definecolor{kit-lightgreen15}{rgb}{0.9324, 0.9571, 0.8853} +\definecolor{kit-lightgreen10}{rgb}{0.9549, 0.9714, 0.9235} +\definecolor{kit-lightgreen5}{rgb}{0.9775, 0.9857, 0.9618} + + +% KIT color purple : +\definecolor{kit-purple}{RGB}{163, 16, 124} +\definecolor{kit-purple100}{RGB}{163, 16, 124} +\definecolor{kit-purple90}{rgb}{0.6753, 0.1565, 0.5376} +\definecolor{kit-purple80}{rgb}{0.7114, 0.2502, 0.589} +\definecolor{kit-purple75}{rgb}{0.7294, 0.2971, 0.6147} +\definecolor{kit-purple70}{rgb}{0.7475, 0.3439, 0.6404} +\definecolor{kit-purple60}{rgb}{0.7835, 0.4376, 0.6918} +\definecolor{kit-purple50}{rgb}{0.8196, 0.5314, 0.7431} +\definecolor{kit-purple40}{rgb}{0.8557, 0.6251, 0.7945} +\definecolor{kit-purple30}{rgb}{0.8918, 0.7188, 0.8459} +\definecolor{kit-purple25}{rgb}{0.9098, 0.7657, 0.8716} +\definecolor{kit-purple20}{rgb}{0.9278, 0.8125, 0.8973} +\definecolor{kit-purple15}{rgb}{0.9459, 0.8594, 0.9229} +\definecolor{kit-purple10}{rgb}{0.9639, 0.9063, 0.9486} +\definecolor{kit-purple5}{rgb}{0.982, 0.9531, 0.9743} + +% KIT color brown : +\definecolor{kit-brown}{RGB}{167, 130, 46} +\definecolor{kit-brown100}{RGB}{167, 130, 46} +\definecolor{kit-brown90}{rgb}{0.6894, 0.5588, 0.2624} +\definecolor{kit-brown80}{rgb}{0.7239, 0.6078, 0.3443} +\definecolor{kit-brown75}{rgb}{0.7412, 0.6324, 0.3853} +\definecolor{kit-brown70}{rgb}{0.7584, 0.6569, 0.4263} +\definecolor{kit-brown60}{rgb}{0.7929, 0.7059, 0.5082} +\definecolor{kit-brown50}{rgb}{0.8275, 0.7549, 0.5902} +\definecolor{kit-brown40}{rgb}{0.862, 0.8039, 0.6722} +\definecolor{kit-brown30}{rgb}{0.8965, 0.8529, 0.7541} +\definecolor{kit-brown25}{rgb}{0.9137, 0.8775, 0.7951} +\definecolor{kit-brown20}{rgb}{0.931, 0.902, 0.8361} +\definecolor{kit-brown15}{rgb}{0.9482, 0.9265, 0.8771} +\definecolor{kit-brown10}{rgb}{0.9655, 0.951, 0.918} +\definecolor{kit-brown5}{rgb}{0.9827, 0.9755, 0.959} + +% KIT color cyan : +\definecolor{kit-cyan}{RGB}{35, 161, 224} +\definecolor{kit-cyan100}{RGB}{35, 161, 224} +\definecolor{kit-cyan90}{rgb}{0.2235, 0.6682, 0.8906} +\definecolor{kit-cyan80}{rgb}{0.3098, 0.7051, 0.9027} +\definecolor{kit-cyan75}{rgb}{0.3529, 0.7235, 0.9088} +\definecolor{kit-cyan70}{rgb}{0.3961, 0.742, 0.9149} +\definecolor{kit-cyan60}{rgb}{0.4824, 0.7788, 0.9271} +\definecolor{kit-cyan50}{rgb}{0.5686, 0.8157, 0.9392} +\definecolor{kit-cyan40}{rgb}{0.6549, 0.8525, 0.9514} +\definecolor{kit-cyan30}{rgb}{0.7412, 0.8894, 0.9635} +\definecolor{kit-cyan25}{rgb}{0.7843, 0.9078, 0.9696} +\definecolor{kit-cyan20}{rgb}{0.8275, 0.9263, 0.9757} +\definecolor{kit-cyan15}{rgb}{0.8706, 0.9447, 0.9818} +\definecolor{kit-cyan10}{rgb}{0.9137, 0.9631, 0.9878} +\definecolor{kit-cyan5}{rgb}{0.9569, 0.9816, 0.9939} + +% KIT color gray : +\definecolor{kit-gray}{RGB}{0, 0, 0} +\definecolor{kit-gray100}{RGB}{0, 0, 0} +\definecolor{kit-gray90}{rgb}{0.1, 0.1, 0.1} +\definecolor{kit-gray80}{rgb}{0.2, 0.2, 0.2} +\definecolor{kit-gray75}{rgb}{0.25, 0.25, 0.25} +\definecolor{kit-gray70}{rgb}{0.3, 0.3, 0.3} +\definecolor{kit-gray60}{rgb}{0.4, 0.4, 0.4} +\definecolor{kit-gray50}{rgb}{0.5, 0.5, 0.5} +\definecolor{kit-gray40}{rgb}{0.6, 0.6, 0.6} +\definecolor{kit-gray30}{rgb}{0.7, 0.7, 0.7} +\definecolor{kit-gray25}{rgb}{0.75, 0.75, 0.75} +\definecolor{kit-gray20}{rgb}{0.8, 0.8, 0.8} +\definecolor{kit-gray15}{rgb}{0.85, 0.85, 0.85} +\definecolor{kit-gray10}{rgb}{0.9, 0.9, 0.9} +\definecolor{kit-gray5}{rgb}{0.95, 0.95, 0.95} + + + +\setbeamercolor*{normal text}{fg=black} +\setbeamercolor*{alerted text}{fg=kit-red100} +\setbeamercolor*{example text}{fg=black} +\setbeamercolor*{structure}{fg=black} + +\setbeamercolor*{palette primary}{fg=black,bg=black!15} +\setbeamercolor*{palette secondary}{fg=black,bg=black!15} +\setbeamercolor*{palette tertiary}{fg=black,bg=black!15} +\setbeamercolor*{palette quaternary}{fg=black,bg=black!15} + +\setbeamercolor*{palette sidebar primary}{fg=black!75} +\setbeamercolor*{palette sidebar secondary}{fg=black!75} +\setbeamercolor*{palette sidebar tertiary}{fg=black!75} +\setbeamercolor*{palette sidebar quaternary}{fg=black!75} + +\setbeamercolor*{item projected}{fg=white,bg=kit-green100} + +\setbeamercolor*{block title}{fg=white,bg=kit-green100} +\setbeamercolor*{block title alerted}{use=alerted text,fg=white,bg=alerted text.fg!75!black} +\setbeamercolor*{block title example}{fg=white,bg=kit-blue100} + +\setbeamercolor*{block body}{fg=black,bg=kit-green15} +\setbeamercolor*{block body alerted}{parent=normal text,use=block title alerted,bg=block title alerted.bg!10!bg} +\setbeamercolor*{block body example}{fg=black,bg=kit-blue15} + +\setbeamercolor*{separation line}{} +\setbeamercolor*{fine separation line}{} + +\setbeamercolor*{background canvas}{bg=white} + +%% ----------- +%% | /Farben | +%% ----------- + +%% ----------------------------------- +%% | halbgerundete Aufzählungspunkte | +%% ----------------------------------- + +% KIT-Aufzählungszeichen +\newcommand{\KITmark}{% + \begin{pgfpicture}{0mm}{0mm}{1ex}{1ex} + {\pgfsetcornersarced{\pgfpoint{.3ex}{.3ex}} + \pgfpathmoveto{\pgfpoint{0cm}{1ex}} + \pgfpathlineto{\pgfpoint{1ex}{1ex}} + \pgfpathlineto{\pgfpoint{1ex}{0cm}}} + {\pgfsetcornersarced{\pgfpoint{.3ex}{.3ex}} + \pgfpathmoveto{\pgfpoint{1ex}{0cm}} + \pgfpathlineto{\pgfpointorigin} + \pgfpathlineto{\pgfpoint{0cm}{1ex}}} + \color{kit-green100} + \pgfusepath{fill} + \end{pgfpicture}% +} + +\setbeamertemplate{itemize items}{\raisebox{.2ex}{\KITmark}} + +%% ---------------------- +%% | Inhaltsverzeichnis | +%% ---------------------- + +\setbeamertemplate{section in toc}{\normalsize\textbf{\textcolor{kit-blue}{\inserttocsectionnumber.~\inserttocsection}}\par} +\setbeamertemplate{subsection in toc}{\small\hspace{1.2em}\raisebox{.2ex}{\KITmark}\hspace{\labelsep}\inserttocsubsection\par} + +%% ------------------------------ +%% | halbgerundete Beamer-Boxen | +%% ------------------------------ + +\renewcommand\beamerboxesrounded[2][]{% + \global\let\beamer@firstlineitemizeunskip=\relax% + \vbox\bgroup% + \setkeys{beamerboxes}{upper=block title,lower=block body,width=\textwidth,shadow=false}% + \setkeys{beamerboxes}{#1}% + {% + \usebeamercolor{\bmb@lower}% + \globalcolorstrue% + \colorlet{lower.bg}{bg}% + }% + {% + \usebeamercolor{\bmb@upper}% + \globalcolorstrue% + \colorlet{upper.bg}{bg}% + }% + % + % Typeset head + % + \vskip4bp + \setbox\bmb@box=\hbox{% + \begin{minipage}[b]{\bmb@width}% + \usebeamercolor[fg]{\bmb@upper}% + #2% + \end{minipage}}% + \ifdim\wd\bmb@box=0pt% + \setbox\bmb@box=\hbox{}% + \ht\bmb@box=1.5pt% + \bmb@prevheight=-4.5pt% + \else% + \wd\bmb@box=\bmb@width% + \bmb@temp=\dp\bmb@box% + \ifdim\bmb@temp<1.5pt% + \bmb@temp=1.5pt% + \fi% + \setbox\bmb@box=\hbox{\raise\bmb@temp\hbox{\box\bmb@box}}% + \dp\bmb@box=0pt% + \bmb@prevheight=\ht\bmb@box% + \fi% + \bmb@temp=\bmb@width% + \bmb@dima=\bmb@temp\advance\bmb@dima by2.2bp% + \bmb@dimb=\bmb@temp\advance\bmb@dimb by4bp% + \hbox{% + \begin{pgfpicture}{0bp}{+-\ht\bmb@box}{0bp}{+-\ht\bmb@box} + \ifdim\wd\bmb@box=0pt% + \color{lower.bg}% + \else% + \color{upper.bg}% + \fi% + \pgfpathqmoveto{-4bp}{-1bp} + % Adaption for "KIT-Design" + \pgfpathlineto{\pgfpoint{-4bp}{3bp}} + %\pgfpathqcurveto{-4bp}{1.2bp}{-2.2bp}{3bp}{0bp}{3bp} + \pgfpathlineto{\pgfpoint{\bmb@temp}{3bp}} + \pgfpathcurveto% + {\pgfpoint{\bmb@dima}{3bp}}% + {\pgfpoint{\bmb@dimb}{1.2bp}}% + {\pgfpoint{\bmb@dimb}{-1bp}}% + \bmb@dima=-\ht\bmb@box% + \advance\bmb@dima by-2pt% + \pgfpathlineto{\pgfpoint{\bmb@dimb}{\bmb@dima}} + \pgfpathlineto{\pgfpoint{-4bp}{\bmb@dima}} + \pgfusepath{fill} + \end{pgfpicture}% + \copy\bmb@box% + }% + \nointerlineskip% + \vskip-1pt% + \ifdim\wd\bmb@box=0pt% + \else% + \hbox{% + \begin{pgfpicture}{0pt}{0pt}{\bmb@width}{6pt} + \bmb@dima=\bmb@width% + \advance\bmb@dima by8bp% + \pgfpathrectangle{\pgfpoint{-4bp}{-1bp}}{\pgfpoint{\bmb@dima}{8bp}} + \pgfusepath{clip} + {\pgftransformshift{\pgfpoint{-4bp}{0bp}}\pgftext[left,base]{\pgfuseshading{bmb@transition}}}% + \end{pgfpicture}% + }% + \nointerlineskip% + \vskip-0.5pt% + \fi% + \ifbmb@shadow% + \setbox\bmb@boxshadow=\hbox{\pgfuseshading{bmb@shadow}}% + \setbox\bmb@boxshadowball=\hbox{\pgfuseshading{bmb@shadowball}}% + \setbox\bmb@boxshadowballlarge=\hbox{\pgfuseshading{bmb@shadowballlarge}}% + \fi% + \setbox\bmb@colorbox=\hbox{{\pgfpicturetrue\pgfsetcolor{lower.bg}}}% + \setbox\bmb@box=\hbox\bgroup\begin{minipage}[b]{\bmb@width}% + \vskip2pt% + \usebeamercolor[fg]{\bmb@lower}% + \colorlet{beamerstructure}{upper.bg}% + \colorlet{structure}{upper.bg}% + %\color{.}% + } + +\def\endbeamerboxesrounded{% + \end{minipage}\egroup% + \wd\bmb@box=\bmb@width% + \bmb@temp=\dp\bmb@box% + \advance\bmb@temp by.5pt% + \setbox\bmb@box=\hbox{\raise\bmb@temp\hbox{\box\bmb@box}}% + \dp\bmb@box=0pt% + \bmb@temp=\wd\bmb@box% + \bmb@dima=\bmb@temp\advance\bmb@dima by2.2bp% + \bmb@dimb=\bmb@temp\advance\bmb@dimb by4bp% + \hbox{% + \begin{pgfpicture}{0bp}{0bp}{0bp}{0bp} + \ifbmb@shadow% + {\pgftransformshift{\pgfpoint{4bp}{-3bp}}\pgftext{\copy\bmb@boxshadowball}} + \begin{pgfscope} + {% + \advance\bmb@temp by-1bp% + \pgfpathrectangle{\pgfpoint{\bmb@temp}{-7bp}}{\pgfpoint{9bp}{9bp}}% + \pgfusepath{clip} + }% + {\pgftransformshift{\pgfpoint{\bmb@temp}{1bp}}\pgftext{\box\bmb@boxshadowballlarge}} + \end{pgfscope} + \begin{pgfscope} + \advance\bmb@temp by-4bp% + \pgfpathrectangle{\pgfpoint{4bp}{-7bp}}{\pgfpoint{\bmb@temp}{5bp}} + \pgfusepath{clip} + {\pgftransformshift{\pgfpoint{4bp}{-7bp}}\pgftext[left,base]{\copy\bmb@boxshadow}}% + \end{pgfscope} + \begin{pgfscope} + \advance\bmb@temp by 4bp% + \bmb@dima=\ht\bmb@box% + \advance\bmb@dima by\bmb@prevheight% + \advance\bmb@dima by 4bp% + \pgfpathrectangle{\pgfpoint{\bmb@temp}{1bp}}{\pgfpoint{4bp}{\bmb@dima}} + \pgfusepath{clip} + \advance\bmb@dima by-4bp% + {\pgftransformshift{\pgfpoint{\bmb@temp}{\bmb@dima}}\pgftext{\box\bmb@boxshadowball}} + \advance\bmb@dima by-1bp% + \pgfpathrectangle{\pgfpoint{\bmb@temp}{1bp}}{\pgfpoint{4bp}{\bmb@dima}} + \pgfusepath{clip} + \advance\bmb@temp by4bp% + {\pgftransformshift{\pgfpoint{\bmb@temp}{-3bp}}% + \pgftransformrotate{90}% + \pgftext[left,base]{\box\bmb@boxshadow}}% + \end{pgfscope} + \fi% + \unhbox\bmb@colorbox% + \pgfpathqmoveto{-4bp}{1bp} + \pgfpathqcurveto{-4bp}{-1.2bp}{-2.2bp}{-3bp}{0bp}{-3bp} + \pgfpathlineto{\pgfpoint{\the\bmb@dimb}{-3bp}} + { + \bmb@dima=\ht\bmb@box% + \pgfpathlineto{\pgfpoint{\bmb@dimb}{\bmb@dima}} + \pgfpathlineto{\pgfpoint{-4bp}{\bmb@dima}} + \pgfusepath{fill} + } + \ifbmb@shadow% + { + \color{black!50!bg} + \pgfsetlinewidth{0pt} + \pgfpathmoveto{\pgfpoint{\bmb@dimb}{-.5bp}} + \bmb@dima=\ht\bmb@box% + \advance\bmb@dima by\bmb@prevheight% + \advance\bmb@dima by 1bp% + \pgfpathlineto{\pgfpoint{\bmb@dimb}{\bmb@dima}} + \pgfusepath{stroke} + \bmb@temp=\bmb@dima + \advance\bmb@dima by 1bp% + \color{black!31!bg} + \pgfpathmoveto{\pgfpoint{\bmb@dimb}{\bmb@temp}} + \pgfpathlineto{\pgfpoint{\bmb@dimb}{\bmb@dima}} + \pgfusepath{stroke} + \advance\bmb@dima by 1bp% + \advance\bmb@temp by 1bp% + \color{black!19!bg} + \pgfpathmoveto{\pgfpoint{\bmb@dimb}{\bmb@temp}} + \pgfpathlineto{\pgfpoint{\bmb@dimb}{\bmb@dima}} + \pgfusepath{stroke} + \advance\bmb@dima by 1bp% + \advance\bmb@temp by 1bp% + \color{black!6!bg} + \pgfpathmoveto{\pgfpoint{\bmb@dimb}{\bmb@temp}} + \pgfpathlineto{\pgfpoint{\bmb@dimb}{\bmb@dima}} + \pgfusepath{stroke} + \advance\bmb@dima by 1.5bp% + \advance\bmb@temp by 1bp% + \color{bg} + \pgfpathmoveto{\pgfpoint{\bmb@dimb}{\bmb@temp}} + \pgfpathlineto{\pgfpoint{\bmb@dimb}{\bmb@dima}} + \pgfusepath{stroke} + } + \fi + \end{pgfpicture}% + \box\bmb@box% + }% + \ifbmb@shadow% + \vskip4bp minus 2bp% + \else% + \vskip2bp% + \fi% + \egroup% of \vbox\bgroup +} + + +%% ------------------------------- +%% | /halbgerundete Beamer-Boxen | +%% ------------------------------- + + +%% ---------------------- +%% | Block-Definitionen | +%% ---------------------- + +% Content environment for structuring. Basically a headline followed by text +\newenvironment<>{contentblock}[1]{\begingroup% + \setbeamertemplate{blocks}[default] + \setbeamercolor{block body}{fg=black,bg=}% + \setbeamercolor{block title}{fg=black,bg=}% + \setbeamerfont*{block title}{family=\sffamily,series=\bfseries,size=\large} + \begin{block}#2{#1}% + }{\end{block}% +\endgroup} + + +\newenvironment<>{greenblock}[1]{\begingroup% + \setbeamercolor{block body}{fg=black,bg=kit-green15}% + \setbeamercolor{block title}{fg=white,bg=kit-green100}% + \begin{block}#2{#1}% + }{\end{block} +\endgroup} + +\newenvironment<>{blueblock}[1]{\begingroup% + \setbeamercolor{block body}{fg=black,bg=kit-blue15}% + \setbeamercolor{block title}{fg=white,bg=kit-blue100}% + \begin{block}#2{#1}% + }{\end{block} +\endgroup} + +\newenvironment<>{redblock}[1]{\begingroup% + \setbeamercolor{block body}{fg=black,bg=kit-red15}% + \setbeamercolor{block title}{fg=white,bg=kit-red100}% + \begin{block}#2{#1}% + }{\end{block}% +\endgroup} + +\newenvironment<>{brownblock}[1]{\begingroup% + \setbeamercolor{block body}{fg=black,bg=kit-brown15}% + \setbeamercolor{block title}{fg=white,bg=kit-brown100}% + \begin{block}#2{#1}% + }{\end{block}% +\endgroup} + +\newenvironment<>{purpleblock}[1]{\begingroup% + \setbeamercolor{block body}{fg=black,bg=kit-purple15}% + \setbeamercolor{block title}{fg=white,bg=kit-purple100}% + \begin{block}#2{#1}% + }{\end{block}% +\endgroup} + +\newenvironment<>{grayblock}[1]{\begingroup% + \setbeamercolor{block body}{fg=black,bg=kit-gray15}% + \setbeamercolor{block title}{fg=white,bg=kit-gray70}% + \begin{block}#2{#1}% + }{\end{block}% +\endgroup} + +\newenvironment<>{yellowblock}[1]{\begingroup% + \setbeamercolor{block body}{fg=black,bg=kit-yellow30}% + \setbeamercolor{block title}{fg=black,bg=kit-yellow100}% + \begin{block}#2{#1}% + }{\end{block}% +\endgroup} + +\newenvironment<>{lightgreenblock}[1]{\begingroup% + \setbeamercolor{block body}{fg=black,bg=kit-lightgreen15}% + \setbeamercolor{block title}{fg=white,bg=kit-lightgreen100}% + \begin{block}#2{#1}% + }{\end{block}% +\endgroup} + +\newenvironment<>{orangeblock}[1]{\begingroup% + \setbeamercolor{block body}{fg=black,bg=kit-orange15}% + \setbeamercolor{block title}{fg=white,bg=kit-orange100}% + \begin{block}#2{#1}% + }{\end{block}% +\endgroup} + +\newenvironment<>{cyanblock}[1]{\begingroup% + \setbeamercolor{block body}{fg=black,bg=kit-cyan15}% + \setbeamercolor{block title}{fg=white,bg=kit-cyan100}% + \begin{block}#2{#1}% + }{\end{block}% +\endgroup} +%% ------------------------------ +%% | /Block-Definitionen | +%% ------------------------------ + +%% use this for setting the total page number +\newcommand{\beginbackup}{ + \newcounter{framenumbervorappendix} + \setcounter{framenumbervorappendix}{\value{framenumber}} +} +\newcommand{\backupend}{ + \addtocounter{framenumbervorappendix}{-\value{framenumber}} + \addtocounter{framenumber}{\value{framenumbervorappendix}} +} diff --git "a/01-pflichtenheft-kolloquium/slides/einf\303\274hrung.tex" "b/01-pflichtenheft-kolloquium/slides/einf\303\274hrung.tex" new file mode 100644 index 0000000..d818cd3 --- /dev/null +++ "b/01-pflichtenheft-kolloquium/slides/einf\303\274hrung.tex" @@ -0,0 +1,40 @@ +\begin{frame}[t]{Einführung} + + + \begin{columns}[t] + \begin{column}{.3\textwidth} + \centering{\fontsize{30pt}{36pt}\selectfont\faPodcast} + \vspace{.2cm} + \begin{block}{Podcast} + \begin{itemize} + \item RSS-Feed + \item Episoden + \item Audio/Video + \end{itemize} + \end{block} + \end{column} + \begin{column}{.3\textwidth} + \centering{\fontsize{30pt}{36pt}\selectfont\faArrowCircleDown} + \vspace{.2cm} + \begin{block}{Podcatcher} + \begin{itemize} + \item lokale Verwaltung von Podcasts + \item API Unterstützung + \item Abspielen von Episoden + \end{itemize} + \end{block} + \end{column} + \begin{column}{.3\textwidth} + \centering{\fontsize{30pt}{36pt}\selectfont\faSync} + \vspace{.2cm} + \begin{block}{Synchronisationsserver} + \begin{itemize} + \item Hörfortschritte + \item Abonnements + \item Discovery + \end{itemize} + \end{block} + \end{column} + \end{columns} + +\end{frame} \ No newline at end of file diff --git a/01-pflichtenheft-kolloquium/slides/features.tex b/01-pflichtenheft-kolloquium/slides/features.tex new file mode 100644 index 0000000..6ec435b --- /dev/null +++ b/01-pflichtenheft-kolloquium/slides/features.tex @@ -0,0 +1,37 @@ +\begin{frame}{Features} +\begin{columns}[t] +\begin{column}{.3\textwidth} +\centering{\fontsize{30pt}{36pt}\selectfont\faSync} +\vspace{.2cm} +\begin{block}{Synchronisation} +\begin{itemize} + \item Abos + \item Hörfortschritt +\end{itemize} +\end{block} +\end{column} +\begin{column}{.3\textwidth} +\centering{\fontsize{30pt}{36pt}\selectfont\faGlobe} +\vspace{.2cm} +\begin{block}{Weboberfläche} +\begin{itemize} + \item Aboliste + \item Zuletzt gehört +\end{itemize} +\end{block} +\end{column} +\begin{column}{.3\textwidth} +\centering{\fontsize{30pt}{36pt}\selectfont\faUser} +\vspace{.2cm} +\begin{block}{Account-Verwaltung} +\begin{itemize} + \item Registrieren und Anmelden + \item Passwort ändern und zurücksetzen + \item Account löschen + \item Daten importieren/exportieren +\end{itemize} +\end{block} +\end{column} +\end{columns} +\end{frame} + diff --git a/01-pflichtenheft-kolloquium/slides/synchronisation.tex b/01-pflichtenheft-kolloquium/slides/synchronisation.tex new file mode 100644 index 0000000..0849279 --- /dev/null +++ b/01-pflichtenheft-kolloquium/slides/synchronisation.tex @@ -0,0 +1,36 @@ +\begin{frame}{Synchronisation} + + \tikzstyle{line} = [draw, -latex'] + + \begin{figure}[H] + + \begin{tikzpicture} + + \tikzset{focus/.style={rectangle, minimum width = 1cm, minimum height = 0.5cm, rounded corners, draw}}; + \tikzset{hyperfocus/.style={rectangle, minimum width = 1cm, minimum height = 0.5cm, draw}}; + + \node[hyperfocus, text width = 2.6cm, fill = green!25](s){Synchronisations-\\\quad \quad Server}; + \node[focus, left = 3cm of s, text width = 3.1cm, orange](p1){\quad \underline{p1:Podcatcher} + \begin{itemize} + \vspace{0.2cm} + \item Abonnements + \item Hörfortschritt + \vspace{0.1cm} + \end{itemize}}; + \node[focus, above = 1.8cm of s, orange](p2){p2}; + \node[focus, right = 2.5cm of s, orange](p3){p3}; + \node[below = 1.7cm of s](p4){\textbf{...}}; + \umlactor[left = 2.7cm of p2, blue!60]{Benutzer}; + + \draw[] (p1) -- (s); + \draw[] (p2) -- (s); + \draw[] (s) -- (p3); + \draw[] (s) -- (p4); + \path [line, thick, blue!60] (Benutzer) -- node [text width=2.5cm, midway, above=0.1cm, align=center] {Podcast abonnieren} (p2); + \path [line, thick, blue!60] (Benutzer) -| node [text width=2.5cm, midway, above=0.1cm, align=center] {Episode anhören} (p1); + + \end{tikzpicture} + + \end{figure} + +\end{frame} \ No newline at end of file diff --git a/01-pflichtenheft-kolloquium/slides/zielsetzung.tex b/01-pflichtenheft-kolloquium/slides/zielsetzung.tex new file mode 100644 index 0000000..84e1064 --- /dev/null +++ b/01-pflichtenheft-kolloquium/slides/zielsetzung.tex @@ -0,0 +1,39 @@ +\begin{frame}{Zielsetzung} + +\begin{figure}[H] + +\raggedright + +\begin{tikzpicture} + +\tikzset{focus/.style={rectangle, minimum width=1cm, minimum height=0.5cm, rounded corners=7pt, draw}}; + +\tikzset{hyperfocus/.style={rectangle, minimum width=1cm, minimum height=0.5cm, draw}}; + +\node[hyperfocus] (main) {gPodder}; +\node[focus, right = 2cm of main] (sync) {Synchronisation}; +\node[above = of sync] (share) {Inhalt teilen}; +\node[above = 0.5cm of share] (discover) {Podcasts entdecken}; +\node[text width = 3cm, below = of sync] (create) {Listen erstellen und teilen}; +\node[text width = 3cm, below = 0.5cm of create] (popular) {Publisher:\\ Was ist beliebt?}; + +\node[hyperfocus, right = 3cm of sync, text width = 4cm] (PSE) {\underline{PSE\textsuperscript{2}}\begin{itemize} + \item Schlankes Design + \item Effizient + \item Intuitiv +\end{itemize} +}; + +\draw[] (main) -- (sync); +\draw[] (main) -- (share); +\draw[] (main) |- (discover); +\draw[] (main) -- (create); +\draw[] (main) |- (popular); +\draw[stealth-, thick] (sync.east) -- ($(PSE.north west) + (0, -0.3)$); + + +\end{tikzpicture} + +\end{figure} + +\end{frame} \ No newline at end of file diff --git a/01-pflichtenheft-kolloquium/tikz-uml.sty b/01-pflichtenheft-kolloquium/tikz-uml.sty new file mode 100644 index 0000000..c6e8e0d --- /dev/null +++ b/01-pflichtenheft-kolloquium/tikz-uml.sty @@ -0,0 +1,5377 @@ +%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% Start of tikz-uml.sty +% +% Some macros for UML Diagrams. +% Home page of project : +% Author: Nicolas Kielbasiewicz +% Style from : +% Fixed by Nicolas Kielbasiewicz (nicolas.kielbasiewicz@ensta-paristech.fr) in march 2016 to compile with pgf 3.00 +%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +\NeedsTeXFormat{LaTeX2e}[1995/12/01]% +\ProvidesPackage{tikz-uml}[2011/01/26]% +% +\RequirePackage{etoolbox}% +\RequirePackage{ifthen}% +\RequirePackage{tikz}% +\RequirePackage{xstring}% +\RequirePackage{calc}% +\RequirePackage{pgfopts}% +\usetikzlibrary{backgrounds,arrows,shapes,fit,shadows,decorations.markings}% +% +\def\tikzumlPackageLayersNum{3}% +\pgfkeys{/tikzuml/options/.cd, packageLayers/.initial=3}% +\pgfkeys{/tikzuml/options/.cd, packageLayers/.store in=\tikzumlPackageLayersNum}% +\def\tikzumlStateLayersNum{3}% +\pgfkeys{/tikzuml/options/.cd, stateLayers/.initial=3}% +\pgfkeys{/tikzuml/options/.cd, stateLayers/.store in=\tikzumlStateLayersNum}% +\def\tikzumlFragmentLayersNum{3}% +\pgfkeys{/tikzuml/options/.cd, fragmentLayers/.initial=3}% +\pgfkeys{/tikzuml/options/.cd, fragmentLayers/.store in=\tikzumlFragmentLayersNum}% +\def\tikzumlComponentLayersNum{3}% +\pgfkeys{/tikzuml/options/.cd, componentLayers/.initial=3}% +\pgfkeys{/tikzuml/options/.cd, componentLayers/.store in=\tikzumlComponentLayersNum}% +% +\ProcessPgfOptions{/tikzuml/options}% +% +\def\pgfsetlayersArg{background}% +\pgfdeclarelayer{background}% +\newcounter{tikzumlPackageLayers}% +\loop \pgfdeclarelayer{package\thetikzumlPackageLayers}% + \xdef\pgfsetlayersArg{\pgfsetlayersArg,package\thetikzumlPackageLayers}% + \ifnum\tikzumlPackageLayersNum>\thetikzumlPackageLayers% + \stepcounter{tikzumlPackageLayers}% +\repeat% +% +\newcounter{tikzumlFragmentLayers}% +\loop \pgfdeclarelayer{fragment\thetikzumlFragmentLayers}% + \xdef\pgfsetlayersArg{\pgfsetlayersArg,fragment\thetikzumlFragmentLayers}% + \ifnum\tikzumlFragmentLayersNum>\thetikzumlFragmentLayers% + \stepcounter{tikzumlFragmentLayers}% +\repeat% +% +\newcounter{tikzumlStateLayers}% +\loop \pgfdeclarelayer{state\thetikzumlStateLayers}% + \xdef\pgfsetlayersArg{\pgfsetlayersArg,state\thetikzumlStateLayers}% + \ifnum\tikzumlStateLayersNum>\thetikzumlStateLayers% + \stepcounter{tikzumlStateLayers}% +\repeat% +% +\newcounter{tikzumlComponentLayers}% +\loop \pgfdeclarelayer{component\thetikzumlComponentLayers}% + \xdef\pgfsetlayersArg{\pgfsetlayersArg,component\thetikzumlComponentLayers}% + \ifnum\tikzumlComponentLayersNum>\thetikzumlComponentLayers% + \stepcounter{tikzumlComponentLayers}% +\repeat% +% +\pgfdeclarelayer{lifelines}% +\pgfdeclarelayer{activity}% +\pgfdeclarelayer{connections}% +\xdef\pgfsetlayersArg{\pgfsetlayersArg,lifelines,activity,connections,main}% +\pgfsetlayers{\pgfsetlayersArg}% +% +\pgfkeys{/tikzuml/.cd, text/.initial=black, draw/.initial=black, font/.initial=\small,% + x/.initial=0, y/.initial=0,% + package type/.initial=tikzumlEmpty, fill package/.initial=blue!20,% + class width/.initial=10ex, simple interface width/.initial=4ex, class type/.initial=class, fill class/.initial=yellow!20, fill template/.initial=yellow!2,% + narynode width/.initial=6ex,% + relation geometry/.initial=--, relation angle1/.initial=-30, relation angle2/.initial=30, relation loopsize/.initial=3em, relation weight/.initial=0.5, relation pos1/.initial=0.2, relation pos2/.initial=0.8, relation pos stereo/.initial=0.5,% + note width/.initial=3cm, fill note/.initial=green!20,% + fill system/.initial=white,% + fill usecase/.initial=blue!20,% + actor below/.initial=0.5cm,% + state join width/.initial=3ex,% + state decision width/.initial=3ex,% + state initial width/.initial=5ex,% + state final width/.initial=5.5ex,% + state enter width/.initial=5ex,% + state exit width/.initial=5ex,% + state end width/.initial=5ex,% + state history width/.initial=5ex,% + state deep history width/.initial=5ex,% + state width/.initial=8ex, fill state/.initial=yellow!20,% + object stereo/.initial=object, fill object/.initial=yellow!20,% + call dt/.initial=tikzumlEmpty, call padding/.initial=2, call type/.initial=synchron, fill call/.initial=white,% + fragment type/.initial=opt, fragment inner xsep/.initial=1, fragment inner ysep/.initial=1, fill fragment/.initial= none,% + create call dt/.initial=4,% + component width/.initial=8ex, fill component/.initial= yellow!20,% + required interface distance/.initial=2.5cm, required interface width/.initial=1em, required interface padding/.initial=1cm,% + provided interface distance/.initial=3cm, provided interface width/.initial=1em, provided interface padding/.initial=1cm,% + port width/.initial=1ex, fill port/.initial= yellow!20,% + fill assembly connector/.initial= white,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \errmessage{TIKZUML ERROR : in tikzuml global, invalid option \keyname}% + }% +}% +% +\pgfkeys{/tikzuml/.cd, text/.get=\tikzumlDefaultTextColor, draw/.get=\tikzumlDefaultDrawColor, font/.get=\tikzumlDefaultFont,% + x/.get=\tikzumlDefaultX, y/.get=\tikzumlDefaultY,% + package type/.get=\tikzumlPackageDefaultType, fill package/.get=\tikzumlPackageDefaultFillColor,% + class width/.get=\tikzumlClassDefaultWidth, simple interface width/.get=\tikzumlSimpleInterfaceDefaultWidth, class type/.get=\tikzumlClassDefaultType, fill class/.get=\tikzumlClassDefaultFillColor, fill template/.get=\tikzumlClassTemplateFillColorDefaultFillColor,% + narynode width/.get=\tikzumlNaryNodeDefaultWidth,% + relation geometry/.get=\tikzumlRelationDefaultGeometry, relation angle1/.get=\tikzumlRelationDefaultAngleO, relation angle2/.get=\tikzumlRelationDefaultAngleT, relation loopsize/.get=\tikzumlRelationDefaultLoopSize, relation weight/.get=\tikzumlRelationDefaultWeight, relation pos1/.get=\tikzumlRelationDefaultPosO, relation pos2/.get=\tikzumlRelationDefaultPosT, relation pos stereo/.get=\tikzumlRelationDefaultPosStereo,% + note width/.get=\tikzumlNoteDefaultWidth, fill note/.get=\tikzumlNoteDefaultFillColor,% + fill system/.get=\tikzumlSystemDefaultFillColor,% + fill usecase/.get=\tikzumlUseCaseDefaultFillColor,% + actor below/.get=\tikzumlActorDefaultBelow,% + state join width/.get=\tikzumlStateJoinDefaultWidth,% + state decision width/.get=\tikzumlStateDecisionDefaultWidth,% + state initial width/.get=\tikzumlStateInitialDefaultWidth,% + state final width/.get=\tikzumlStateFinalDefaultWidth,% + state enter width/.get=\tikzumlStateEnterDefaultWidth,% + state exit width/.get=\tikzumlStateExitDefaultWidth,% + state end width/.get=\tikzumlStateEndDefaultWidth,% + state history width/.get=\tikzumlStateHistoryDefaultWidth,% + state deep history width/.get=\tikzumlStateDeepHistoryDefaultWidth,% + state width/.get=\tikzumlStateDefaultWidth, fill state/.get=\tikzumlStateDefaultFillColor,% + object stereo/.get=\tikzumlObjectDefaultStereo, fill object/.get=\tikzumlObjectDefaultFillColor,% + call dt/.get=\tikzumlCallDefaultDT, call padding/.get=\tikzumlCallDefaultPadding, call type/.get=\tikzumlCallDefaultType, fill call/.get=\tikzumlCallDefaultFillColor,% + fragment type/.get=\tikzumlFragmentDefaultType, fragment inner xsep/.get=\tikzumlFragmentDefaultXSep, fragment inner ysep/.get=\tikzumlFragmentDefaultYSep, fill fragment/.get=\tikzumlFragmentDefaultFillColor,% + create call dt/.get=\tikzumlCreateCallDefaultDT,% + component width/.get=\tikzumlComponentDefaultWidth, fill component/.get=\tikzumlComponentDefaultFillColor,% + required interface distance/.get=\tikzumlRequiredInterfaceDefaultDistance, required interface width/.get=\tikzumlRequiredInterfaceDefaultWidth, required interface padding/.get=\tikzumlRequiredInterfaceDefaultPadding,% + provided interface distance/.get=\tikzumlProvidedInterfaceDefaultDistance, provided interface width/.get=\tikzumlProvidedInterfaceDefaultWidth, provided interface padding/.get=\tikzumlProvidedInterfaceDefaultPadding,% + port width/.get=\tikzumlPortDefaultWidth, fill port/.get=\tikzumlPortDefaultFillColor,% + fill assembly connector/.get=\tikzumlAssemblyConnectorDefaultFillColor% +}% +% +% utility : change default colors +\newcommand{\tikzumlset}[1]{% + \pgfkeys{/tikzuml/.cd,#1}% + \pgfkeys{/tikzuml/.cd, text/.get=\tikzumlDefaultTextColor, draw/.get=\tikzumlDefaultDrawColor, font/.get=\tikzumlDefaultFont,% + x/.get=\tikzumlDefaultX, y/.get=\tikzumlDefaultY,% + package type/.get=\tikzumlPackageDefaultType, fill package/.get=\tikzumlPackageDefaultFillColor,% + class width/.get=\tikzumlClassDefaultWidth, simple interface width/.get=\tikzumlSimpleInterfaceDefaultWidth, class type/.get=\tikzumlClassDefaultType, fill class/.get=\tikzumlClassDefaultFillColor, fill template/.get=\tikzumlClassTemplateFillColorDefaultFillColor,% + narynode width/.get=\tikzumlNaryNodeWidth,% + relation geometry/.get=\tikzumlRelationDefaultGeometry, relation angle1/.get=\tikzumlRelationDefaultAngleO, relation angle2/.get=\tikzumlRelationDefaultAngleT, relation loopsize/.get=\tikzumlRelationDefaultLoopSize, relation weight/.get=\tikzumlRelationDefaultWeight, relation pos1/.get=\tikzumlRelationDefaultPosO, relation pos2/.get=\tikzumlRelationDefaultPosT, relation pos stereo/.get=\tikzumlRelationDefaultPosStereo,% + note width/.get=\tikzumlNoteDefaultWidth, fill note/.get=\tikzumlNoteDefaultFillColor,% + fill system/.get=\tikzumlSystemDefaultFillColor,% + fill usecase/.get=\tikzumlUseCaseDefaultFillColor,% + actor below/.get=\tikzumlActorDefaultBelow,% + state join width/.get=\tikzumlStateJoinDefaultWidth,% + state decision width/.get=\tikzumlStateDecisionDefaultWidth,% + state initial width/.get=\tikzumlStateInitialDefaultWidth,% + state final width/.get=\tikzumlStateFinalDefaultWidth,% + state enter width/.get=\tikzumlStateEnterDefaultWidth,% + state exit width/.get=\tikzumlStateExitDefaultWidth,% + state end width/.get=\tikzumlStateEndDefaultWidth,% + state history width/.get=\tikzumlStateHistoryDefaultWidth,% + state deep history width/.get=\tikzumlStateDeepHistoryDefaultWidth,% + state width/.get=\tikzumlStateDefaultWidth, fill state/.get=\tikzumlStateDefaultFillColor,% + object stereo/.get=\tikzumlObjectDefaultStereo, fill object/.get=\tikzumlObjectDefaultFillColor,% + call dt/.get=\tikzumlCallDefaultDT, call padding/.get=\tikzumlCallDefaultPadding, call type/.get=\tikzumlCallDefaultType, fill call/.get=\tikzumlCallDefaultFillColor,% + fragment type/.get=\tikzumlFragmentDefaultType, fragment inner xsep/.get=\tikzumlFragmentDefaultXSep, fragment inner ysep/.get=\tikzumlFragmentDefaultYSep, fill fragment/.get=\tikzumlFragmentDefaultFillColor,% + create call dt/.get=\tikzumlCreateCallDT,% + component width/.get=\tikzumlComponentDefaultWidth, fill component/.get=\tikzumlComponentDefaultFillColor,% + required interface distance/.get=\tikzumlRequiredInterfaceDefaultDistance, required interface width/.get=\tikzumlRequiredInterfaceDefaultWidth, required interface padding/.get=\tikzumlRequiredInterfaceDefaultPadding,% + provided interface distance/.get=\tikzumlProvidedInterfaceDefaultDistance, provided interface width/.get=\tikzumlProvidedInterfaceDefaultWidth, provided interface padding/.get=\tikzumlProvidedInterfaceDefaultPadding,% + port width/.get=\tikzumlPortDefaultWidth, fill port/.get=\tikzumlPortDefaultFillColor,% + fill assembly connector/.get=\tikzumlAssemblyConnectorDefaultFillColor% + }% +}% +% +% define a point +% arg : node/coordinates of the point +\newcommand{\umlpoint}[1]{% + \begin{pgfonlayer}{connections}% + \node[tikzuml control nodes style] at (#1) {};% + \end{pgfonlayer}% +}% +% +\newcommand{\tikzumlskipescape}[3][_]{% +\begingroup% + \def\_{#1}\edef\x{\endgroup% + \def\noexpand\csname #3\endcsname{#2}}\x% +}% +% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% class diagrams % +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +\pgfkeys{/tikzuml/relation/.cd, attr1/.style args={#1|#2}{arg1=#1, mult1=#2},% + attr2/.style args={#1|#2}{arg2=#1, mult2=#2},% + attr/.style args={#1|#2}{arg=#1, mult=#2},% + recursive/.style args={#1|#2|#3}{angle1=#1, angle2=#2, loopsize=#3},% + anchors/.style args={#1 and #2}{anchor1=#1, anchor2=#2},% + recursive direction/.style args={#1 to #2}{recursive direction start=#1, recursive direction end=#2}% +}% +% +\pgfkeys{/tikzuml/note/.cd, anchors/.style args={#1 and #2}{anchor1=#1, anchor2=#2}}% +% +\tikzstyle{tikzuml simpleclass style}=[rectangle, minimum height=2em, node distance=2em]% +\tikzstyle{tikzuml simpleinterface style}=[circle, minimum height=1em, node distance=1em]% +\tikzstyle{tikzuml class style}=[rectangle split, rectangle split parts=3, rectangle split part align={center, left, left}, minimum height=2em, node distance=2em]% +\tikzstyle{tikzuml narynode style}=[diamond]% +\tikzstyle{tikzuml template style}=[dashed, inner ysep=0.5em, inner xsep=1ex]% +\tikzstyle{tikzuml control nodes style}=[fill=black, inner sep=1.5pt, circle]% +% +\tikzstyle{tikzuml association style}=[color=\tikzumlDefaultDrawColor, -]% +\tikzstyle{tikzuml bidirectional association style}=[color=\tikzumlDefaultDrawColor, angle45-angle45]% +\tikzstyle{tikzuml unidirectional association style}=[color=\tikzumlDefaultDrawColor, -angle 45]% +\tikzstyle{tikzuml aggregation style}=[color=\tikzumlDefaultDrawColor, open diamond-]% +\tikzstyle{tikzuml unidirectional aggregation style}=[color=\tikzumlDefaultDrawColor, open diamond-angle 45]% +\tikzstyle{tikzuml composition style}=[color=\tikzumlDefaultDrawColor, diamond-]% +\tikzstyle{tikzuml unidirectional composition style}=[color=\tikzumlDefaultDrawColor, diamond-angle 45]% +\tikzstyle{tikzuml nesting style}=[color=\tikzumlDefaultDrawColor]% +\tikzstyle{tikzuml dependency style}=[color=\tikzumlDefaultDrawColor, -angle 45, dashed]% +\tikzstyle{tikzuml import style}=[color=\tikzumlDefaultDrawColor, -angle 45, dashed]% +\tikzstyle{tikzuml inherit style}=[color=\tikzumlDefaultDrawColor, -open triangle 45]% +\tikzstyle{tikzuml implements style}=[color=\tikzumlDefaultDrawColor, -open triangle 45, dashed]% +% +\pgfkeys{/tikzuml/assemblyconnectorrelation/.cd, anchors/.style args={#1 and #2}{anchor1=#1, anchor2=#2}}% +% +\newcounter{tikzumlPackageClassNum}% +\newcounter{tikzumlPackageSubPackageNum}% +\newcounter{tikzumlRelationNum}% +\setcounter{tikzumlRelationNum}{1}% +\newcounter{tikzumlNoteNum}% +\setcounter{tikzumlNoteNum}{1}% +% +\newcounter{pos}% +\newcounter{posT}% +\newcounter{posStereo}% +% +\newcounter{tikzumlPackageLevel}% +\setcounter{tikzumlPackageLevel}{0}% +% +\newif\iftikzumlpackageSimpleStyle% +\newif\iftikzumlclassSimpleStyle% +\newif\iftikzumlclassCircleShape% +\newif\iftikzumlpackageWithoutCoords% +\newif\iftikzumlclassWithoutCoords% +\newif\iftikzumlassocclassWithoutCoords% +\newif\iftikzumlnoteWithoutCoords% +% +% define a uml package +% arg : package name +% optional : x, y: coordinates of the package +% type: stereotype of the package +% name: name of the package node +% draw, fill, text: colors +% style: to manage every default TikZ option +% no coords: to tell that the package position is defined relatively +% to another node (automatically used with TikZ options above, below, left, right, below left, ...) +\newenvironment{umlpackage}[2][]{% + \pgfkeys{/tikzuml/package/.cd, x/.initial=\tikzumlDefaultX, y/.initial=\tikzumlDefaultX, style/.style={},% + name/.initial=tikzumlEmpty, type/.initial=\tikzumlPackageDefaultType, draw/.initial=\tikzumlDefaultDrawColor,% + fill/.initial=\tikzumlPackageDefaultFillColor, text/.initial=\tikzumlDefaultTextColor,% + no coords/.is if=tikzumlpackageWithoutCoords,% + no coords=false,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{above}\OR% + \equal{\keyname}{left}\OR% + \equal{\keyname}{below}\OR% + \equal{\keyname}{right}\OR% + \equal{\keyname}{above left}\OR% + \equal{\keyname}{above right}\OR% + \equal{\keyname}{below left}\OR% + \equal{\keyname}{below right}}{% + \IfSubStr{\keyvalue}{ of }{% + \pgfkeys{/tikzuml/package/.cd, no coords}% + }{}% + }{}% + \ifx\keyvalue\pgfkeysnovalue% + \pgfkeys{/tikzuml/package/.cd, style/.append style/.expand once={\keyname}}% + \else% + \pgfkeys{/tikzuml/package/.cd, style/.append style/.expand twice={\expandafter\keyname\expandafter=\keyvalue}}% + \fi% + %\errmessage{TIKZUML ERROR : in umlpackage, invalid option \keyname}% + }% + }% + \pgfkeys{/tikzuml/package/.cd, #1}% + \pgfkeys{/tikzuml/package/.cd, x/.get=\tikzumlPackageXShift, y/.get=\tikzumlPackageYShift, name/.get=\tikzumlPackageName, type/.get=\tikzumlPackageTypeTmp,% + draw/.get=\tikzumlPackageDrawColor, fill/.get=\tikzumlPackageFillColor,% + text/.get=\tikzumlPackageTextColor% + }% + % + + % + \ifthenelse{\equal{\tikzumlPackageTypeTmp}{tikzumlEmpty}}{% + \def\tikzumlPackageType{}% + }{% + \expandafter\def\expandafter\tikzumlPackageType\expandafter{$\ll$\tikzumlPackageTypeTmp$\gg$ \\}% + }% + % + \ifnum\thetikzumlPackageLevel>0% + \let\tikzumlPackage@nameold\tikzumlPackage@fitname% + \def\tikzumlPackage@name{#2}% + \begingroup% + \def\_{@}\edef\x{\endgroup% + \def\noexpand\tikzumlPackage@fitname{\tikzumlPackage@name}}\x% + \let\tikzumlPackage@parentold\tikzumlPackage@parent% + \edef\tikzumlPackage@parent{\tikzumlPackage@parentold @@\tikzumlPackage@nameold}% + \else% + \def\tikzumlPackage@parent{}% + \def\tikzumlPackage@name{#2}% + \begingroup% + \def\_{@}\edef\x{\endgroup% + \def\noexpand\tikzumlPackage@fitname{\tikzumlPackage@name}}\x% + \fi% + % + \let\tikzumlPackage@nodeNameold\tikzumlPackage@nodeName% + % + \begingroup% + \def\_{_}\edef\x{\endgroup% + \def\noexpand\tikzumlPackage@nodeName{\tikzumlPackage@name}}\x% + % + \ifthenelse{\equal{\tikzumlPackageName}{tikzumlEmpty}}{}{% + \def\tikzumlPackage@nodeName{\tikzumlPackageName}% + }% + % + \StrSubstitute{\tikzumlPackage@nodeName}{.}{@POINT@}{\tikzumlPackage@nodeName}% + % + \expandafter\gdef\csname tikzumlPackageFit\tikzumlPackage@parent @@\tikzumlPackage@fitname\endcsname{}% + % + \setcounter{tikzumlPackageClassNum}{0}% + \setcounter{tikzumlPackageSubPackageNum}{0}% + \stepcounter{tikzumlPackageLevel}% + % + \begin{scope}[xshift=\tikzumlPackageXShift cm, yshift=\tikzumlPackageYShift cm]% +}{% + \addtocounter{tikzumlPackageLevel}{-1}% + \begin{pgfonlayer}{package\thetikzumlPackageLevel}% + % + % if contains no class, and not simple, one define a fictive node to enable the fit option + \ifnum\c@tikzumlPackageClassNum=0% + \ifnum\c@tikzumlPackageSubPackageNum=0% + \iftikzumlpackageWithoutCoords% + \node[inner sep=1.5ex, /tikzuml/package/style] (\tikzumlPackage@nodeName-root) {\phantom{\tikzumlPackage@nodeName}};% + \else% + \node[inner sep=1.5ex, /tikzuml/package/style] (\tikzumlPackage@nodeName-root) at (0,0) {\phantom{\tikzumlPackage@nodeName}};% + \fi% + \expandafter\xdef\csname tikzumlPackageFit\tikzumlPackage@parent @@\tikzumlPackage@fitname\endcsname{(\tikzumlPackage@nodeName-root)}% + \fi% + \fi% + % + \ifnum\c@tikzumlPackageLevel>0% + \def\tikzumlPackageFitTmp{\csname tikzumlPackageFit\tikzumlPackage@parent\endcsname}% + \expandafter\xdef\csname tikzumlPackageFit\tikzumlPackage@parent\endcsname{\tikzumlPackageFitTmp (\tikzumlPackage@nodeName) (\tikzumlPackage@nodeName-caption)}% + \stepcounter{tikzumlPackageSubPackageNum}% + \fi% + % + \node[draw=\tikzumlPackageDrawColor, fill=\tikzumlPackageFillColor, text=\tikzumlPackageTextColor, font=\tikzumlDefaultFont, inner sep=1.5ex, /tikzuml/package/style, fit = \csname tikzumlPackageFit\tikzumlPackage@parent @@\tikzumlPackage@fitname\endcsname] (\tikzumlPackage@nodeName) {};% + \node[draw=\tikzumlPackageDrawColor, fill=\tikzumlPackageFillColor, text=\tikzumlPackageTextColor, font=\tikzumlDefaultFont, minimum height=1.5em, outer ysep=-0.3, anchor=south west] (\tikzumlPackage@nodeName-caption) at (\tikzumlPackage@nodeName.north west) {\begin{tabular}{c} \tikzumlPackageType \textbf{\tikzumlPackage@name}\end{tabular}};% + \end{pgfonlayer}% + \end{scope}% +}% +% +% shortcut to define an empty package +\newcommand{\umlemptypackage}[2][]{\begin{umlpackage}[#1]{#2} \end{umlpackage}}% +% +% define a uml class +% args : name of the class +% attributes of the class +% operations of the class +% optional : x, y: coordinates of the class +% width: of the class node +% type: type of class (class, interface, typedef, enum) +% tags: tagged values of class +% template: template parameters +% simple: if used, class is empty and drawn with a rectangle +% circle: if used with simple, class is empty and drawn with a circle +% draw, fill, fill template, and text: colors +% style: to manage every default TikZ option +% no coords: to tell that the class position is defined relatively +% to another node (automatically used with TikZ options above, below, left, right, below left, ...) +\newcommand{\umlclass}[4][]{% + \pgfkeys{/tikzuml/class/.cd, x/.initial=\tikzumlDefaultX, y/.initial=\tikzumlDefaultX, width/.initial=\tikzumlClassDefaultWidth, type/.initial=\tikzumlClassDefaultType,% + tags/.initial={}, style/.style={},% + template/.initial={}, name/.initial=tikzumlEmpty,% + draw/.initial=\tikzumlDefaultDrawColor,% + fill template/.initial=\tikzumlClassTemplateFillColorDefaultFillColor,% + fill/.initial=\tikzumlClassDefaultFillColor,% + text/.initial=\tikzumlDefaultTextColor,% + simple/.is if=tikzumlclassSimpleStyle, circle/.is if=tikzumlclassCircleShape, no coords/.is if=tikzumlclassWithoutCoords,% + simple=false, circle=false, no coords=false,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{above}\OR% + \equal{\keyname}{left}\OR% + \equal{\keyname}{below}\OR% + \equal{\keyname}{right}\OR% + \equal{\keyname}{above left}\OR% + \equal{\keyname}{above right}\OR% + \equal{\keyname}{below left}\OR% + \equal{\keyname}{below right}}{% + \IfSubStr{\keyvalue}{ of }{% + \pgfkeys{/tikzuml/class/.cd, no coords}% + }{}% + }{}% + \ifx\keyvalue\pgfkeysnovalue% + \pgfkeys{/tikzuml/class/.cd, style/.append style/.expand once={\keyname}}% + \else% + \pgfkeys{/tikzuml/class/.cd, style/.append style/.expand twice={\expandafter\keyname\expandafter=\keyvalue}}% + \fi% + %\errmessage{TIKZUML ERROR : in umlclass, invalid option \keyname}% + }% + }% + \pgfkeys{/tikzuml/class/.cd,#1}% + % + \iftikzumlclassSimpleStyle% + \iftikzumlclassCircleShape% + \pgfkeys{/tikzuml/class/.cd, width/.initial=\tikzumlSimpleInterfaceDefaultWidth}% + \fi% + \fi% + % + \pgfkeys{/tikzuml/class/.cd, x/.get=\tikzumlClassX, y/.get=\tikzumlClassY, width/.get=\tikzumlClassMinimumWidth,% + type/.get=\tikzumlClassTypeTmp, tags/.get=\tikzumlClassTagsTmp, template/.get=\tikzumlClassTemplateFillColorParam,% + name/.get=\tikzumlClassName,% + draw/.get=\tikzumlClassDrawColor, fill/.get=\tikzumlClassFillColor,% + text/.get=\tikzumlClassTextColor, fill template/.get=\tikzumlClassTemplateFillColor% + }% + % + \ifthenelse{\equal{\tikzumlClassTypeTmp}{class}\OR\equal{\tikzumlClassTypeTmp}{abstract}}{% + \def\tikzumlClassType{}% + }{% + \expandafter\def\expandafter\tikzumlClassType\expandafter{$\ll$\tikzumlClassTypeTmp$\gg$ \\}% + }% + % + \ifthenelse{\equal{\tikzumlClassTagsTmp}{}}{% + \def\tikzumlClassTags{}% + }{% + \def\tikzumlClassTags{\\ \{\tikzumlClassTagsTmp\}}% + }% + % + \ifthenelse{\equal{\tikzumlClassTemplateFillColorParam}{}}{% + \def\tikzumlClassVPadding{}% + \def\tikzumlClassHPadding{}% + }{% + \def\tikzumlClassVPadding{\vspace{0.1em} \\}% + \def\tikzumlClassHPadding{\hspace{0.5ex} $ $}% + }% + % + \def\tikzumlClassName{#2}% + % + \begingroup% + \def\_{_}\edef\x{\endgroup% + \def\noexpand\tikzumlClassNodeName{\tikzumlClassName}}\x% + % + \ifthenelse{\equal{\tikzumlClassName}{tikzumlEmpty}}{}{% + \def\tikzumlClassNodeName{\tikzumlClassName}% + }% + % + \StrSubstitute{\tikzumlClassNodeName}{:}{@COLON@}[\tikzumlClassNodeName]% + \StrSubstitute{\tikzumlClassNodeName}{\_}{@UNDERSCORE@}[\tikzumlClassNodeName]% + % + \ifthenelse{\equal{\tikzumlClassTypeTmp}{abstract}}{% + \let\tikzumlClassNameOld\tikzumlClassName% + \def\tikzumlClassName{{\it \tikzumlClassNameOld}}% + }{}% + % + \def\tikzumlClassPos{\tikzumlClassX,\tikzumlClassY}% + \def\tikzumlClassAttributes{#3}% + \def\tikzumlClassOperations{#4}% + % + \iftikzumlclassSimpleStyle% + \iftikzumlclassWithoutCoords% + \iftikzumlclassCircleShape% + \node[tikzuml simpleinterface style, draw=\tikzumlClassDrawColor, fill=\tikzumlClassFillColor, text=\tikzumlClassTextColor, font=\tikzumlDefaultFont, minimum width=\tikzumlClassMinimumWidth, /tikzuml/class/style] (\tikzumlClassNodeName) {};% + \node[anchor=south] (\tikzumlClassNodeName-label) at (\tikzumlClassNodeName.north) {\begin{tabular}{c}\tikzumlClassVPadding \tikzumlClassType \tikzumlClassHPadding \textbf{\tikzumlClassName} \tikzumlClassHPadding \tikzumlClassTags \end{tabular}% + };% + \else% + \node[tikzuml simpleclass style, draw=\tikzumlClassDrawColor, fill=\tikzumlClassFillColor, text=\tikzumlClassTextColor, font=\tikzumlDefaultFont, minimum width=\tikzumlClassMinimumWidth, /tikzuml/class/style] (\tikzumlClassNodeName) {\begin{tabular}{c}\tikzumlClassVPadding \tikzumlClassType \tikzumlClassHPadding \textbf{\tikzumlClassName} \tikzumlClassHPadding \tikzumlClassTags \end{tabular}% + };% + \fi% + \else% + \iftikzumlclassCircleShape% + \node[tikzuml simpleinterface style, draw=\tikzumlClassDrawColor, fill=\tikzumlClassFillColor, text=\tikzumlClassTextColor, font=\tikzumlDefaultFont, minimum width=\tikzumlClassMinimumWidth, /tikzuml/class/style] (\tikzumlClassNodeName) at (\tikzumlClassPos) {}; + \node[anchor=south] (\tikzumlClassNodeName-label) at (\tikzumlClassNodeName.north){\begin{tabular}{c}\tikzumlClassVPadding \tikzumlClassType \tikzumlClassHPadding \textbf{\tikzumlClassName} \tikzumlClassHPadding \tikzumlClassTags \end{tabular}% + };% + \else% + \node[tikzuml simpleclass style, draw=\tikzumlClassDrawColor, fill=\tikzumlClassFillColor, text=\tikzumlClassTextColor, font=\tikzumlDefaultFont, minimum width=\tikzumlClassMinimumWidth, /tikzuml/class/style] (\tikzumlClassNodeName) at (\tikzumlClassPos) {\begin{tabular}{c}\tikzumlClassVPadding \tikzumlClassType \tikzumlClassHPadding \textbf{\tikzumlClassName} \tikzumlClassHPadding \tikzumlClassTags \end{tabular}% + };% + \fi% + \fi% + \else% + \iftikzumlclassWithoutCoords% + \node[tikzuml class style, draw=\tikzumlClassDrawColor, fill=\tikzumlClassFillColor, text=\tikzumlClassTextColor, font=\tikzumlDefaultFont, minimum width=\tikzumlClassMinimumWidth, /tikzuml/class/style] (\tikzumlClassNodeName) {\begin{tabular}{c}\tikzumlClassVPadding \tikzumlClassType \tikzumlClassHPadding \textbf{\tikzumlClassName} \tikzumlClassHPadding \tikzumlClassTags \end{tabular}% + \nodepart{second}% + \begin{tabular}{l}% + \tikzumlClassAttributes% + \end{tabular}% + \nodepart{third}% + \begin{tabular}{l}% + \tikzumlClassOperations% + \end{tabular}% + };% + \else% + \node[tikzuml class style, draw=\tikzumlClassDrawColor, fill=\tikzumlClassFillColor, text=\tikzumlClassTextColor, font=\tikzumlDefaultFont, minimum width=\tikzumlClassMinimumWidth, /tikzuml/class/style] (\tikzumlClassNodeName) at (\tikzumlClassPos) {\begin{tabular}{c}\tikzumlClassVPadding \tikzumlClassType \tikzumlClassHPadding \textbf{\tikzumlClassName} \tikzumlClassHPadding \tikzumlClassTags \end{tabular}% + \nodepart{second}% + \begin{tabular}{l}% + \tikzumlClassAttributes% + \end{tabular}% + \nodepart{third}% + \begin{tabular}{l}% + \tikzumlClassOperations% + \end{tabular}% + };% + \fi% + \fi% + % + \ifthenelse{\equal{\tikzumlClassTemplateFillColorParam}{}}{}{% + \draw (\tikzumlClassNodeName.north east) node[tikzuml template style, name=\tikzumlClassNodeName-template, draw=\tikzumlClassDrawColor, fill=\tikzumlClassTemplateFillColor, text=\tikzumlClassTextColor, font=\tikzumlDefaultFont] {\tikzumlClassTemplateFillColorParam};% + }% + % + % add to fit + \ifnum\c@tikzumlPackageLevel>0% + \edef\tikzumlPackageFitOld{\csname tikzumlPackageFit\tikzumlPackage@parent @@\tikzumlPackage@fitname\endcsname}% + \ifthenelse{\equal{\tikzumlClassTemplateFillColorParam}{}}{% + \expandafter\xdef\csname tikzumlPackageFit\tikzumlPackage@parent @@\tikzumlPackage@fitname\endcsname{\tikzumlPackageFitOld (\tikzumlClassNodeName)}% + }{% + \expandafter\xdef\csname tikzumlPackageFit\tikzumlPackage@parent @@\tikzumlPackage@fitname\endcsname{\tikzumlPackageFitOld (\tikzumlClassNodeName) (\tikzumlClassNodeName-template)}% + }% + \stepcounter{tikzumlPackageClassNum}% + \fi% + \ifnum\c@tikzumlComponentLevel>0% + \def\tikzumlComponentFitTmp{\csname tikzumlComponentFit\tikzumlComponent@parent @@\tikzumlComponent@fitname\endcsname}% + \ifthenelse{\equal{\tikzumlClassTemplateFillColorParam}{}}{% + \expandafter\xdef\csname tikzumlComponentFit\tikzumlComponent@parent @@\tikzumlComponent@fitname\endcsname{\tikzumlComponentFitTmp (\tikzumlClassNodeName)}% + }{% + \expandafter\xdef\csname tikzumlComponentFit\tikzumlComponent@parent @@\tikzumlComponent@fitname\endcsname{\tikzumlComponentFitTmp (\tikzumlClassNodeName) (\tikzumlClassNodeName-template)}% + }% + \stepcounter{tikzumlComponentSubComponentNum}% + \fi% +}% +% +% shortcuts for interface, enum and typedef environments +\newcommand{\umlabstract}[4][]{% + \pgfkeys{/tikzuml/class/.cd, .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{type}}{% + \errmessage{TIKZUML ERROR : in umlabstract, forbidden option type}% + }{}% + }% + }% + \pgfkeys{/tikzuml/class/.cd, #1}% + \umlclass[type=abstract,#1]{#2}{#3}{#4}% +}% +\newcommand{\umlinterface}[4][]{% + \pgfkeys{/tikzuml/class/.cd, .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{type}}{% + \errmessage{TIKZUML ERROR : in umlinterface, forbidden option type}% + }{}% + }% + }% + \pgfkeys{/tikzuml/class/.cd, #1}% + \umlclass[type=interface,#1]{#2}{#3}{#4}% +}% +\newcommand{\umltypedef}[4][]{% + \pgfkeys{/tikzuml/class/.cd, .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{type}}{% + \errmessage{TIKZUML ERROR : in umltypedef, forbidden option type}% + }{}% + }% + }% + \pgfkeys{/tikzuml/class/.cd, #1}% + \umlclass[type=typedef,#1]{#2}{#3}{#4}% +}% +\newcommand{\umlenum}[4][]{% + \pgfkeys{/tikzuml/class/.cd, .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{type}}{% + \errmessage{TIKZUML ERROR : in umlenum, forbidden option type}% + }{}% + }% + }% + \pgfkeys{/tikzuml/class/.cd, #1}% + \umlclass[type=enum,#1]{#2}{#3}{#4} +}% +% +% shortcut to define an empty class +\newcommand{\umlemptyclass}[2][]{\umlclass[#1]{#2}{}{}}% +\newcommand{\umlsimpleclass}[2][]{% + \pgfkeys{/tikzuml/class/.cd, .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{simple}}{% + \errmessage{TIKZUML ERROR : in umlsimpleclass, forbidden option simple}% + }{}% + }% + }% + \pgfkeys{/tikzuml/class/.cd, #1}% + \umlemptyclass[simple, #1]{#2}% +}% +% +\newcommand{\umlsimpleinterface}[2][]{% + \pgfkeys{/tikzuml/class/.cd, .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{simple}}{% + \errmessage{TIKZUML ERROR : in umlsimpleinterface, forbidden option simple}% + }{% + \ifthenelse{\equal{\keyname}{circle}}{% + \errmessage{TIKZUML ERROR : in umlsimpleinterface, forbidden option circle}% + }{}% + }% + }% + }% + \pgfkeys{/tikzuml/class/.cd, #1}% + \umlsimpleclass[circle, #1]{#2}% +}% +% underline the text for static arg +\newcommand{\umlstatic}[1]{\underline{#1}}% +\newcommand{\umlvirt}[1]{\textit{#1}}% +% +% define node for n-ary association +\newcommand{\umlNarynode}[2][]{% + \def\tikzumlNaryNodeAnchor{.north} + \def\tikzumlNaryNodeLabelPos{above} + \pgfkeys{/tikzuml/narynode/.cd, x/.initial=\tikzumlDefaultX, y/.initial=\tikzumlDefaultX, width/.initial=\tikzumlNaryNodeDefaultWidth, name/.initial=tikzumlEmpty,% + draw/.initial=\tikzumlDefaultDrawColor, fill/.initial=\tikzumlClassDefaultFillColor,% + text/.initial=\tikzumlDefaultTextColor, style/.style={},% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{above}}{% + \def\tikzumlNaryNodeAnchor{.north}% + \def\tikzumlNaryNodeLabelPos{above}% + }{% + \ifthenelse{\equal{\keyname}{above left}}{% + \def\tikzumlNaryNodeAnchor{.north west}% + \def\tikzumlNaryNodeLabelPos{above left}% + }{% + \ifthenelse{\equal{\keyname}{left}}{% + \def\tikzumlNaryNodeAnchor{.west}% + \def\tikzumlNaryNodeLabelPos{left}% + }{% + \ifthenelse{\equal{\keyname}{below left}}{% + \def\tikzumlNaryNodeAnchor{.south west}% + \def\tikzumlNaryNodeLabelPos{below left}% + }{% + \ifthenelse{\equal{\keyname}{below}}{% + \def\tikzumlNaryNodeAnchor{.south}% + \def\tikzumlNaryNodeLabelPos{below}% + }{% + \ifthenelse{\equal{\keyname}{below right}}{% + \def\tikzumlNaryNodeAnchor{.south east}% + \def\tikzumlNaryNodeLabelPos{below right}% + }{% + \ifthenelse{\equal{\keyname}{right}}{% + \def\tikzumlNaryNodeAnchor{.east}% + \def\tikzumlNaryNodeLabelPos{right}% + }{% + \ifthenelse{\equal{\keyname}{above right}}{% + \def\tikzumlNaryNodeAnchor{.north east}% + \def\tikzumlNaryNodeLabelPos{above right}% + }{% + \ifx\keyvalue\pgfkeysnovalue% + \pgfkeys{/tikzuml/narynode/.cd, style/.append style/.expand once={\keyname}}% + \else% + \pgfkeys{/tikzuml/narynode/.cd, style/.append style/.expand twice={\expandafter\keyname\expandafter=\keyvalue}}% + \fi% + %\errmessage{TIKZUML ERROR : in umlNarynode, invalid option \keyname}% + }% + }% + }% + }% + }% + }% + }% + }% + }% + }% + \pgfkeys{/tikzuml/narynode/.cd,#1}% + \pgfkeys{/tikzuml/narynode/.cd, x/.get=\tikzumlNaryNodeX, y/.get=\tikzumlNaryNodeY, width/.get=\tikzumlNaryNodeMinimumWidth,% + name/.get=\tikzumlNaryNodeName,% + draw/.get=\tikzumlNaryNodeDrawColor, fill/.get=\tikzumlNaryNodeFillColor,% + text/.get=\tikzumlNaryNodeTextColor% + }% + % + \def\tikzumlNaryName{#2}% + % + \ifthenelse{\equal{\tikzumlNaryNodeName}{tikzumlEmpty}}{% + \edef\tikzumlNaryNodeName{\tikzumlNaryName}% + }{% + \edef\tikzumlNaryNodeName{\tikzumlNaryNodeName}% + }% + % + \begingroup% + \def\_{_}\edef\x{\endgroup% + \def\noexpand\tikzumlNaryNode@nodeName{\tikzumlNaryNodeName}}\x% + % + \StrSubstitute{\tikzumlNaryNode@nodeName}{:}{@COLON@}[\tikzumlNaryNode@nodeName]% + \StrSubstitute{\tikzumlNaryNode@nodeName}{\_}{@UNDERSCORE@}[\tikzumlNaryNode@nodeName]% + % + \def\tikzumlNarynodePos{\tikzumlNaryNodeX,\tikzumlNaryNodeY}% + % + \node[tikzuml narynode style, draw=\tikzumlNaryNodeDrawColor, fill=\tikzumlNaryNodeFillColor, text=\tikzumlNaryNodeTextColor, font=\tikzumlDefaultFont, minimum width=\tikzumlNaryNodeMinimumWidth, minimum height=\tikzumlNaryNodeMinimumWidth, /tikzuml/narynode/style] (\tikzumlNaryNode@nodeName) at (\tikzumlNarynodePos) {};% + \draw (\tikzumlNaryNode@nodeName\tikzumlNaryNodeAnchor) node[\tikzumlNaryNodeLabelPos] {\tikzumlNaryName};% + % + % add to fit + \ifnum\c@tikzumlPackageLevel>0% + \edef\tikzumlPackageFitOld{\csname tikzumlPackageFit\tikzumlPackage@parent @@\tikzumlPackage@fitname\endcsname}% + \expandafter\xdef\csname tikzumlPackageFit\tikzumlPackage@parent @@\tikzumlPackage@fitname\endcsname{\tikzumlPackageFitOld (\tikzumlNaryNode@nodeName)}% + \stepcounter{tikzumlPackageClassNum}% + \fi% +}% +% +\newdimen\tikzumlNestingSymbolSize% +% +% main command to define a relation between two classes +% args : src class +% dest class +% optional : geometry: geometry of the line +% weight: barycentric weight of the middle part when geometry is a 3-line +% arm1, arm2: lengths of first or last part when geometry id a 3-line +% arg1, arg2, arg: name of the src/dest/dest side class type attribute defined by the relation +% mult1, mult2, mult: multiplicity of the src/dest/dest side class type attribute defined by the relation +% pos1, pos2, pos: position of the src/dest/dest side class type attribute defined by the relation +% align1, align2, align: text justification of the src/dest/dest side class type attribute defined by the relation +% anchor1, anchor2: src/dest anchors on linked classes +% angle1, angle2, loopsize: start angle, end angle and size of the relation (only if recursive) +% stereo: stereotype of the relation +% pos stereo: position of the stereotype on the relation +% style: style of the relation (association, aggregation, composition, inherit, ...) +% name: rootname used for naming nodes of the relation +% recursive mode: type of recursive arrow (transition for state diagrams, or default) +% recursive direction start/end: when transition relation, start/end directions of the relation arrow +\newcommand{\umlrelation}[3][]{% + \pgfkeys{/tikzuml/relation/.cd, geometry/.initial=\tikzumlRelationDefaultGeometry, weight/.initial=\tikzumlRelationDefaultWeight,% + arm1/.initial=auto, arm2/.initial=auto,% + arg1/.initial={}, arg2/.initial={}, arg/.initial={},% + mult1/.initial={}, mult2/.initial={}, mult/.initial={},% + pos1/.initial=\tikzumlRelationDefaultPosO, pos2/.initial=\tikzumlRelationDefaultPosT, pos/.initial=tikzumlEmpty,% + align1/.initial={}, align2/.initial={}, align/.initial={},% + anchor1/.initial=tikzumlEmpty, anchor2/.initial=tikzumlEmpty,% + angle1/.initial=\tikzumlRelationDefaultAngleO, angle2/.initial=\tikzumlRelationDefaultAngleT, loopsize/.initial=\tikzumlRelationDefaultLoopSize,% + stereo/.initial={}, pos stereo/.initial=\tikzumlRelationDefaultPosStereo,% + style/.initial=->, style2/.style={}, name/.initial=relation-\thetikzumlRelationNum,% + recursive mode/.initial=default, recursive direction start/.initial=right,% + recursive direction end/.initial=bottom,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{with port}% + \OR\equal{\keyname}{interface}% + \OR\equal{\keyname}{padding}% + \OR\equal{\keyname}{width}% + \OR\equal{\keyname}{first arm}% + \OR\equal{\keyname}{second arm}% + \OR\equal{\keyname}{middle arm}% + \OR\equal{\keyname}{last arm}% + \OR\equal{\keyname}{distance}}{}{% + \ifx\keyvalue\pgfkeysnovalue% + \pgfkeys{/tikzuml/relation/.cd, style2/.append style/.expand once={\keyname}}% + \else% + \pgfkeys{/tikzuml/relation/.cd, style2/.append style/.expand twice={\expandafter\keyname\expandafter=\keyvalue}}% + \fi% + %\errmessage{TIKZUML ERROR : in umlrelation, invalid option \keyname}% + }% + }% + }% + \pgfkeys{/tikzuml/relation/.cd,#1}% + \pgfkeys{/tikzuml/relation/.cd, geometry/.get=\tikzumlRelationGeometry, weight/.get=\tikzumlRelationWeight,% + arm1/.get=\tikzumlRelationArmO, arm2/.get=\tikzumlRelationArmT,% + arg1/.get=\tikzumlRelationAttrName, arg2/.get=\tikzumlRelationAttrNameTO, arg/.get=\tikzumlRelationAttrNameTT,% + mult1/.get=\tikzumlRelationMultiplicity, mult2/.get=\tikzumlRelationMultiplicityTO, mult/.get=\tikzumlRelationMultiplicityTT,% + pos1/.get=\tikzumlRelationPosition, pos2/.get=\tikzumlRelationPositionTO, pos/.get=\tikzumlRelationPositionTT,% + align1/.get=\tikzumlRelationAlign, align2/.get=\tikzumlRelationAlignTO, align/.get=\tikzumlRelationAlignTT,% + anchor1/.get=\tikzumlRelationSrcAnchor, anchor2/.get=\tikzumlRelationDestAnchor,% + angle1/.get=\tikzumlRelationStartAngle, angle2/.get=\tikzumlRelationEndAngle, loopsize/.get=\tikzumlRelationLoopSize,% + stereo/.get=\tikzumlRelationStereoType, pos stereo/.get=\tikzumlRelationPositionStereotype,% + style/.get=\tikzumlRelationStyle, name/.get=\tikzumlRelationName,% + recursive mode/.get=\tikzumlRelationRecursiveMode,% + recursive direction start/.get=\tikzumlRelationRecursiveDirectionStart,% + recursive direction end/.get=\tikzumlRelationRecursiveDirectionEnd% + }% + % + \def\tikzumlSrcClassName{#2}% + % + % managing \_ in class names for node names + \begingroup% + \def\_{_}\edef\x{\endgroup% + \def\noexpand\tikzumlSrcClassNodeName{\tikzumlSrcClassName}}\x% + % + \StrSubstitute{\tikzumlSrcClassNodeName}{:}{@COLON@}[\tikzumlSrcClassNodeName]% + \StrSubstitute{\tikzumlSrcClassNodeName}{\_}{@UNDERSCORE@}[\tikzumlSrcClassNodeName]% + % + \def\tikzumlDestClassName{#3}% + \begingroup% + \def\_{_}\edef\x{\endgroup% + \def\noexpand\tikzumlDestClassNodeName{\tikzumlDestClassName}}\x% + % + \StrSubstitute{\tikzumlDestClassNodeName}{:}{@COLON@}[\tikzumlDestClassNodeName]% + \StrSubstitute{\tikzumlDestClassNodeName}{\_}{@UNDERSCORE@}[\tikzumlDestClassNodeName]% + % + % managing alias keys + \def\tikzumlRelationAttrNameT{\tikzumlRelationAttrNameTO\tikzumlRelationAttrNameTT}% + \def\tikzumlRelationMultiplicityT{\tikzumlRelationMultiplicityTO\tikzumlRelationMultiplicityTT}% + \def\tikzumlRelationAlignT{\tikzumlRelationAlignTO\tikzumlRelationAlignTT}% + \def\posAttrName{}% + \def\posMultiplicity{}% + \def\posAttrNameT{}% + \def\posMultiplicityT{}% + % + \ifthenelse{\equal{\tikzumlRelationPositionTT}{tikzumlEmpty}}{% + \def\tikzumlRelationPositionT{\tikzumlRelationPositionTO}% + }{% + \def\tikzumlRelationPositionT{\tikzumlRelationPositionTT}% + }% + % + \def\attrAlign{}% + \def\multAlign{}% + \def\attrAlignT{}% + \def\multAlignT{}% + % + \ifthenelse{\equal{\tikzumlRelationAlign}{left}}{% + \def\attrAlign{above right}% + \def\multAlign{below right}% + }{% + \ifthenelse{\equal{\tikzumlRelationAlign}{right}}{% + \def\attrAlign{above left}% + \def\multAlign{below left}% + }{}% + }% + % + \ifthenelse{\equal{\tikzumlRelationAlignT}{left}}{% + \def\attrAlignT{above right}% + \def\multAlignT{below right}% + }{% + \ifthenelse{\equal{\tikzumlRelationAlignT}{right}}{% + \def\attrAlignT{above left}% + \def\multAlignT{below left}% + }{}% + }% + % + % def stereotype + \ifthenelse{\equal{\tikzumlRelationStereoType}{}}{% + \def\stereotype{}% + }{% + \def\stereotype{$\ll$\tikzumlRelationStereoType$\gg$}% + }% + + % def anchors macros + \ifthenelse{\equal{\tikzumlRelationSrcAnchor}{tikzumlEmpty}}{% + \def\tikzumlRelationSrcAnchor{}% + }{% + \let\tikzumlRelationSrcAnchorold\tikzumlRelationSrcAnchor% + \def\tikzumlRelationSrcAnchor{.\tikzumlRelationSrcAnchorold}% + }% + % + \ifthenelse{\equal{\tikzumlRelationDestAnchor}{tikzumlEmpty}}{% + \def\tikzumlRelationDestAnchor{}% + }{% + \let\tikzumlRelationDestAnchorold\tikzumlRelationDestAnchor% + \def\tikzumlRelationDestAnchor{.\tikzumlRelationDestAnchorold}% + }% + % + \setcounter{pos}{100*\real{\tikzumlRelationPosition}}% + \setcounter{posT}{100*\real{\tikzumlRelationPositionT}}% + \setcounter{posStereo}{100*\real{\tikzumlRelationPositionStereotype}}% + % + \pgfmathsetmacro{\tikzumlRelationWeightT}{1.0-\tikzumlRelationWeight}% + % + %\newcounter{tikzumlControlNodesNum}% + %\setcounter{tikzumlControlNodesNum}{0}% + % + \node[inner sep=0] (\tikzumlRelationName-middle) at (barycentric cs:\tikzumlSrcClassNodeName=\tikzumlRelationWeightT,\tikzumlDestClassNodeName=\tikzumlRelationWeight) {};% + % + % straight line + \ifthenelse{\equal{\tikzumlRelationGeometry}{--}}% + {% + \ifthenelse{\equal{\tikzumlSrcClassNodeName}{\tikzumlDestClassNodeName}}{% + \def\arcNum{1}% + \def\arcNumT{1}% + % + \ifthenelse{\equal{\tikzumlRelationRecursiveMode}{default}}{% + \xdef\tikzumlLastArc{node[pos=\tikzumlRelationPosition, \posAttrName, \attrAlign] {\tikzumlRelationAttrName}% + node[pos=\tikzumlRelationPosition, swap, \posMultiplicity, \multAlign] {\tikzumlRelationMultiplicity}% + node[pos=\tikzumlRelationPositionT, \posAttrNameT, \attrAlignT] {\tikzumlRelationAttrNameT}% + node[pos=\tikzumlRelationPositionT, swap, \posMultiplicityT, \multAlignT] {\tikzumlRelationMultiplicityT}% + node[pos=\tikzumlRelationPositionStereotype, anchor=center] {\stereotype} }% + \xdef\tikzumlPath{(\tikzumlSrcClassNodeName) edge[in=\tikzumlRelationEndAngle, out=\tikzumlRelationStartAngle, distance=\tikzumlRelationLoopSize] \tikzumlLastArc% + node[midway, inner sep=0, name=\tikzumlRelationName-1, anchor=center] {} (\tikzumlDestClassNodeName) }% + }{% + \ifthenelse{\equal{\tikzumlRelationRecursiveMode}{transition}}{% + \xdef\tikzumlFirstArc{node[midway, inner sep=0, name=\tikzumlRelationName-1, anchor=center] {}}% + \xdef\tikzumlMidOneArc{node[midway, inner sep=0, name=\tikzumlRelationName-3, anchor=center] {}}% + % + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionStart}{\tikzumlRelationRecursiveDirectionEnd}}{% + \def\numArcs{3}% + \xdef\tikzumlLastArc{node[midway, inner sep=0, name=\tikzumlRelationName-5, anchor=center] {}}% + % + \begin{pgfonlayer}{connections}% + \draw (\tikzumlSrcClassNodeName) edge[in=\tikzumlRelationEndAngle, out=\tikzumlRelationStartAngle, distance=\tikzumlRelationLoopSize, draw=none] % + node[midway, inner sep=0, name=\tikzumlRelationName-tmp, anchor=center] {} (\tikzumlDestClassNodeName);% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionStart}{right}\OR\equal{\tikzumlRelationRecursiveDirectionStart}{left}}{% + \node[inner sep=0, name=\tikzumlRelationName-2] at (\tikzumlSrcClassNodeName.\tikzumlRelationStartAngle -| \tikzumlRelationName-tmp) {};% + \node[inner sep=0, name=\tikzumlRelationName-4] at (\tikzumlDestClassNodeName.\tikzumlRelationEndAngle -| \tikzumlRelationName-tmp) {};% + }{% + \node[inner sep=0, name=\tikzumlRelationName-2] at (\tikzumlSrcClassNodeName.\tikzumlRelationStartAngle |- \tikzumlRelationName-tmp) {};% + \node[inner sep=0, name=\tikzumlRelationName-4] at (\tikzumlDestClassNodeName.\tikzumlRelationEndAngle |- \tikzumlRelationName-tmp) {};% + }% + \end{pgfonlayer}% + }{% + \def\numArcs{4}% + \xdef\tikzumlMidTwoArc{node[midway, inner sep=0, name=\tikzumlRelationName-5, anchor=center] {}}% + \xdef\tikzumlLastArc{node[midway, inner sep=0, name=\tikzumlRelationName-7, anchor=center] {}}% + % + \begin{pgfonlayer}{connections}% + \draw (\tikzumlSrcClassNodeName) edge[in=\tikzumlRelationEndAngle, out=\tikzumlRelationStartAngle, distance=\tikzumlRelationLoopSize, draw=none] % + node[midway, name=\tikzumlRelationName-4, anchor=center] {} (\tikzumlDestClassNodeName);% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionStart}{right}\OR\equal{\tikzumlRelationRecursiveDirectionStart}{left}}{% + \node[inner sep=0, name=\tikzumlRelationName-2] at (\tikzumlSrcClassNodeName.\tikzumlRelationStartAngle -| \tikzumlRelationName-4) {};% + \node[inner sep=0, name=\tikzumlRelationName-6] at (\tikzumlDestClassNodeName.\tikzumlRelationEndAngle |- \tikzumlRelationName-4) {};% + }{% + \node[inner sep=0, name=\tikzumlRelationName-2] at (\tikzumlSrcClassNodeName.\tikzumlRelationStartAngle |- \tikzumlRelationName-4) {};% + \node[inner sep=0, name=\tikzumlRelationName-6] at (\tikzumlDestClassNodeName.\tikzumlRelationEndAngle -| \tikzumlRelationName-4) {};% + }% + \end{pgfonlayer}% + }% + % + \ifnum\numArcs=4% + \ifnum\theposStereo>300% + \pgfmathsetmacro{\tikzumlRelationPositionStereotype}{(\theposStereo-300)/100}% + \xdef\tikzumlLastArc{\tikzumlLastArc node[pos=\tikzumlRelationPositionStereotype, anchor=center] {\stereotype}}% + \else% + \ifnum\theposStereo<100% + \xdef\tikzumlFirstArc{\tikzumlFirstArc node[pos=\tikzumlRelationPositionStereotype, anchor=center] {\stereotype}}% + \else% + \ifnum\theposStereo>200% + \pgfmathsetmacro{\tikzumlRelationPositionStereotype}{(\theposStereo-200)/100}% + \xdef\tikzumlMidTwoArc{\tikzumlMidTwoArc node[pos=\tikzumlRelationPositionStereotype, anchor=center] {\stereotype}}% + \else% + \pgfmathsetmacro{\tikzumlRelationPositionStereotype}{(\theposStereo-100)/100}% + \xdef\tikzumlMidOneArc{\tikzumlMidOneArc node[pos=\tikzumlRelationPositionStereotype, anchor=center] {\stereotype}}% + \fi% + \fi% + \fi% + % + \ifthenelse{\thepos=300\OR\thepos=100}{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionStart}{right}}{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionEnd}{bottom}}{% + \def\posAttrName{above right}% + \def\posMultiplicity{below left}% + }{% + \def\posAttrName{above left}% + \def\posMultiplicity{below right}% + }% + }{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionStart}{left}}{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionEnd}{bottom}}{% + \def\posAttrName{above left}% + \def\posMultiplicity{below right}% + }{% + \def\posAttrName{above right}% + \def\posMultiplicity{below left}% + }% + }{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionStart}{top}}{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionEnd}{left}}{% + \def\posAttrName{above right}% + \def\posMultiplicity{below left}% + }{% + \def\posAttrName{above left}% + \def\posMultiplicity{below right}% + }% + }{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionEnd}{left}}{% + \def\posAttrName{above left}% + \def\posMultiplicity{below right}% + }{% + \def\posAttrName{above right}% + \def\posMultiplicity{below left}% + }% + }% + }% + }% + }{}% + % + \ifthenelse{\thepos=200}{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionStart}{right}}{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionEnd}{bottom}}{% + \def\posAttrName{above left}% + \def\posMultiplicity{below right}% + }{% + \def\posAttrName{above right}% + \def\posMultiplicity{below left}% + }% + }{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionStart}{left}}{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionEnd}{bottom}}{% + \def\posAttrName{above right}% + \def\posMultiplicity{below left}% + }{% + \def\posAttrName{above left}% + \def\posMultiplicity{below right}% + }% + }{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionStart}{top}}{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionEnd}{left}}{% + \def\posAttrName{above left}% + \def\posMultiplicity{below right}% + }{% + \def\posAttrName{above right}% + \def\posMultiplicity{below left}% + }% + }{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionEnd}{left}}{% + \def\posAttrName{above right}% + \def\posMultiplicity{below left}% + }{% + \def\posAttrName{above left}% + \def\posMultiplicity{below right}% + }% + }% + }% + }% + }{}% + % + \ifnum\thepos>300% + \pgfmathsetmacro{\tikzumlRelationPosition}{(\thepos-300)/100}% + \xdef\tikzumlLastArc{\tikzumlLastArc node[pos=\tikzumlRelationPosition, \posAttrName, \attrAlign] {\tikzumlRelationAttrName}% + node[pos=\tikzumlRelationPosition, swap, \posMultiplicity, \multAlign] {\tikzumlRelationMultiplicity}% + }% + \else% + \ifnum\thepos<100% + \xdef\tikzumlFirstArc{\tikzumlFirstArc node[pos=\tikzumlRelationPosition, \posAttrName, \attrAlign] {\tikzumlRelationAttrName}% + node[pos=\tikzumlRelationPosition, swap, \posMultiplicity, \multAlign] {\tikzumlRelationMultiplicity}% + }% + \else% + \ifnum\thepos>200% + \pgfmathsetmacro{\tikzumlRelationPosition}{(\thepos-200)/100}% + \xdef\tikzumlMidTwoArc{\tikzumlMidTwoArc node[pos=\tikzumlRelationPosition, \posAttrName, \attrAlign] {\tikzumlRelationAttrName}% + node[pos=\tikzumlRelationPosition, swap, \posMultiplicity, \multAlign] {\tikzumlRelationMultiplicity}% + }% + \else% + \pgfmathsetmacro{\tikzumlRelationPosition}{(\thepos-100)/100}% + \xdef\tikzumlMidOneArc{\tikzumlMidOneArc node[pos=\tikzumlRelationPosition, \posAttrName, \attrAlign] {\tikzumlRelationAttrName}% + node[pos=\tikzumlRelationPosition, swap, \posMultiplicity, \multAlign] {\tikzumlRelationMultiplicity}% + }% + \fi% + \fi% + \fi% + % + \ifthenelse{\theposT=300\OR\theposT=100}{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionStart}{right}}{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionEnd}{bottom}}{% + \def\posAttrNameT{above right}% + \def\posMultiplicityT{below left}% + }{% + \def\posAttrNameT{above left}% + \def\posMultiplicityT{below right}% + }% + }{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionStart}{left}}{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionEnd}{bottom}}{% + \def\posAttrNameT{above left}% + \def\posMultiplicityT{below right}% + }{% + \def\posAttrNameT{above right}% + \def\posMultiplicityT{below left}% + }% + }{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionStart}{top}}{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionEnd}{left}}{% + \def\posAttrNameT{above right}% + \def\posMultiplicityT{below left}% + }{% + \def\posAttrNameT{above left}% + \def\posMultiplicityT{below right}% + }% + }{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionEnd}{left}}{% + \def\posAttrNameT{above left}% + \def\posMultiplicityT{below right}% + }{% + \def\posAttrNameT{above right}% + \def\posMultiplicityT{below left}% + }% + }% + }% + }% + }{}% + \ifthenelse{\theposT=200}{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionStart}{right}}{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionEnd}{bottom}}{% + \def\posAttrNameT{above left}% + \def\posMultiplicity{below right}% + }{% + \def\posAttrNameT{above right}% + \def\posMultiplicityT{below left}% + }% + }{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionStart}{left}}{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionEnd}{bottom}}{% + \def\posAttrNameT{above right}% + \def\posMultiplicityT{below left}% + }{% + \def\posAttrNameT{above left}% + \def\posMultiplicityT{below right}% + }% + }{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionStart}{top}}{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionEnd}{left}}{% + \def\posAttrNameT{above left}% + \def\posMultiplicityT{below right}% + }{% + \def\posAttrNameT{above right}% + \def\posMultiplicityT{below left}% + }% + }{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionEnd}{left}}{% + \def\posAttrNameT{above right}% + \def\posMultiplicityT{below left}% + }{% + \def\posAttrNameT{above left}% + \def\posMultiplicityT{below right}% + }% + }% + }% + }% + }{}% + % + \ifnum\theposT>300% + \pgfmathsetmacro{\tikzumlRelationPositionT}{(\theposT-300)/100}% + \xdef\tikzumlLastArc{\tikzumlLastArc node[pos=\tikzumlRelationPositionT, \posAttrNameT, \attrAlignT] {\tikzumlRelationAttrNameT}% + node[pos=\tikzumlRelationPositionT, swap, \posMultiplicityT, \multAlignT] {\tikzumlRelationMultiplicityT}% + }% + \else% + \ifnum\theposT<100% + \xdef\tikzumlFirstArc{\tikzumlFirstArc node[pos=\tikzumlRelationPositionT, \posAttrNameT, \attrAlignT] {\tikzumlRelationAttrNameT}% + node[pos=\tikzumlRelationPositionT, swap, \posMultiplicityT, \multAlignT] {\tikzumlRelationMultiplicityT}% + }% + \else% + \ifnum\theposT>200% + \pgfmathsetmacro{\tikzumlRelationPositionT}{(\theposT-200)/100}% + \xdef\tikzumlMidTwoArc{\tikzumlMidTwoArc node[pos=\tikzumlRelationPositionT, \posAttrNameT, \attrAlignT] {\tikzumlRelationAttrNameT}% + node[pos=\tikzumlRelationPositionT, swap, \posMultiplicityT, \multAlignT] {\tikzumlRelationMultiplicityT}% + }% + \else% + \pgfmathsetmacro{\tikzumlRelationPositionT}{(\theposT-100)/100}% + \xdef\tikzumlMidOneArc{\tikzumlMidOneArc node[pos=\tikzumlRelationPositionT, \posAttrNameT, \attrAlignT] {\tikzumlRelationAttrNameT}% + node[pos=\tikzumlRelationPositionT, swap, \posMultiplicityT, \multAlignT] {\tikzumlRelationMultiplicityT}% + }% + \fi% + \fi% + \fi% + \else% + \ifnum\theposStereo>200% + \pgfmathsetmacro{\tikzumlRelationPositionStereotype}{(\theposStereo-200)/100}% + \xdef\tikzumlLastArc{\tikzumlLastArc node[pos=\tikzumlRelationPositionStereotype, anchor=center] {\stereotype} }% + \else% + \ifnum\theposStereo<100% + \xdef\tikzumlFirstArc{\tikzumlFirstArc node[pos=\tikzumlRelationPositionStereotype, anchor=center] {\stereotype} }% + \else% + \pgfmathsetmacro{\tikzumlRelationPositionStereotype}{(\theposStereo-100)/100}% + \xdef\tikzumlMidOneArc{\tikzumlMidOneArc node[pos=\tikzumlRelationPositionStereotype, anchor=center] {\stereotype} }% + \fi% + \fi% + % + \ifthenelse{\thepos=100}{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionStart}{right}}{% + \ifthenelse{\tikzumlRelationEndAngle<\tikzumlRelationStartAngle}{% + \def\posAttrName{above right}% + \def\posMultiplicity{below left}% + }{% + \def\posAttrName{above left}% + \def\posMultiplicity{below right}% + }% + }{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionStart}{left}}{% + \ifthenelse{\tikzumlRelationEndAngle<\tikzumlRelationStartAngle}{% + \def\posAttrName{above left}% + \def\posMultiplicity{below right}% + }{% + \def\posAttrName{above right}% + \def\posMultiplicity{below left}% + }% + }{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionStart}{top}}{% + \ifthenelse{\tikzumlRelationEndAngle<\tikzumlRelationStartAngle}{% + \def\posAttrName{above right}% + \def\posMultiplicity{below left}% + }{% + \def\posAttrName{above left}% + \def\posMultiplicity{below right}% + }% + }{% + \ifthenelse{\tikzumlRelationEndAngle<\tikzumlRelationStartAngle}{% + \def\posAttrName{above left}% + \def\posMultiplicity{below right}% + }{% + \def\posAttrName{above right}% + \def\posMultiplicity{below left}% + }% + }% + }% + }% + }{}% + % + \ifthenelse{\thepos=200}{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionStart}{right}}{% + \ifthenelse{\tikzumlRelationEndAngle<\tikzumlRelationStartAngle}{% + \def\posAttrName{above left}% + \def\posMultiplicity{below right}% + }{% + \def\posAttrName{above right}% + \def\posMultiplicity{below left}% + }% + }{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionStart}{left}}{% + \ifthenelse{\tikzumlRelationEndAngle<\tikzumlRelationStartAngle}{% + \def\posAttrName{above right}% + \def\posMultiplicity{below left}% + }{% + \def\posAttrName{above left}% + \def\posMultiplicity{below right}% + }% + }{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionStart}{top}}{% + \ifthenelse{\tikzumlRelationEndAngle<\tikzumlRelationStartAngle}{% + \def\posAttrName{above left}% + \def\posMultiplicity{below right}% + }{% + \def\posAttrName{above right}% + \def\posMultiplicity{below left}% + }% + }{% + \ifthenelse{\tikzumlRelationEndAngle<\tikzumlRelationStartAngle}{% + \def\posAttrName{above right}% + \def\posMultiplicity{below left}% + }{% + \def\posAttrName{above left}% + \def\posMultiplicity{below right}% + }% + }% + }% + }% + }{}% + % + \ifnum\thepos>200% + \pgfmathsetmacro{\tikzumlRelationPosition}{(\thepos-200)/100}% + \xdef\tikzumlLastArc{\tikzumlLastArc node[pos=\tikzumlRelationPosition, \posAttrName, \attrAlign] {\tikzumlRelationAttrName}% + node[pos=\tikzumlRelationPosition, swap, \posMultiplicity, \multAlign] {\tikzumlRelationMultiplicity}% + }% + \else% + \ifnum\thepos<100% + \xdef\tikzumlFirstArc{\tikzumlFirstArc node[pos=\tikzumlRelationPosition, \posAttrName, \attrAlign] {\tikzumlRelationAttrName}% + node[pos=\tikzumlRelationPosition, swap, \posMultiplicity, \multAlign] {\tikzumlRelationMultiplicity}% + }% + \else% + \pgfmathsetmacro{\tikzumlRelationPosition}{(\thepos-100)/100}% + \xdef\tikzumlMidOneArc{\tikzumlMidOneArc node[pos=\tikzumlRelationPosition, \posAttrName, \attrAlign] {\tikzumlRelationAttrName}% + node[pos=\tikzumlRelationPosition, swap, \posMultiplicity, \multAlign] {\tikzumlRelationMultiplicity}% + }% + \fi% + \fi% + % + \ifthenelse{\theposT=100}{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionStart}{right}}{% + \ifthenelse{\tikzumlRelationEndAngle<\tikzumlRelationStartAngle}{% + \def\posAttrNameT{above right}% + \def\posMultiplicityT{below left}% + }{% + \def\posAttrNameT{above left}% + \def\posMultiplicityT{below right}% + }% + }{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionStart}{left}}{% + \ifthenelse{\tikzumlRelationEndAngle<\tikzumlRelationStartAngle}{% + \def\posAttrNameT{above left}% + \def\posMultiplicityT{below right}% + }{% + \def\posAttrNameT{above right}% + \def\posMultiplicityT{below left}% + }% + }{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionStart}{top}}{% + \ifthenelse{\tikzumlRelationEndAngle<\tikzumlRelationStartAngle}{% + \def\posAttrNameT{above right}% + \def\posMultiplicityT{below left}% + }{% + \def\posAttrNameT{above left}% + \def\posMultiplicityT{below right}% + }% + }{% + \ifthenelse{\tikzumlRelationEndAngle<\tikzumlRelationStartAngle}{% + \def\posAttrNameT{above left}% + \def\posMultiplicityT{below right}% + }{% + \def\posAttrNameT{above right}% + \def\posMultiplicityT{below left}% + }% + }% + }% + }% + }{}% + % + \ifthenelse{\theposT=200}{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionStart}{right}}{% + \ifthenelse{\tikzumlRelationEndAngle<\tikzumlRelationStartAngle}{% + \def\posAttrNameT{above left}% + \def\posMultiplicityT{below right}% + }{% + \def\posAttrNameT{above right}% + \def\posMultiplicityT{below left}% + }% + }{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionStart}{left}}{% + \ifthenelse{\tikzumlRelationEndAngle<\tikzumlRelationStartAngle}{% + \def\posAttrNameT{above right}% + \def\posMultiplicityT{below left}% + }{% + \def\posAttrNameT{above left}% + \def\posMultiplicityT{below right}% + }% + }{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionStart}{top}}{% + \ifthenelse{\tikzumlRelationEndAngle<\tikzumlRelationStartAngle}{% + \def\posAttrNameT{above left}% + \def\posMultiplicityT{below right}% + }{% + \def\posAttrNameT{above right}% + \def\posMultiplicityT{below left}% + }% + }{% + \ifthenelse{\tikzumlRelationEndAngle<\tikzumlRelationStartAngle}{% + \def\posAttrNameT{above right}% + \def\posMultiplicityT{below left}% + }{% + \def\posAttrNameT{above left}% + \def\posMultiplicityT{below right}% + }% + }% + }% + }% + }{}% + % + \ifnum\theposT>200% + \pgfmathsetmacro{\tikzumlRelationPositionT}{(\theposT-200)/100}% + \xdef\tikzumlLastArc{\tikzumlLastArc node[pos=\tikzumlRelationPositionT, \posAttrNameT, \attrAlignT] {\tikzumlRelationAttrNameT}% + node[pos=\tikzumlRelationPositionT, swap, \posMultiplicityT, \multAlignT] {\tikzumlRelationMultiplicityT}% + }% + \else% + \ifnum\theposT<100% + \xdef\tikzumlFirstArc{\tikzumlFirstArc node[pos=\tikzumlRelationPositionT, \posAttrNameT, \attrAlignT] {\tikzumlRelationAttrNameT}% + node[pos=\tikzumlRelationPositionT, swap, \posMultiplicityT, \multAlignT] {\tikzumlRelationMultiplicityT}% + }% + \else% + \pgfmathsetmacro{\tikzumlRelationPositionT}{(\theposT-100)/100}% + \xdef\tikzumlMidOneArc{\tikzumlMidOneArc node[pos=\tikzumlRelationPositionT, \posAttrNameT, \attrAlignT] {\tikzumlRelationAttrNameT}% + node[pos=\tikzumlRelationPositionT, swap, \posMultiplicityT, \multAlignT] {\tikzumlRelationMultiplicityT}% + }% + \fi% + \fi% + \fi% + % + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionStart}{\tikzumlRelationRecursiveDirectionEnd}}{% + \xdef\tikzumlPath{(\tikzumlSrcClassNodeName.\tikzumlRelationStartAngle) -- \tikzumlFirstArc (\tikzumlRelationName-2.center) -- \tikzumlMidOneArc (\tikzumlRelationName-4.center) -- \tikzumlLastArc (\tikzumlDestClassNodeName.\tikzumlRelationEndAngle) }% + \ifnum\thetikzumlStateLevel>0% + \def\tikzumlStateFitTmp{\csname tikzumlStateFit\tikzumlState@parent @@\tikzumlState@fitname\endcsname}% + \expandafter\xdef\csname tikzumlStateFit\tikzumlState@parent @@\tikzumlState@fitname\endcsname{\tikzumlStateFitTmp (\tikzumlRelationName-1) (\tikzumlRelationName-2) (\tikzumlRelationName-3) (\tikzumlRelationName-4) (\tikzumlRelationName-5)}% + \fi% + }{% + \xdef\tikzumlPath{(\tikzumlSrcClassNodeName.\tikzumlRelationStartAngle) -- \tikzumlFirstArc (\tikzumlRelationName-2.center) -- \tikzumlMidOneArc (\tikzumlRelationName-4.center) -- \tikzumlMidTwoArc (\tikzumlRelationName-6.center) -- \tikzumlLastArc (\tikzumlDestClassNodeName.\tikzumlRelationEndAngle) }% + \ifnum\thetikzumlStateLevel>0% + \def\tikzumlStateFitTmp{\csname tikzumlStateFit\tikzumlState@parent @@\tikzumlState@fitname\endcsname}% + \expandafter\xdef\csname tikzumlStateFit\tikzumlState@parent @@\tikzumlState@fitname\endcsname{\tikzumlStateFitTmp (\tikzumlRelationName-1) (\tikzumlRelationName-2) (\tikzumlRelationName-3) (\tikzumlRelationName-4) (\tikzumlRelationName-5) (\tikzumlRelationName-6) (\tikzumlRelationName-7)}% + \fi% + }% + }{}% + }% + }{% + \def\arcNum{1}% + \def\arcNumT{1}% + % + \node[inner sep=0] (\tikzumlRelationName-1) at (\tikzumlRelationName-middle) {};% + \xdef\tikzumlLastArc{node[pos=\tikzumlRelationPosition, \posAttrName, \attrAlign] {\tikzumlRelationAttrName}% + node[pos=\tikzumlRelationPosition, swap, \posMultiplicity, \multAlign] {\tikzumlRelationMultiplicity}% + node[pos=\tikzumlRelationPositionT, \posAttrNameT, \attrAlignT] {\tikzumlRelationAttrNameT}% + node[pos=\tikzumlRelationPositionT, swap, \posMultiplicityT, \multAlignT] {\tikzumlRelationMultiplicityT}% + node[pos=\tikzumlRelationPositionStereotype, anchor=center] {\stereotype} }% + \xdef\tikzumlPath{(\tikzumlSrcClassNodeName\tikzumlRelationSrcAnchor) -- \tikzumlLastArc (\tikzumlDestClassNodeName\tikzumlRelationDestAnchor) }% + \ifnum\thetikzumlStateLevel>0% + \def\tikzumlStateFitTmp{\csname tikzumlStateFit\tikzumlState@parent @@\tikzumlState@fitname\endcsname}% + \expandafter\xdef\csname tikzumlStateFit\tikzumlState@parent @@\tikzumlState@fitname\endcsname{\tikzumlStateFitTmp (\tikzumlRelationName-1) }% + \fi% + }% + }{% + % first vertical then horizontal line + \ifthenelse{\equal{\tikzumlRelationGeometry}{|-}}% + {% + %\setcounter{tikzumlControlNodesNum}{1}% + % + \def\tikzumlFirstArc{node[midway, inner sep=0, name=\tikzumlRelationName-1, anchor=center] {} }% + \def\tikzumlLastArc{node[midway, inner sep=0, name=\tikzumlRelationName-3, anchor=center]{} }% + % + \begin{pgfonlayer}{connections}% + \node[inner sep=0] (\tikzumlRelationName-2) at (\tikzumlSrcClassNodeName\tikzumlRelationSrcAnchor |- \tikzumlDestClassNodeName\tikzumlRelationDestAnchor) {};% + \end{pgfonlayer}% + % + \ifnum\theposStereo>100% + \pgfmathsetmacro{\tikzumlRelationPositionStereotype}{(\theposStereo-100)/100}% + \xdef\tikzumlLastArc{\tikzumlLastArc node[pos=\tikzumlRelationPositionStereotype, anchor=center] {\stereotype} }% + \else% + \xdef\tikzumlFirstArc{\tikzumlFirstArc node[pos=\tikzumlRelationPositionStereotype, anchor=center] {\stereotype} }% + \fi% + % + \ifnum\thepos>100% + \pgfmathsetmacro{\tikzumlRelationPosition}{(\thepos-100)/100}% + \def\arcNum{2}% + \else% + \def\arcNum{1}% + \ifnum\thepos=100% + \def\posAttrName{above left}% + \def\posMultiplicity{below right}% + \fi% + \fi% + % + \ifnum\arcNum=1% + \xdef\tikzumlFirstArc{\tikzumlFirstArc node[pos=\tikzumlRelationPosition, \posAttrName, \attrAlign] {\tikzumlRelationAttrName}% + node[pos=\tikzumlRelationPosition, swap, \posMultiplicity, \multAlign] {\tikzumlRelationMultiplicity} }% + \fi% + \ifnum\arcNum=2% + \xdef\tikzumlLastArc{\tikzumlLastArc node[pos=\tikzumlRelationPosition, \posAttrName, \attrAlign] {\tikzumlRelationAttrName}% + node[pos=\tikzumlRelationPosition, swap, \posMultiplicity, \multAlign] {\tikzumlRelationMultiplicity} }% + \fi% + % + \ifnum\theposT>100% + \pgfmathsetmacro{\tikzumlRelationPositionT}{(\theposT-100)/100}% + \def\arcNumT{2}% + \else% + \def\arcNumT{1}% + \ifnum\theposT=100% + \def\posAttrNameT{above left}% + \def\posMultiplicityT{below right}% + \fi% + \fi% + % + \ifnum\arcNumT=1% + \xdef\tikzumlFirstArc{\tikzumlFirstArc node[pos=\tikzumlRelationPositionT, \posAttrNameT, \attrAlignT] {\tikzumlRelationAttrNameT}% + node[pos=\tikzumlRelationPositionT, swap, \posMultiplicityT, \multAlignT] {\tikzumlRelationMultiplicityT} }% + \fi% + \ifnum\arcNumT=2% + \xdef\tikzumlLastArc{\tikzumlLastArc node[pos=\tikzumlRelationPositionT, \posAttrNameT, \attrAlignT] {\tikzumlRelationAttrNameT}% + node[pos=\tikzumlRelationPositionT, swap, \posMultiplicityT, \multAlignT] {\tikzumlRelationMultiplicityT} }% + \fi% + % + \xdef\tikzumlPath{(\tikzumlSrcClassNodeName\tikzumlRelationSrcAnchor) -- \tikzumlFirstArc (\tikzumlRelationName-2.base) -- \tikzumlLastArc (\tikzumlDestClassNodeName\tikzumlRelationDestAnchor) }% + \ifnum\thetikzumlStateLevel>0% + \def\tikzumlStateFitTmp{\csname tikzumlStateFit\tikzumlState@parent @@\tikzumlState@fitname\endcsname}% + \expandafter\xdef\csname tikzumlStateFit\tikzumlState@parent @@\tikzumlState@fitname\endcsname{\tikzumlStateFitTmp (\tikzumlRelationName-1) (\tikzumlRelationName-2) (\tikzumlRelationName-3) }% + \fi% + }{% + % first horizontal then vertical line + \ifthenelse{\equal{\tikzumlRelationGeometry}{-|}}% + {% + %\setcounter{tikzumlControlNodesNum}{1}% + % + \def\tikzumlFirstArc{node[midway, inner sep=0, name=\tikzumlRelationName-1, anchor=center]{} }% + \def\tikzumlLastArc{node[midway, inner sep=0, name=\tikzumlRelationName-3, anchor=center] {} }% + % + \begin{pgfonlayer}{connections}% + \node[inner sep=0] (\tikzumlRelationName-2) at (\tikzumlSrcClassNodeName\tikzumlRelationSrcAnchor -| \tikzumlDestClassNodeName\tikzumlRelationDestAnchor) {};% + \end{pgfonlayer}% + % + \ifnum\theposStereo>100% + \pgfmathsetmacro{\tikzumlRelationPositionStereotype}{(\theposStereo-100)/100}% + \xdef\tikzumlLastArc{\tikzumlLastArc node[pos=\tikzumlRelationPositionStereotype, anchor=center] {\stereotype} }% + \else% + \xdef\tikzumlFirstArc{\tikzumlFirstArc node[pos=\tikzumlRelationPositionStereotype, anchor=center] {\stereotype} }% + \fi% + % + \ifnum\thepos>100% + \pgfmathsetmacro{\tikzumlRelationPosition}{(\thepos-100)/100}% + \def\arcNum{2}% + \else% + \def\arcNum{1}% + \ifnum\thepos=100% + \def\posAttrName{above left}% + \def\posMultiplicity{below right}% + \fi% + \fi% + % + \ifnum\arcNum=1% + \xdef\tikzumlFirstArc{\tikzumlFirstArc node[pos=\tikzumlRelationPosition, \posAttrName, \attrAlign] {\tikzumlRelationAttrName}% + node[pos=\tikzumlRelationPosition, swap, \posMultiplicity, \multAlign] {\tikzumlRelationMultiplicity} }% + \fi% + \ifnum\arcNum=2% + \xdef\tikzumlLastArc{\tikzumlLastArc node[pos=\tikzumlRelationPosition, \posAttrName, \attrAlign] {\tikzumlRelationAttrName}% + node[pos=\tikzumlRelationPosition, swap, \posMultiplicity, \multAlign] {\tikzumlRelationMultiplicity} }% + \fi% + % + \ifnum\theposT>100% + \pgfmathsetmacro{\tikzumlRelationPositionT}{(\theposT-100)/100}% + \def\arcNumT{2}% + \else% + \def\arcNumT{1}% + \ifnum\theposT=100% + \def\posAttrNameT{above left}% + \def\posMultiplicityT{below right}% + \fi% + \fi% + % + \ifnum\arcNumT=1% + \xdef\tikzumlFirstArc{\tikzumlFirstArc node[pos=\tikzumlRelationPositionT, \posAttrNameT, \attrAlignT] {\tikzumlRelationAttrNameT}% + node[pos=\tikzumlRelationPositionT, swap, \posMultiplicityT, \multAlignT] {\tikzumlRelationMultiplicityT} }% + \fi% + \ifnum\arcNumT=2% + \xdef\tikzumlLastArc{\tikzumlLastArc node[pos=\tikzumlRelationPositionT, \posAttrNameT, \attrAlignT] {\tikzumlRelationAttrNameT}% + node[pos=\tikzumlRelationPositionT, swap, \posMultiplicityT, \multAlignT] {\tikzumlRelationMultiplicityT} }% + \fi% + % + \xdef\tikzumlPath{(\tikzumlSrcClassNodeName\tikzumlRelationSrcAnchor) -- \tikzumlFirstArc (\tikzumlRelationName-2.base) -- \tikzumlLastArc (\tikzumlDestClassNodeName\tikzumlRelationDestAnchor) }% + \ifnum\thetikzumlStateLevel>0% + \def\tikzumlStateFitTmp{\csname tikzumlStateFit\tikzumlState@parent @@\tikzumlState@fitname\endcsname}% + \expandafter\xdef\csname tikzumlStateFit\tikzumlState@parent @@\tikzumlState@fitname\endcsname{\tikzumlStateFitTmp (\tikzumlRelationName-1) (\tikzumlRelationName-2) (\tikzumlRelationName-3) }% + \fi% + }{% + % first vertical, then horizontal, finally vertical line + \ifthenelse{\equal{\tikzumlRelationGeometry}{|-|}}% + {% + %\setcounter{tikzumlControlNodesNum}{2}% + % + \def\tikzumlFirstArc{node[midway, inner sep=0, name=\tikzumlRelationName-1, anchor=center] {} }% + \def\tikzumlLastArc{node[midway, inner sep=0, name=\tikzumlRelationName-5, anchor=center] {} }% + \def\tikzumlMidOneArc{ }% + % + \begin{pgfonlayer}{connections}% + % + \ifthenelse{\equal{\tikzumlRelationArmO}{auto}}{% + \ifthenelse{\equal{\tikzumlRelationArmT}{auto}}{% + \node[inner sep=0] (\tikzumlRelationName-3) at (\tikzumlRelationName-middle) {};% + \node[inner sep=0] (\tikzumlRelationName-2) at (\tikzumlSrcClassNodeName\tikzumlRelationSrcAnchor |- \tikzumlRelationName-3) {};% + \node[inner sep=0] (\tikzumlRelationName-4) at (\tikzumlRelationName-3 -| \tikzumlDestClassNodeName\tikzumlRelationDestAnchor) {};% + }{% + \draw (\tikzumlDestClassNodeName\tikzumlRelationDestAnchor)+(0,\tikzumlRelationArmT) node[inner sep=0, name=\tikzumlRelationName-4] {};% + \node[inner sep=0] (\tikzumlRelationName-2) at (\tikzumlRelationName-4 -| \tikzumlSrcClassNodeName\tikzumlRelationSrcAnchor) {};% + \node[inner sep=0] (\tikzumlRelationName-3) at (barycentric cs:\tikzumlRelationName-2=0.5,\tikzumlRelationName-4=0.5) {};% + }% + }{% + \draw (\tikzumlSrcClassNodeName\tikzumlRelationSrcAnchor)+(0,\tikzumlRelationArmO) node[inner sep=0, name=\tikzumlRelationName-2] {};% + \node[inner sep=0] (\tikzumlRelationName-4) at (\tikzumlRelationName-2 -| \tikzumlDestClassNodeName\tikzumlRelationDestAnchor) {};% + \node[inner sep=0] (\tikzumlRelationName-3) at (barycentric cs:\tikzumlRelationName-2=0.5,\tikzumlRelationName-4=0.5) {};% + }% + \end{pgfonlayer}% + % + \ifnum\theposStereo>200% + \pgfmathsetmacro{\tikzumlRelationPositionStereotype}{(\theposStereo-200)/100}% + \xdef\tikzumlLastArc{\tikzumlLastArc node[pos=\tikzumlRelationPositionStereotype, anchor=center] {\stereotype} }% + \else% + \ifnum\theposStereo<100% + \xdef\tikzumlFirstArc{\tikzumlFirstArc node[pos=\tikzumlRelationPositionStereotype, anchor=center] {\stereotype} }% + \else% + \pgfmathsetmacro{\tikzumlRelationPositionStereotype}{(\theposStereo-100)/100}% + \xdef\tikzumlMidOneArc{\tikzumlMidOneArc node[pos=\tikzumlRelationPositionStereotype, anchor=center] {\stereotype} }% + \fi% + \fi% + % + \ifthenelse{\thepos=200\OR\thepos=100}{% + \def\posAttrName{above left}% + \def\posMultiplicity{below right}% + }{}% + % + \ifthenelse{\thepos>200}{% + \pgfmathsetmacro{\tikzumlRelationPosition}{(\thepos-200)/100}% + \def\arcNum{3}% + }{% + \ifthenelse{\thepos<100}{% + \def\arcNum{1}% + }{% + \pgfmathsetmacro{\tikzumlRelationPosition}{(\thepos-100)/100}% + \def\arcNum{2}% + }% + }% + % + \ifnum\arcNum=1% + \xdef\tikzumlFirstArc{\tikzumlFirstArc node[pos=\tikzumlRelationPosition, \posAttrName, \attrAlign] {\tikzumlRelationAttrName}% + node[pos=\tikzumlRelationPosition, swap, \posMultiplicity, \multAlign] {\tikzumlRelationMultiplicity} }% + \fi% + \ifnum\arcNum=2% + \xdef\tikzumlMidOneArc{\tikzumlMidOneArc node[pos=\tikzumlRelationPosition, \posAttrName, \attrAlign] {\tikzumlRelationAttrName}% + node[pos=\tikzumlRelationPosition, swap, \posMultiplicity, \multAlign] {\tikzumlRelationMultiplicity} }% + \fi% + \ifnum\arcNum=3% + \xdef\tikzumlLastArc{\tikzumlLastArc node[pos=\tikzumlRelationPosition, \posAttrName, \attrAlign] {\tikzumlRelationAttrName}% + node[pos=\tikzumlRelationPosition, swap, \posMultiplicity, \multAlign] {\tikzumlRelationMultiplicity} }% + \fi% + % + \ifthenelse{\theposT=200\OR\theposT=100}{% + \def\posAttrNameT{above left}% + \def\posMultiplicityT{below right}% + }{}% + % + \ifthenelse{\theposT>200}{% + \pgfmathsetmacro{\tikzumlRelationPositionT}{(\theposT-200)/100}% + \def\arcNumT{3}% + }{% + \ifthenelse{\theposT<100}{% + \def\arcNumT{1}% + }{% + \pgfmathsetmacro{\tikzumlRelationPositionT}{(\theposT-100)/100}% + \def\arcNumT{2}% + }% + }% + % + \ifnum\arcNumT=1% + \xdef\tikzumlFirstArc{\tikzumlFirstArc node[pos=\tikzumlRelationPositionT, \posAttrNameT, \attrAlignT] {\tikzumlRelationAttrNameT}% + node[pos=\tikzumlRelationPositionT, swap, \posMultiplicityT, \multAlignT] {\tikzumlRelationMultiplicityT} }% + \fi% + \ifnum\arcNumT=2% + \xdef\tikzumlMidOneArc{\tikzumlMidOneArc node[pos=\tikzumlRelationPositionT, \posAttrNameT, \attrAlignT] {\tikzumlRelationAttrNameT}% + node[pos=\tikzumlRelationPositionT, swap, \posMultiplicityT, \multAlignT] {\tikzumlRelationMultiplicityT} }% + \fi% + \ifnum\arcNumT=3% + \xdef\tikzumlLastArc{\tikzumlLastArc node[pos=\tikzumlRelationPositionT, \posAttrNameT, \attrAlignT] {\tikzumlRelationAttrNameT}% + node[pos=\tikzumlRelationPositionT, swap, \posMultiplicityT, \multAlignT] {\tikzumlRelationMultiplicityT} }% + \fi% + % + \xdef\tikzumlPath{(\tikzumlSrcClassNodeName\tikzumlRelationSrcAnchor) -- \tikzumlFirstArc (\tikzumlRelationName-2.base) -- \tikzumlMidOneArc (\tikzumlRelationName-4.base) -- \tikzumlLastArc (\tikzumlDestClassNodeName\tikzumlRelationDestAnchor) }% + \ifnum\thetikzumlStateLevel>0% + \def\tikzumlStateFitTmp{\csname tikzumlStateFit\tikzumlState@parent @@\tikzumlState@fitname\endcsname}% + \expandafter\xdef\csname tikzumlStateFit\tikzumlState@parent @@\tikzumlState@fitname\endcsname{\tikzumlStateFitTmp (\tikzumlRelationName-1) (\tikzumlRelationName-2) (\tikzumlRelationName-3) (\tikzumlRelationName-4) (\tikzumlRelationName-5) }% + \fi% + }{% + % first horizontal, then vertical, finally horizontal line + \ifthenelse{\equal{\tikzumlRelationGeometry}{-|-}}% + {% + %\setcounter{tikzumlControlNodesNum}{2}% + % + \def\tikzumlFirstArc{node[midway, inner sep=0, name=\tikzumlRelationName-1, anchor=center] {} }% + \def\tikzumlLastArc{node[midway, inner sep=0, name=\tikzumlRelationName-5, anchor=center] {} }% + \def\tikzumlMidOneArc{}% + % + \begin{pgfonlayer}{connections}% + % + \ifthenelse{\equal{\tikzumlRelationArmO}{auto}}{% + \ifthenelse{\equal{\tikzumlRelationArmT}{auto}}{% + \node[inner sep=0] (\tikzumlRelationName-3) at (\tikzumlRelationName-middle) {};% + \node[inner sep=0] (\tikzumlRelationName-2) at (\tikzumlSrcClassNodeName\tikzumlRelationSrcAnchor -| \tikzumlRelationName-3) {};% + \node[inner sep=0] (\tikzumlRelationName-4) at (\tikzumlRelationName-3 |- \tikzumlDestClassNodeName\tikzumlRelationDestAnchor) {};% + }{% + \draw (\tikzumlDestClassNodeName\tikzumlRelationDestAnchor)+(\tikzumlRelationArmT,0) node[inner sep=0, name=\tikzumlRelationName-4] {};% + \node[inner sep=0] (\tikzumlRelationName-2) at (\tikzumlRelationName-4 |- \tikzumlSrcClassNodeName\tikzumlRelationSrcAnchor) {};% + \node[inner sep=0] (\tikzumlRelationName-3) at (barycentric cs:\tikzumlRelationName-2=0.5,\tikzumlRelationName-4=0.5) {};% + }% + }{% + \draw (\tikzumlSrcClassNodeName\tikzumlRelationSrcAnchor)+(\tikzumlRelationArmO,0) node[inner sep=0, name=\tikzumlRelationName-2] {};% + \node[inner sep=0] (\tikzumlRelationName-4) at (\tikzumlRelationName-2 |- \tikzumlDestClassNodeName\tikzumlRelationDestAnchor) {};% + \node[inner sep=0] (\tikzumlRelationName-3) at (barycentric cs:\tikzumlRelationName-2=0.5,\tikzumlRelationName-4=0.5) {};% + }% + \end{pgfonlayer}% + % + \ifnum\theposStereo>200% + \pgfmathsetmacro{\tikzumlRelationPositionStereotype}{(\theposStereo-200)/100}% + \xdef\tikzumlLastArc{\tikzumlLastArc node[pos=\tikzumlRelationPositionStereotype, anchor=center] {\stereotype} }% + \else% + \ifnum\theposStereo<100% + \xdef\tikzumlFirstArc{\tikzumlFirstArc node[pos=\tikzumlRelationPositionStereotype, anchor=center] {\stereotype} }% + \else% + \pgfmathsetmacro{\tikzumlRelationPositionStereotype}{(\theposStereo-100)/100}% + \xdef\tikzumlMidOneArc{\tikzumlMidOneArc node[pos=\tikzumlRelationPositionStereotype, anchor=center] {\stereotype} }% + \fi% + \fi% + % + \ifthenelse{\thepos=200\OR\thepos=100}{% + \def\posAttrName{above left}% + \def\posMultiplicity{below right}% + }{}% + % + \ifthenelse{\thepos>200}{% + \pgfmathsetmacro{\tikzumlRelationPosition}{(\thepos-200)/100}% + \def\arcNum{3}% + }{% + \ifthenelse{\thepos<100}{% + \def\arcNum{1}% + }{% + \pgfmathsetmacro{\tikzumlRelationPosition}{(\thepos-100)/100}% + \def\arcNum{2}% + }% + }% + % + \ifnum\arcNum=1% + \xdef\tikzumlFirstArc{\tikzumlFirstArc node[pos=\tikzumlRelationPosition, \posAttrName, \attrAlign] {\tikzumlRelationAttrName}% + node[pos=\tikzumlRelationPosition, swap, \posMultiplicity, \multAlign] {\tikzumlRelationMultiplicity} }% + \fi% + \ifnum\arcNum=2% + \xdef\tikzumlMidOneArc{\tikzumlMidOneArc node[pos=\tikzumlRelationPosition, \posAttrName, \attrAlign] {\tikzumlRelationAttrName}% + node[pos=\tikzumlRelationPosition, swap, \posMultiplicity, \multAlign] {\tikzumlRelationMultiplicity} }% + \fi% + \ifnum\arcNum=3% + \xdef\tikzumlLastArc{\tikzumlLastArc node[pos=\tikzumlRelationPosition, \posAttrName, \attrAlign] {\tikzumlRelationAttrName}% + node[pos=\tikzumlRelationPosition, swap, \posMultiplicity, \multAlign] {\tikzumlRelationMultiplicity} }% + \fi% + % + \ifthenelse{\theposT=200\OR\theposT=100}{% + \def\posAttrNameT{above left}% + \def\posMultiplicityT{below right}% + }{}% + % + \ifthenelse{\theposT>200}{% + \pgfmathsetmacro{\tikzumlRelationPositionT}{(\theposT-200)/100}% + \def\arcNumT{3}% + }{% + \ifthenelse{\theposT<100}{% + \def\arcNumT{1}% + }{% + \pgfmathsetmacro{\tikzumlRelationPositionT}{(\theposT-100)/100}% + \def\arcNumT{2}% + }% + }% + % + \ifnum\arcNumT=1% + \xdef\tikzumlFirstArc{\tikzumlFirstArc node[pos=\tikzumlRelationPositionT, \posAttrNameT, \attrAlignT] {\tikzumlRelationAttrNameT}% + node[pos=\tikzumlRelationPositionT, swap, \posMultiplicityT, \multAlignT] {\tikzumlRelationMultiplicityT} }% + \fi% + \ifnum\arcNumT=2% + \xdef\tikzumlMidOneArc{\tikzumlMidOneArc node[pos=\tikzumlRelationPositionT, \posAttrNameT, \attrAlignT] {\tikzumlRelationAttrNameT}% + node[pos=\tikzumlRelationPositionT, swap, \posMultiplicityT, \multAlignT] {\tikzumlRelationMultiplicityT} }% + \fi% + \ifnum\arcNumT=3% + \xdef\tikzumlLastArc{\tikzumlLastArc node[pos=\tikzumlRelationPositionT, \posAttrNameT, \attrAlignT] {\tikzumlRelationAttrNameT}% + node[pos=\tikzumlRelationPositionT, swap, \posMultiplicityT, \multAlignT] {\tikzumlRelationMultiplicityT} }% + \fi% + % + \xdef\tikzumlPath{(\tikzumlSrcClassNodeName\tikzumlRelationSrcAnchor) -- \tikzumlFirstArc (\tikzumlRelationName-2.base) -- \tikzumlMidOneArc (\tikzumlRelationName-4.base) -- \tikzumlLastArc (\tikzumlDestClassNodeName\tikzumlRelationDestAnchor) }% + \ifnum\thetikzumlStateLevel>0% + \def\tikzumlStateFitTmp{\csname tikzumlStateFit\tikzumlState@parent @@\tikzumlState@fitname\endcsname}% + \expandafter\xdef\csname tikzumlStateFit\tikzumlState@parent @@\tikzumlState@fitname\endcsname{\tikzumlStateFitTmp (\tikzumlRelationName-1) (\tikzumlRelationName-2) (\tikzumlRelationName-3) (\tikzumlRelationName-4) (\tikzumlRelationName-5) }% + \fi% + }{% + \errmessage{TIKZUML ERROR : Unknown geometry value !!! It should be in the following list : --, |-, -|, |-|, -|-}% + }% + }% + }% + }% + }% + % + \begin{pgfonlayer}{connections}% + \ifthenelse{\equal{\tikzumlRelationStyle}{tikzuml nesting style}}{% + \pgfarrowsdeclare{nested}{nested}{...} + { + \tikzumlNestingSymbolSize=0.2pt% + \advance\tikzumlNestingSymbolSize by .5\pgflinewidth% + \pgfsetdash{}{0pt} % do not dash + \pgfsetroundjoin % fix join + \pgfsetroundcap % fix cap + \pgfpathmoveto{\pgfpoint{-16*\tikzumlNestingSymbolSize}{0pt}}% + \pgfpatharc{180}{90}{8*\tikzumlNestingSymbolSize}% + \pgfpatharc{90}{0}{8*\tikzumlNestingSymbolSize}% + \pgfpatharc{0}{-90}{8*\tikzumlNestingSymbolSize}% + \pgfpatharc{-90}{-180}{8*\tikzumlNestingSymbolSize}% + \pgfpathmoveto{\pgfpoint{-8*\tikzumlNestingSymbolSize}{8*\tikzumlNestingSymbolSize}}% + \pgfpathlineto{\pgfpoint{-8*\tikzumlNestingSymbolSize}{-8*\tikzumlNestingSymbolSize}}% + \pgfusepathqstroke% + }% + \draw[auto, nested-, font=\tikzumlDefaultFont, \tikzumlRelationStyle, /tikzuml/relation/style2] \tikzumlPath ;% + }{ + \draw[auto, font=\tikzumlDefaultFont, \tikzumlRelationStyle, /tikzuml/relation/style2] \tikzumlPath ;% + } + \end{pgfonlayer}% + % + \stepcounter{tikzumlRelationNum}% +}% +% +% shortcuts of \umlrelation +\newcommand{\umlHVrelation}[3][]{% + \pgfkeys{/tikzuml/HVrelation/.cd,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{geometry}}{% + \errmessage{TIKZUML ERROR : in umlHVrelation, forbidden option geometry}% + }{}% + }}% + \pgfkeys{/tikzuml/HVrelation/.cd, #1}% + \umlrelation[geometry=-|, #1]{#2}{#3}% +}% +% +\newcommand{\umlVHrelation}[3][]{% + \pgfkeys{/tikzuml/VHrelation/.cd,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{geometry}}{% + \errmessage{TIKZUML ERROR : in umlVHrelation, forbidden option geometry}% + }{}% + }}% + \pgfkeys{/tikzuml/VHrelation/.cd, #1}% + \umlrelation[geometry=|-, #1]{#2}{#3}% +}% +% +\newcommand{\umlHVHrelation}[3][]{% + \pgfkeys{/tikzuml/HVHrelation/.cd,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{geometry}}{% + \errmessage{TIKZUML ERROR : in umlHVHrelation, forbidden option geometry}% + }{}% + }}% + \pgfkeys{/tikzuml/HVHrelation/.cd, #1}% + \umlrelation[geometry=-|-, #1]{#2}{#3}% +}% +% +\newcommand{\umlVHVrelation}[3][]{% + \pgfkeys{/tikzuml/VHVrelation/.cd,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{geometry}}{% + \errmessage{TIKZUML ERROR : in umlVHVrelation, forbidden option geometry}% + }{}% + }}% + \pgfkeys{/tikzuml/VHVrelation/.cd, #1}% + \umlrelation[geometry=|-|, #1]{#2}{#3}% +}% +% +% +% shortcuts for relations +\newcommand{\umlinherit}[3][]{\umlrelation[style={tikzuml inherit style}, #1]{#2}{#3}}% +\newcommand{\umlimpl}[3][]{\umlrelation[style={tikzuml implements style}, #1]{#2}{#3}}% +\newcommand{\umlreal}[3][]{\umlrelation[style={tikzuml implements style}, #1]{#2}{#3}}% +\newcommand{\umlassoc}[3][]{\umlrelation[style={tikzuml association style}, #1]{#2}{#3}}% +\newcommand{\umlbiassoc}[3][]{\umlrelation[style={tikzuml bidirectional association style}, #1]{#2}{#3}}% +\newcommand{\umluniassoc}[3][]{\umlrelation[style={tikzuml unidirectional association style}, #1]{#2}{#3}}% +\newcommand{\umlaggreg}[3][]{\umlrelation[style={tikzuml aggregation style}, #1]{#2}{#3}}% +\newcommand{\umluniaggreg}[3][]{\umlrelation[style={tikzuml unidirectional aggregation style}, #1]{#2}{#3}}% +\newcommand{\umlcompo}[3][]{\umlrelation[style={tikzuml composition style}, #1]{#2}{#3}}% +\newcommand{\umlunicompo}[3][]{\umlrelation[style={tikzuml unidirectional composition style}, #1]{#2}{#3}}% +\newcommand{\umlimport}[3][]{\umlrelation[style={tikzuml import style}, #1]{#2}{#3}}% +\newcommand{\umlnest}[3][]{\umlrelation[style={tikzuml nesting style}, #1]{#2}{#3}}% +\newcommand{\umldep}[3][]{\umlrelation[style={tikzuml dependency style}, #1]{#2}{#3}}% +\newcommand{\umlfriend}[3][]{% + \pgfkeys{/tikzuml/friendrelation/.cd,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{stereo}}{% + \errmessage{TIKZUML ERROR : in umlfriend, forbidden option stereo}% + }{}% + }}% + \pgfkeys{/tikzuml/friendrelation/.cd, #1}% + \umlrelation[stereo=friend, style={tikzuml dependency style}, #1]{#2}{#3}% +}% +% +\newcommand{\umlHVinherit}[3][]{\umlHVrelation[style={tikzuml inherit style}, #1]{#2}{#3}}% +\newcommand{\umlHVimpl}[3][]{\umlHVrelation[style={tikzuml implements style}, #1]{#2}{#3}}% +\newcommand{\umlHVreal}[3][]{\umlHVrelation[style={tikzuml implements style}, #1]{#2}{#3}}% +\newcommand{\umlHVassoc}[3][]{\umlHVrelation[style={tikzuml association style}, #1]{#2}{#3}}% +\newcommand{\umlHVuniassoc}[3][]{\umlHVrelation[style={tikzuml unidirectional association style}, #1]{#2}{#3}}% +\newcommand{\umlHVaggreg}[3][]{\umlHVrelation[style={tikzuml aggregation style}, #1]{#2}{#3}}% +\newcommand{\umlHVuniaggreg}[3][]{\umlHVrelation[style={tikzuml unidirectional aggregation style}, #1]{#2}{#3}}% +\newcommand{\umlHVcompo}[3][]{\umlHVrelation[style={tikzuml composition style}, #1]{#2}{#3}}% +\newcommand{\umlHVunicompo}[3][]{\umlHVrelation[style={tikzuml unidirectional composition style}, #1]{#2}{#3}}% +\newcommand{\umlHVimport}[3][]{\umlHVrelation[style={tikzuml import style}, #1]{#2}{#3}}% +\newcommand{\umlHVnest}[3][]{\umlHVrelation[style={tikzuml nesting style}, #1]{#2}{#3}}% +\newcommand{\umlHVdep}[3][]{\umlHVrelation[style={tikzuml dependency style}, #1]{#2}{#3}}% +\newcommand{\umlHVfriend}[3][]{% + \pgfkeys{/tikzuml/friendrelation/.cd,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{stereo}}{% + \errmessage{TIKZUML ERROR : in umlHVfriend, forbidden option stereo}% + }{% + \ifthenelse{\equal{\keyname}{geometry}}{% + \errmessage{TIKZUML ERROR : in umlHVfriend, forbidden option geometry}% + }{}% + }% + }}% + \pgfkeys{/tikzuml/friendrelation/.cd, #1}% + \umlrelation[geometry=-|, stereo=friend, style={tikzuml dependency style}, #1]{#2}{#3}% +}% +% +\newcommand{\umlVHinherit}[3][]{\umlVHrelation[style={tikzuml inherit style}, #1]{#2}{#3}}% +\newcommand{\umlVHimpl}[3][]{\umlVHrelation[style={tikzuml implements style}, #1]{#2}{#3}}% +\newcommand{\umlVHreal}[3][]{\umlVHrelation[style={tikzuml implements style}, #1]{#2}{#3}}% +\newcommand{\umlVHassoc}[3][]{\umlVHrelation[style={tikzuml association style}, #1]{#2}{#3}}% +\newcommand{\umlVHuniassoc}[3][]{\umlVHrelation[style={tikzuml unidirectional association style}, #1]{#2}{#3}}% +\newcommand{\umlVHaggreg}[3][]{\umlVHrelation[style={tikzuml aggregation style}, #1]{#2}{#3}}% +\newcommand{\umlVHuniaggreg}[3][]{\umlVHrelation[style={tikzuml unidirectional aggregation style}, #1]{#2}{#3}}% +\newcommand{\umlVHcompo}[3][]{\umlVHrelation[style={tikzuml composition style}, #1]{#2}{#3}}% +\newcommand{\umlVHunicompo}[3][]{\umlVHrelation[style={tikzuml unidirectional composition style}, #1]{#2}{#3}}% +\newcommand{\umlVHimport}[3][]{\umlVHrelation[style={tikzuml import style}, #1]{#2}{#3}}% +\newcommand{\umlVHnest}[3][]{\umlVHrelation[style={tikzuml nesting style}, #1]{#2}{#3}}% +\newcommand{\umlVHdep}[3][]{\umlVHrelation[style={tikzuml dependency style}, #1]{#2}{#3}}% +\newcommand{\umlVHfriend}[3][]{% + \pgfkeys{/tikzuml/friendrelation/.cd,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{stereo}}{% + \errmessage{TIKZUML ERROR : in umlVHfriend, forbidden option stereo}% + }{% + \ifthenelse{\equal{\keyname}{geometry}}{% + \errmessage{TIKZUML ERROR : in umlVHfriend, forbidden option geometry}% + }{}% + }% + }}% + \pgfkeys{/tikzuml/friendrelation/.cd, #1}% + \umlrelation[geometry=|-, stereo=friend, style={tikzuml dependency style}, #1]{#2}{#3}% +}% +% +\newcommand{\umlHVHinherit}[3][]{\umlHVHrelation[style={tikzuml inherit style}, #1]{#2}{#3}}% +\newcommand{\umlHVHimpl}[3][]{\umlHVHrelation[style={tikzuml implements style}, #1]{#2}{#3}}% +\newcommand{\umlHVHreal}[3][]{\umlHVHrelation[style={tikzuml implements style}, #1]{#2}{#3}}% +\newcommand{\umlHVHassoc}[3][]{\umlHVHrelation[style={tikzuml association style}, #1]{#2}{#3}}% +\newcommand{\umlHVHuniassoc}[3][]{\umlHVHrelation[style={tikzuml unidirectional association style}, #1]{#2}{#3}}% +\newcommand{\umlHVHaggreg}[3][]{\umlHVHrelation[style={tikzuml aggregation style}, #1]{#2}{#3}}% +\newcommand{\umlHVHuniaggreg}[3][]{\umlHVHrelation[style={tikzuml unidirectional aggregation style}, #1]{#2}{#3}}% +\newcommand{\umlHVHcompo}[3][]{\umlHVHrelation[style={tikzuml composition style}, #1]{#2}{#3}}% +\newcommand{\umlHVHunicompo}[3][]{\umlHVHrelation[style={tikzuml unidirectional composition style}, #1]{#2}{#3}}% +\newcommand{\umlHVHimport}[3][]{\umlHVHrelation[style={tikzuml import style}, #1]{#2}{#3}}% +\newcommand{\umlHVHnest}[3][]{\umlHVHrelation[style={tikzuml nesting style}, #1]{#2}{#3}}% +\newcommand{\umlHVHdep}[3][]{\umlHVHrelation[style={tikzuml dependency style}, #1]{#2}{#3}}% +\newcommand{\umlHVHfriend}[3][]{% + \pgfkeys{/tikzuml/friendrelation/.cd,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{stereo}}{% + \errmessage{TIKZUML ERROR : in umlHVHfriend, forbidden option stereo}% + }{% + \ifthenelse{\equal{\keyname}{geometry}}{% + \errmessage{TIKZUML ERROR : in umlHVHfriend, forbidden option geometry}% + }{}% + }% + }}% + \pgfkeys{/tikzuml/friendrelation/.cd, #1}% + \umlrelation[geometry=-|-, stereo=friend, style={tikzuml dependency style}, #1]{#2}{#3}% +}% +% +\newcommand{\umlVHVinherit}[3][]{\umlVHVrelation[style={tikzuml inherit style}, #1]{#2}{#3}}% +\newcommand{\umlVHVimpl}[3][]{\umlVHVrelation[style={tikzuml implements style}, #1]{#2}{#3}}% +\newcommand{\umlVHVreal}[3][]{\umlVHVrelation[style={tikzuml implements style}, #1]{#2}{#3}}% +\newcommand{\umlVHVassoc}[3][]{\umlVHVrelation[style={tikzuml association style}, #1]{#2}{#3}}% +\newcommand{\umlVHVuniassoc}[3][]{\umlVHVrelation[style={tikzuml unidirectional association style}, #1]{#2}{#3}}% +\newcommand{\umlVHVaggreg}[3][]{\umlVHVrelation[style={tikzuml aggregation style}, #1]{#2}{#3}}% +\newcommand{\umlVHVuniaggreg}[3][]{\umlVHVrelation[style={tikzuml unidirectional aggregation style}, #1]{#2}{#3}}% +\newcommand{\umlVHVcompo}[3][]{\umlVHVrelation[style={tikzuml composition style}, #1]{#2}{#3}}% +\newcommand{\umlVHVunicompo}[3][]{\umlVHVrelation[style={tikzuml unidirectional composition style}, #1]{#2}{#3}}% +\newcommand{\umlVHVimport}[3][]{\umlVHVrelation[style={tikzuml import style}, #1]{#2}{#3}}% +\newcommand{\umlVHVnest}[3][]{\umlVHVrelation[style={tikzuml nesting style}, #1]{#2}{#3}}% +\newcommand{\umlVHVdep}[3][]{\umlVHVrelation[style={tikzuml dependency style}, #1]{#2}{#3}}% +\newcommand{\umlVHVfriend}[3][]{% + \pgfkeys{/tikzuml/friendrelation/.cd,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{stereo}}{% + \errmessage{TIKZUML ERROR : in umlVHVfriend, forbidden option stereo}% + }{% + \ifthenelse{\equal{\keyname}{geometry}}{% + \errmessage{TIKZUML ERROR : in umlVHVfriend, forbidden option geometry}% + }{}% + }% + }}% + \pgfkeys{/tikzuml/friendrelation/.cd, #1}% + \umlrelation[geometry=|-|, stereo=friend, style={tikzuml dependency style}, #1]{#2}{#3}% +}% +% +% define a node +\newcommand{\umlnode}[2]{% + \node (#2) at (#1) {};% +}% +% +% main command to define a relation between two classes through a control node +% args : src class +% control node +% dest class +% optional : arg1, arg2, arg: name of the src/dest/dest side class type attribute defined by the relation +% mult1, mult2, mult: multiplicity of the src/dest/dest side class type attribute defined by the relation +% pos1, pos2, pos: position of the src/dest/dest side class type attribute defined by the relation +% align1, align2, align: text justification of the src/dest/dest side class type attribute defined by the relation +% anchor1, anchor2: src/dest anchors on linked classes +% stereo: stereotype of the relation +% pos stereo: position of the stereotype on the relation +% style: style of the relation (association, aggregation, composition, inherit, ...) +% name: rootname used for naming nodes of the relation +\newcommand{\umlCNrelation}[4][]{% + \pgfkeys{/tikzuml/relation/.cd, arg1/.initial={}, arg2/.initial={}, arg/.initial={},% + mult1/.initial={}, mult2/.initial={}, mult/.initial={},% + pos1/.initial=0.2, pos2/.initial=0.8, pos/.initial=tikzumlEmpty,% + align1/.initial={}, align2/.initial={}, align/.initial={},% + anchor1/.initial=tikzumlEmpty, anchor2/.initial=tikzumlEmpty,% + stereo/.initial={}, pos stereo/.initial=1,% + style/.initial=->, name/.initial=relation-\thetikzumlRelationNum,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \errmessage{TIKZUML ERROR : in umlCNrelation, invalid option \keyname}% + }% + }% + \pgfkeys{/tikzuml/relation/.cd,#1}% + \pgfkeys{/tikzuml/relation/.cd, arg1/.get=\tikzumlCNRelationAttrName, arg2/.get=\tikzumlCNRelationAttrNameTO, arg/.get=\tikzumlCNRelationAttrNameTT,% + mult1/.get=\tikzumlCNRelationMultiplicity, mult2/.get=\tikzumlCNRelationMultiplicityTO, mult/.get=\tikzumlCNRelationMultiplicityTT,% + pos1/.get=\tikzumlCNRelationPosition, pos2/.get=\tikzumlCNRelationPositionTO, pos/.get=\tikzumlCNRelationPositionTT,% + align1/.get=\tikzumlCNRelationAlign, align2/.get=\tikzumlCNRelationAlignTO, align/.get=\tikzumlCNRelationAlignTT,% + anchor1/.get=\tikzumlCNRelationSrcAnchor, anchor2/.get=\tikzumlCNRelationDestAnchor,% + stereo/.get=\tikzumlCNRelationStereoType, pos stereo/.get=\tikzumlCNRelationPositionStereotype,% + style/.get=\tikzumlCNRelationStyle, name/.get=\tikzumlCNRelationName% + }% + % + % managing \_ in class names for node names + \def\tikzumlSrcClassName{#2}% + \begingroup% + \def\_{_}\edef\x{\endgroup% + \def\noexpand\tikzumlSrcClassNodeName{\tikzumlSrcClassName}}\x% + % + \StrSubstitute{\tikzumlSrcClassNodeName}{:}{@COLON@}[\tikzumlSrcClassNodeName]% + \StrSubstitute{\tikzumlSrcClassNodeName}{\_}{@UNDERSCORE@}[\tikzumlSrcClassNodeName]% + % + \def\tikzumlDestClassName{#4}% + \begingroup% + \def\_{_}\edef\x{\endgroup% + \def\noexpand\tikzumlDestClassNodeName{\tikzumlDestClassName}}\x% + % + \StrSubstitute{\tikzumlDestClassNodeName}{:}{@COLON@}[\tikzumlDestClassNodeName]% + \StrSubstitute{\tikzumlDestClassNodeName}{\_}{@UNDERSCORE@}[\tikzumlDestClassNodeName]% + % + % managing alias keys + \def\tikzumlCNRelationAttrNameT{\tikzumlCNRelationAttrNameTO\tikzumlCNRelationAttrNameTT}% + \def\tikzumlCNRelationMultiplicityT{\tikzumlCNRelationMultiplicityTO\tikzumlCNRelationMultiplicityTT}% + \def\tikzumlCNRelationAlignT{\tikzumlCNRelationAlignTO\tikzumlCNRelationAlignTT}% + \def\orientationT{\orientationTO\orientationTT}% + % + \ifthenelse{\equal{\tikzumlCNRelationPositionTT}{tikzumlEmpty}}{% + \def\tikzumlCNRelationPositionT{\tikzumlCNRelationPositionTO}% + }{% + \def\tikzumlCNRelationPositionT{\tikzumlCNRelationPositionTT}% + }% + % + \def\attrAlign{}% + \def\multAlign{}% + \def\attrAlignT{}% + \def\multAlignT{}% + % + \ifthenelse{\equal{\tikzumlCNRelationAlign}{left}}{% + \def\attrAlign{above right}% + \def\multAlign{below right}% + }{% + \ifthenelse{\equal{\tikzumlCNRelationAlign}{right}}{% + \def\attrAlign{above left}% + \def\multAlign{below left}% + }{}% + }% + % + \ifthenelse{\equal{\tikzumlCNRelationAlignT}{left}}{% + \def\attrAlignT{above right}% + \def\multAlignT{below right}% + }{% + \ifthenelse{\equal{\tikzumlCNRelationAlignT}{right}}{% + \def\attrAlignT{above left}% + \def\multAlignT{below left}% + }{}% + }% + % + % def stereotype + \ifthenelse{\equal{\tikzumlCNRelationStereoType}{}}{% + \def\stereotype{}% + }{% + \def\stereotype{$\ll$\tikzumlCNRelationStereoType$\gg$}% + }% + % + % def anchors macros + \ifthenelse{\equal{\tikzumlCNRelationSrcAnchor}{tikzumlEmpty}}{% + \def\tikzumlCNRelationSrcAnchor{}% + }{% + \let\tikzumlCNRelationSrcAnchorold\tikzumlCNRelationSrcAnchor% + \def\tikzumlCNRelationSrcAnchor{.\tikzumlCNRelationSrcAnchorold}% + }% + % + \ifthenelse{\equal{\tikzumlCNRelationDestAnchor}{tikzumlEmpty}}{% + \def\tikzumlCNRelationDestAnchor{}% + }{% + \let\tikzumlCNRelationDestAnchorold\tikzumlCNRelationDestAnchor% + \def\tikzumlCNRelationDestAnchor{.\tikzumlCNRelationDestAnchorold}% + }% + % + \setcounter{pos}{100*\real{\tikzumlCNRelationPosition}}% + \setcounter{posT}{100*\real{\tikzumlCNRelationPositionT}}% + \setcounter{posStereo}{100*\real{\tikzumlCNRelationPositionStereotype}}% + % + % straight line + %\setcounter{tikzumlControlNodesNum}{1}% + % + \def\tikzumlFirstArc{node[midway, name=\tikzumlCNRelationName-1, anchor=center] {} }% + \def\tikzumlLastArc{node[midway, name=\tikzumlCNRelationName-3, anchor=center]{} }% + \def\posAttrName{}% + \def\posMultiplicity{}% + \def\posAttrNameT{}% + \def\posMultiplicityT{}% + % + \begin{pgfonlayer}{connections}% + \node (\tikzumlCNRelationName-2) at (#3) {};% + \end{pgfonlayer}% + % + \ifnum\theposStereo>100% + \pgfmathsetmacro{\tikzumlCNRelationPositionStereotype}{(\theposStereo-100)/100}% + \xdef\tikzumlLastArc{\tikzumlLastArc node[pos=\tikzumlCNRelationPositionStereotype, anchor=center] {\stereotype} }% + \else% + \xdef\tikzumlFirstArc{\tikzumlFirstArc node[pos=\tikzumlCNRelationPositionStereotype, anchor=center] {\stereotype} }% + \fi% + % + \ifnum\thepos>100% + \pgfmathsetmacro{\tikzumlCNRelationPosition}{(\thepos-100)/100}% + \def\arcNum{2}% + \else% + \def\arcNum{1}% + \ifnum\thepos=100% + \def\posAttrName{above left}% + \def\posMultiplicity{below right}% + \fi% + \fi% + % + \ifnum\arcNum=1% + \xdef\tikzumlFirstArc{\tikzumlFirstArc node[pos=\tikzumlCNRelationPosition, \posAttrName, \attrAlign] {\tikzumlCNRelationAttrName}% + node[pos=\tikzumlCNRelationPosition, swap, \posMultiplicity, \multAlign] {\tikzumlCNRelationMultiplicity} }% + \fi% + \ifnum\arcNum=2% + \xdef\tikzumlLastArc{\tikzumlLastArc node[pos=\tikzumlCNRelationPosition, \posAttrName, \attrAlign] {\tikzumlCNRelationAttrName}% + node[pos=\tikzumlCNRelationPosition, swap, \posMultiplicity, \multAlign] {\tikzumlCNRelationMultiplicity} }% + \fi% + % + \ifnum\theposT>100% + \pgfmathsetmacro{\tikzumlCNRelationPositionT}{(\theposT-100)/100}% + \def\arcNumT{2}% + \else% + \def\arcNumT{1}% + \ifnum\theposT=100% + \def\posAttrNameT{above left}% + \def\posMultiplicityT{below right}% + \fi% + \fi% + % + \ifnum\arcNumT=1% + \xdef\tikzumlFirstArc{\tikzumlFirstArc node[pos=\tikzumlCNRelationPositionT, \posAttrNameT, \attrAlignT] {\tikzumlCNRelationAttrNameT}% + node[pos=\tikzumlCNRelationPositionT, swap, \posMultiplicityT, \multAlignT] {\tikzumlCNRelationMultiplicityT} }% + \fi% + \ifnum\arcNumT=2% + \xdef\tikzumlLastArc{\tikzumlLastArc node[pos=\tikzumlCNRelationPositionT, \posAttrNameT, \attrAlignT] {\tikzumlCNRelationAttrNameT}% + node[pos=\tikzumlCNRelationPositionT, swap, \posMultiplicityT, \multAlignT] {\tikzumlCNRelationMultiplicityT} }% + \fi% + % + \xdef\tikzumlPath{(\tikzumlSrcClassNodeName\tikzumlCNRelationSrcAnchor) -- \tikzumlFirstArc (\tikzumlCNRelationName-2.base) -- \tikzumlLastArc (\tikzumlDestClassNodeName\tikzumlCNRelationDestAnchor) }% + + \begin{pgfonlayer}{connections}% + \ifthenelse{\equal{\tikzumlCNRelationStyle}{tikzuml nesting style}}{% + \pgfarrowsdeclare{nested}{nested}{...} + { + \tikzumlNestingSymbolSize=0.2pt% + \advance\tikzumlNestingSymbolSize by .5\pgflinewidth% + \pgfsetdash{}{0pt} % do not dash + \pgfsetroundjoin % fix join + \pgfsetroundcap % fix cap + \pgfpathmoveto{\pgfpoint{-16*\tikzumlNestingSymbolSize}{0pt}}% + \pgfpatharc{180}{90}{8*\tikzumlNestingSymbolSize}% + \pgfpatharc{90}{0}{8*\tikzumlNestingSymbolSize}% + \pgfpatharc{0}{-90}{8*\tikzumlNestingSymbolSize}% + \pgfpatharc{-90}{-180}{8*\tikzumlNestingSymbolSize}% + \pgfpathmoveto{\pgfpoint{-8*\tikzumlNestingSymbolSize}{8*\tikzumlNestingSymbolSize}}% + \pgfpathlineto{\pgfpoint{-8*\tikzumlNestingSymbolSize}{-8*\tikzumlNestingSymbolSize}}% + \pgfusepathqstroke% + }% + \draw[auto, \tikzumlCNRelationStyle, nested-, font=\tikzumlDefaultFont] \tikzumlPath ;% + }{ + \draw[auto, \tikzumlCNRelationStyle, font=\tikzumlDefaultFont] \tikzumlPath ;% + } + \end{pgfonlayer}% + % + \stepcounter{tikzumlRelationNum}% +}% +% +% shortcuts for cnrelations +\newcommand{\umlCNinherit}[4][]{\umlCNrelation[style={tikzuml inherit style}, #1]{#2}{#3}{#4}}% +\newcommand{\umlCNimpl}[4][]{\umlCNrelation[style={tikzuml implements style}, #1]{#2}{#3}{#4}}% +\newcommand{\umlCNreal}[4][]{\umlCNrelation[style={tikzuml implements style}, #1]{#2}{#3}{#4}}% +\newcommand{\umlCNassoc}[4][]{\umlCNrelation[style={tikzuml association style}, #1]{#2}{#3}{#4}}% +\newcommand{\umlCNuniassoc}[4][]{\umlCNrelation[style={tikzuml unidirectional association style}, #1]{#2}{#3}{#4}}% +\newcommand{\umlCNaggreg}[4][]{\umlCNrelation[style={tikzuml aggregation style}, #1]{#2}{#3}{#4}}% +\newcommand{\umlCNuniaggreg}[4][]{\umlCNrelation[style={tikzuml unidirectional aggregation style}, #1]{#2}{#3}{#4}}% +\newcommand{\umlCNcompo}[4][]{\umlCNrelation[style={tikzuml composition style}, #1]{#2}{#3}{#4}}% +\newcommand{\umlCNunicompo}[4][]{\umlCNrelation[style={tikzuml unidirectional composition style}, #1]{#2}{#3}{#4}}% +\newcommand{\umlCNimport}[4][]{\umlCNrelation[style={tikzuml import style}, #1]{#2}{#3}{#4}}% +\newcommand{\umlCNnest}[4][]{\umlCNrelation[style={tikzuml nesting style}, #1]{#2}{#3}{#4}}% +\newcommand{\umlCNdep}[4][]{\umlCNrelation[style={tikzuml dependency style}, #1]{#2}{#3}{#4}}% +\newcommand{\umlCNfriend}[4][]{% + \pgfkeys{/tikzuml/friendrelation/.cd,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{stereo}}{% + \errmessage{TIKZUML ERROR : in umlCNfriend, forbidden option stereo}% + }{}% + }}% + \pgfkeys{/tikzuml/friendrelation/.cd, #1}% + \umlCNrelation[stereo=friend, style={tikzuml dependency style}, #1]{#2}{#3}{#4}% +}% +% +% define a note +% arg : attached class +% label of the note +% optional : x,y: coordinates of the note +% width: width of the note +% geometry: geometry of the relation between the note and what it is about +% weight: barycentric weight for a 3-line relation +% arm: length of the first arm +% anchor1, anchor2: anchors of the relation +% draw, fill, text: colors +% style: to manage every default TikZ option +% no coords: to tell that the note position is defined relatively +% to another node (automatically used with TikZ options above, below, left, right, below left, ...) +\newcommand{\umlnote}[3][]{% + \pgfkeys{/tikzuml/note/.cd, x/.initial=\tikzumlDefaultX, y/.initial=\tikzumlDefaultX, width/.initial=\tikzumlNoteDefaultWidth,% + geometry/.initial=\tikzumlRelationDefaultGeometry,% + weight/.initial=\tikzumlRelationDefaultWeight, arm/.initial=auto, style/.style={},% + anchor1/.initial=tikzumlEmpty, anchor2/.initial=tikzumlEmpty,% + draw/.initial=\tikzumlDefaultDrawColor, fill/.initial=\tikzumlNoteDefaultFillColor,% + text/.initial=\tikzumlDefaultTextColor,% + no coords/.is if=tikzumlnoteWithoutCoords,% + no coords=false,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{above}\OR% + \equal{\keyname}{left}\OR% + \equal{\keyname}{below}\OR% + \equal{\keyname}{right}\OR% + \equal{\keyname}{above left}\OR% + \equal{\keyname}{above right}\OR% + \equal{\keyname}{below left}\OR% + \equal{\keyname}{below right}}{% + \IfSubStr{\keyvalue}{ of }{% + \pgfkeys{/tikzuml/note/.cd, no coords}% + }{}% + }{}% + \ifx\keyvalue\pgfkeysnovalue% + \pgfkeys{/tikzuml/note/.cd, style/.append style/.expand once={\keyname}}% + \else% + \pgfkeys{/tikzuml/note/.cd, style/.append style/.expand twice={\expandafter\keyname\expandafter=\keyvalue}}% + \fi% + %\errmessage{TIKZUML ERROR : in umlnote, invalid option \keyname}% + }% + }% + \pgfkeys{/tikzuml/note/.cd, #1}% + \pgfkeys{/tikzuml/note/.cd, x/.get=\tikzumlNoteX, y/.get=\tikzumlNoteY, width/.get=\tikzumlNoteTextWidth,% + geometry/.get=\tikzumlNoteGeometry,% + weight/.get=\tikzumlNoteWeight, arm/.get=\tikzumlNoteArm,% + anchor1/.get=\tikzumlNoteSrcAnchor, anchor2/.get=\tikzumlNoteDestAnchor,% + draw/.get=\tikzumlNoteDrawColor, fill/.get=\tikzumlNoteFillColor,% + text/.get=\tikzumlNoteTextColor% + }% + % + \def\tikzumlClassName{#2}% + \begingroup% + \def\_{_}\edef\x{\endgroup% + \def\noexpand\tikzumlClassNodeName{\tikzumlClassName}}\x% + % + % def anchors macros + \ifthenelse{\equal{\tikzumlNoteSrcAnchor}{tikzumlEmpty}}{% + \def\tikzumlNoteSrcAnchor{}% + }{% + \let\tikzumlNoteSrcAnchorold\tikzumlNoteSrcAnchor% + \def\tikzumlNoteSrcAnchor{.\tikzumlNoteSrcAnchorold}% + }% + % + \ifthenelse{\equal{\tikzumlNoteDestAnchor}{tikzumlEmpty}}{% + \def\tikzumlNoteDestAnchor{}% + }{% + \let\tikzumlNoteDestAnchorold\tikzumlNoteDestAnchor% + \def\tikzumlNoteDestAnchor{.\tikzumlNoteDestAnchorold}% + }% + % + \iftikzumlnoteWithoutCoords% + \node[text=\tikzumlNoteTextColor, text width=\tikzumlNoteTextWidth, font=\tikzumlDefaultFont, outer sep=0, inner xsep=1ex, inner ysep=3ex, /tikzuml/note/style] (note-\thetikzumlNoteNum-coord) {#3};% + \else% + \node[text=\tikzumlNoteTextColor, text width=\tikzumlNoteTextWidth, font=\tikzumlDefaultFont, outer sep=0, inner xsep=1ex, inner ysep=3ex, /tikzuml/note/style] (note-\thetikzumlNoteNum-coord) at (\tikzumlNoteX, \tikzumlNoteY) {#3};% + \fi% + \draw (note-\thetikzumlNoteNum-coord.north east) node[name=note-\thetikzumlNoteNum-right-top, below=2ex, coordinate] {};% + \draw (note-\thetikzumlNoteNum-coord.north east) node[name=note-\thetikzumlNoteNum-top-right, left=2ex, coordinate] {};% + \draw[draw=\tikzumlNoteDrawColor, fill=\tikzumlNoteFillColor] (note-\thetikzumlNoteNum-coord.south west) -- (note-\thetikzumlNoteNum-coord.south east) -- (note-\thetikzumlNoteNum-right-top.base) -- (note-\thetikzumlNoteNum-top-right.base) -- (note-\thetikzumlNoteNum-coord.north west) -- cycle;% + \node[text=\tikzumlNoteTextColor, text width=\tikzumlNoteTextWidth, outer sep=0, inner xsep=1ex, inner ysep=3ex, font=\tikzumlDefaultFont] (note-\thetikzumlNoteNum) at (note-\thetikzumlNoteNum-coord) {#3};% + \draw[draw=\tikzumlNoteDrawColor] (note-\thetikzumlNoteNum-right-top) -| (note-\thetikzumlNoteNum-top-right);% + % + \pgfmathsetmacro{\tikzumlNoteWeightT}{1.0-\tikzumlNoteWeight}% + \node (note-\thetikzumlNoteNum-middle) at (barycentric cs:note-\thetikzumlNoteNum-coord=\tikzumlNoteWeight,\tikzumlClassNodeName=\tikzumlNoteWeightT) {};% + % + \ifthenelse{\equal{\tikzumlNoteGeometry}{--}% + \OR\equal{\tikzumlNoteGeometry}{-|}% + \OR\equal{\tikzumlNoteGeometry}{|-}}{% + \edef\tikzumlnotepath{\tikzumlNoteGeometry} + }{% + \ifthenelse{\equal{\tikzumlNoteGeometry}{-|-}}{% + \ifthenelse{\equal{\tikzumlNoteArm}{auto}}{% + \edef\tikzumlnotepath{-- (note-\thetikzumlNoteNum-coord\tikzumlNoteSrcAnchor -| note-\thetikzumlNoteNum-middle.center) -- (note-\thetikzumlNoteNum-middle.center) -- (note-\thetikzumlNoteNum-middle.center |- \tikzumlClassNodeName\tikzumlNoteDestAnchor) --}% + }{% + \draw (note-\thetikzumlNoteNum-coord\tikzumlNoteSrcAnchor)+(\tikzumlNoteArm,0) node[name=note-\thetikzumlNoteNum-tmp] {}; + \edef\tikzumlnotepath{-- (note-\thetikzumlNoteNum-tmp.center) |-}% + }% + }{% + \ifthenelse{\equal{\tikzumlNoteGeometry}{|-|}}{% + \ifthenelse{\equal{\tikzumlNoteArm}{auto}}{% + \edef\tikzumlnotepath{-- (note-\thetikzumlNoteNum-coord\tikzumlNoteSrcAnchor |- note-\thetikzumlNoteNum-middle.center) -- (note-\thetikzumlNoteNum-middle.center) -- (note-\thetikzumlNoteNum-middle.center -| \tikzumlClassNodeName\tikzumlNoteDestAnchor) --}% + }{% + \draw (note-\thetikzumlNoteNum-coord\tikzumlNoteSrcAnchor)+(0,\tikzumlNoteArm) node[name=note-\thetikzumlNoteNum-tmp] {}; + \edef\tikzumlnotepath{-- (note-\thetikzumlNoteNum-tmp.center) -|}% + }% + + }{% + \errmessage{TIKZUML ERROR : Unknown geometry value !!! It should be in the following list : --, |-, -|, |-|, -|-}% + }% + }% + }% + % + \begin{pgfonlayer}{connections}% + \draw[dashed] (note-\thetikzumlNoteNum-coord\tikzumlNoteSrcAnchor) \tikzumlnotepath (\tikzumlClassNodeName\tikzumlNoteDestAnchor);% + \end{pgfonlayer}% + % + \stepcounter{tikzumlNoteNum}% +}% +% +% shortcuts for note with geometry +\newcommand{\umlHVnote}[3][]{% + \pgfkeys{/tikzuml/note/.cd,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{geometry}}{% + \errmessage{TIKZUML ERROR : in umlHVnote, forbidden option geometry}% + }{}% + }}% + \pgfkeys{/tikzuml/note/.cd, #1}% + \umlnote[geometry=-|, #1]{#2}{#3}% +}% +\newcommand{\umlVHnote}[3][]{% + \pgfkeys{/tikzuml/note/.cd,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{geometry}}{% + \errmessage{TIKZUML ERROR : in umlVHnote, forbidden option geometry}% + }{}% + }}% + \pgfkeys{/tikzuml/note/.cd, #1}% + \umlnote[geometry=|-, #1]{#2}{#3}% +}% +\newcommand{\umlVHVnote}[3][]{% + \pgfkeys{/tikzuml/note/.cd,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{geometry}}{% + \errmessage{TIKZUML ERROR : in umlVHVnote, forbidden option geometry}% + }{}% + }}% + \pgfkeys{/tikzuml/note/.cd, #1}% + \umlnote[geometry=|-|, #1]{#2}{#3}% +}% +\newcommand{\umlHVHnote}[3][]{% + \pgfkeys{/tikzuml/note/.cd,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{geometry}}{% + \errmessage{TIKZUML ERROR : in umlHVHnote, forbidden option geometry}% + }{}% + }}% + \pgfkeys{/tikzuml/note/.cd, #1}% + \umlnote[geometry=-|-, #1]{#2}{#3}% +}% +% +% define a uml association class (command) +% args : name of the class +% attributes of the class +% operations of the class +% optional : x,y: coordinates of the class +% width: width of the class node +% type: type of of class (class, interface, typedef, enum) +% template: template parameters +% name: name of the class node +% geometry: geometry of the line +% weight: barycentric weight of the middle part when geometry is a 3-line +% arm: length of first part when geometry id a 3-line +% anchor1, anchor2: src/dest anchors on linked classes +% style: style of the association class (association, aggregation, composition, inherit, ...) +% draw, fill, fill template, text: colors +% style: to manage every default TikZ option +% no coords: to tell that the class position is defined relatively +% to another node (automatically used with TikZ options above, below, left, right, below left, ...) +\newcommand{\umlassocclass}[5][]{% + \pgfkeys{/tikzuml/assocclass/.cd, x/.initial=\tikzumlDefaultX, y/.initial=\tikzumlDefaultX, + width/.initial=\tikzumlClassDefaultWidth, type/.initial=\tikzumlClassDefaultType, style/.style={},% + template/.initial={}, name/.initial=tikzumlEmpty, geometry/.initial=\tikzumlRelationDefaultGeometry,% + weight/.initial=\tikzumlRelationDefaultWeight, arm/.initial=auto,% + anchor1/.initial=tikzumlEmpty, anchor2/.initial=tikzumlEmpty,% + draw/.initial=\tikzumlDefaultDrawColor,% + fill template/.initial=\tikzumlClassTemplateFillColorDefaultFillColor,% + fill/.initial=\tikzumlClassDefaultFillColor,% + text/.initial=\tikzumlDefaultTextColor,% + no coords/.is if=tikzumlassocclassWithoutCoords,% + no coords=false,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{above}\OR% + \equal{\keyname}{left}\OR% + \equal{\keyname}{below}\OR% + \equal{\keyname}{right}\OR% + \equal{\keyname}{above left}\OR% + \equal{\keyname}{above right}\OR% + \equal{\keyname}{below left}\OR% + \equal{\keyname}{below right}}{% + \IfSubStr{\keyvalue}{ of }{% + \pgfkeys{/tikzuml/assocclass/.cd, no coords}% + }{}% + }{}% + \ifx\keyvalue\pgfkeysnovalue% + \pgfkeys{/tikzuml/assocclass/.cd, style/.append style/.expand once={\keyname}}% + \else% + \pgfkeys{/tikzuml/assocclass/.cd, style/.append style/.expand twice={\expandafter\keyname\expandafter=\keyvalue}}% + \fi% + %\errmessage{TIKZUML ERROR : in umlassocclass, invalid option \keyname}% + }% + }% + % + \pgfkeys{/tikzuml/assocclass/.cd,#1}% + \pgfkeys{/tikzuml/assocclass/.cd, x/.get=\tikzumlAssocClassX, y/.get=\tikzumlAssocClassY,% + width/.get=\tikzumlAssocClassMinimumWidth, type/.get=\tikzumlAssocClassTypeTmp,% + template/.get=\tikzumlAssocClassTemplateParam,% + name/.get=\tikzumlAssocClassName, geometry/.get=\tikzumlAssocClassGeometry,% + weight/.get=\tikzumlAssocClassWeight, arm/.get=\tikzumlAssocClassArm,% + anchor1/.get=\tikzumlAssocClassSrcAnchor,% + anchor2/.get=\tikzumlAssocClassDestAnchor,% + draw/.get=\tikzumlAssocClassDrawColor, fill/.get=\tikzumlAssocClassFillColor,% + text/.get=\tikzumlAssocClassTextColor, fill template/.get=\tikzumlAssocClassTemplateFillColor% + }% + % + \ifthenelse{\equal{\tikzumlAssocClassTypeTmp}{class}\OR\equal{\tikzumlAssocClassTypeTmp}{abstract}}{% + \def\tikzumlAssocClassType{}% + }{% + \def\tikzumlAssocClassType{$\ll$\tikzumlAssocClassTypeTmp$\gg$ \\}% + }% + % + \ifthenelse{\equal{\tikzumlAssocClassTemplateParam}{}}{% + \def\tikzumlAssocClassVPadding{}% + \def\tikzumlAssocClassHPadding{}% + }{% + \def\tikzumlAssocClassVPadding{\vspace{0.1em} \\}% + \def\tikzumlAssocClassHPadding{\hspace{0.5ex} $ $}% + }% + % + \def\tikzumlAssocClassName{#2}% + \def\tikzumlAssocClassRelationName{#3}% + % + \begingroup% + \def\_{_}\edef\x{\endgroup% + \def\noexpand\tikzumlAssocClassNodeName{\tikzumlAssocClassName}}\x% + % + \begingroup% + \def\_{_}\edef\x{\endgroup% + \def\noexpand\tikzumlAssocClassRelationNodeName{\tikzumlAssocClassRelationName}}\x% + % + \ifthenelse{\equal{\tikzumlAssocClassName}{tikzumlEmpty}}{}{% + \def\tikzumlAssocClassNodeName{\tikzumlAssocClassName}% + }% + % + \StrSubstitute{\tikzumlAssocClassNodeName}{:}{@COLON@}[\tikzumlAssocClassNodeName]% + \StrSubstitute{\tikzumlAssocClassNodeName}{\_}{@UNDERSCORE@}[\tikzumlAssocClassNodeName]% + % + \ifthenelse{\equal{\tikzumlAssocClassTypeTmp}{abstract}}{% + \let\tikzumlAssocClassNameOld\tikzumlAssocClassName% + \def\tikzumlAssocClassName{{\it \tikzumlAssocClassNameOld}}% + }{}% + % + \def\tikzumlAssocClassPos{\tikzumlAssocClassX,\tikzumlAssocClassY}% + \def\tikzumlAssocClassAttributes{#4}% + \def\tikzumlAssocClassOperations{#5}% + % + % def anchors macros + \ifthenelse{\equal{\tikzumlAssocClassSrcAnchor}{tikzumlEmpty}}{% + \def\tikzumlAssocClassSrcAnchor{}% + }{% + \let\tikzumlAssocClassSrcAnchorold\tikzumlAssocClassSrcAnchor% + \def\tikzumlAssocClassSrcAnchor{.\tikzumlAssocClassSrcAnchorold}% + }% + % + \ifthenelse{\equal{\tikzumlAssocClassDestAnchor}{tikzumlEmpty}}{% + \def\tikzumlAssocClassDestAnchor{}% + }{% + \let\tikzumlAssocClassDestAnchorold\tikzumlAssocClassDestAnchor% + \def\tikzumlAssocClassDestAnchor{.\tikzumlAssocClassDestAnchorold}% + }% + % + \iftikzumlassocclassWithoutCoords% + \node[tikzuml class style, draw=\tikzumlAssocClassDrawColor, fill=\tikzumlAssocClassFillColor, text=\tikzumlAssocClassTextColor, font=\tikzumlDefaultFont, minimum width=\tikzumlAssocClassMinimumWidth, /tikzuml/assocclass/style] (\tikzumlAssocClassNodeName) {\begin{tabular}{c}\tikzumlAssocClassVPadding \tikzumlAssocClassType \tikzumlAssocClassHPadding \textbf{\tikzumlAssocClassName} \tikzumlAssocClassHPadding \end{tabular}% + \nodepart{second}% + \begin{tabular}{l}% + \tikzumlAssocClassAttributes% + \end{tabular}% + \nodepart{third}% + \begin{tabular}{l}% + \tikzumlAssocClassOperations% + \end{tabular}% + };% + \else% + \node[tikzuml class style, draw=\tikzumlAssocClassDrawColor, fill=\tikzumlAssocClassFillColor, text=\tikzumlAssocClassTextColor, font=\tikzumlDefaultFont, minimum width=\tikzumlAssocClassMinimumWidth, /tikzuml/assocclass/style] (\tikzumlAssocClassNodeName) at (\tikzumlAssocClassPos) {\begin{tabular}{c}\tikzumlAssocClassVPadding \tikzumlAssocClassType \tikzumlAssocClassHPadding \textbf{\tikzumlAssocClassName} \tikzumlAssocClassHPadding \end{tabular}% + \nodepart{second}% + \begin{tabular}{l}% + \tikzumlAssocClassAttributes% + \end{tabular}% + \nodepart{third}% + \begin{tabular}{l}% + \tikzumlAssocClassOperations% + \end{tabular}% + };% + \fi% + % + \ifthenelse{\equal{\tikzumlAssocClassTemplateParam}{}}{}{% + \draw (\tikzumlAssocClassNodeName.north east) node[tikzuml template style, name=\tikzumlAssocClassNodeName-template, draw=\tikzumlAssocClassDrawColor, fill=\tikzumlAssocClassTemplateFillColor, text=\tikzumlAssocClassTextColor, font=\tikzumlDefaultFont] {\tikzumlAssocClassTemplateParam};% + }% + % + \pgfmathsetmacro{\tikzumlAssocClassWeightT}{1.0-\tikzumlAssocClassWeight} + \node (\tikzumlAssocClassNodeName-middle) at (barycentric cs:\tikzumlAssocClassNodeName=\tikzumlAssocClassWeight,\tikzumlAssocClassRelationNodeName=\tikzumlAssocClassWeightT) {};% + % + \ifthenelse{\equal{\tikzumlAssocClassGeometry}{--}\OR\equal{\tikzumlAssocClassGeometry}{-|}\OR\equal{\tikzumlAssocClassGeometry}{|-}}{% + \edef\tikzumlassocclasspath{\tikzumlAssocClassGeometry} + }{% + \ifthenelse{\equal{\tikzumlAssocClassGeometry}{-|-}}{% + \ifthenelse{\equal{\tikzumlAssocClassArm}{auto}}{% + \edef\tikzumlassocclasspath{-- (\tikzumlAssocClassNodeName\tikzumlAssocClassSrcAnchor -| \tikzumlAssocClassNodeName-middle.center) -- (\tikzumlAssocClassNodeName-middle.center) -- (\tikzumlAssocClassNodeName-middle.center |- \tikzumlAssocClassRelationNodeName\tikzumlAssocClassDestAnchor) --}% + }{% + \draw (\tikzumlAssocClassNodeName\tikzumlAssocClassSrcAnchor)+(\tikzumlAssocClassArm,0) node[name=\tikzumlAssocClassNodeName-tmp] {}; + \edef\tikzumlnotepath{-- (\tikzumlAssocClassNodeName-tmp.center) |-}% + }% + }{% + \ifthenelse{\equal{\tikzumlAssocClassGeometry}{|-|}}{% + \ifthenelse{\equal{\tikzumlAssocClassArm}{auto}}{% + \edef\tikzumlassocclasspath{-- (\tikzumlAssocClassNodeName\tikzumlAssocClassSrcAnchor |- \tikzumlAssocClassNodeName-middle.center) -- (\tikzumlAssocClassNodeName-middle.center) -- (\tikzumlAssocClassNodeName-middle.center -| \tikzumlAssocClassRelationNodeName\tikzumlAssocClassDestAnchor) --}% + }{% + \draw (\tikzumlAssocClassNodeName\tikzumlAssocClassSrcAnchor)+(0,\tikzumlAssocClassArm) node[name=\tikzumlAssocClassNodeName-tmp] {}; + \edef\tikzumlassocclasspath{-- (\thetikzumlAssocClassNodeName-tmp.center) -|}% + }% + + }{% + \errmessage{TIKZUML ERROR : Unknown geometry value !!! It should be in the following list : --, |-, -|, |-|, -|-}% + }% + }% + }% + % + \begin{pgfonlayer}{connections}% + \draw[dashed] (\tikzumlAssocClassNodeName\tikzumlAssocClassSrcAnchor) \tikzumlassocclasspath (\tikzumlAssocClassRelationNodeName\tikzumlAssocClassDestAnchor);% + \end{pgfonlayer}% + % + % add to fit + \ifnum\c@tikzumlPackageLevel>0% + \edef\tikzumlPackageFitOld{\csname tikzumlPackageFit\tikzumlPackage@parent @@\tikzumlPackage@fitname\endcsname}% + \ifthenelse{\equal{\tikzumlAssocClassTemplateParam}{}}{% + \expandafter\xdef\csname tikzumlPackageFit\tikzumlPackage@parent @@\tikzumlPackage@fitname\endcsname{\tikzumlPackageFitOld (\tikzumlAssocClassNodeName)(\tikzumlAssocClassNodeName-middle)}% + }{% + \expandafter\xdef\csname tikzumlPackageFit\tikzumlPackage@parent @@\tikzumlPackage@fitname\endcsname{\tikzumlPackageFitOld (\tikzumlAssocClassNodeName) (\tikzumlAssocClassNodeName-template)(\tikzumlAssocClassNodeName-middle)}% + }% + \stepcounter{tikzumlPackageClassNum}% + \fi% +}% +% +% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% use case diagrams % +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +\tikzstyle{tikzuml usecase style}=[ellipse, text centered]% +\tikzstyle{tikzuml actor style}=[ellipse, inner sep=0, outer sep=0]% +% +\newcounter{tikzumlSystemUseCaseNum}% +\newcounter{tikzumlSystemLevel}% +\newcounter{tikzumlUseCaseNum}% +\newcounter{tikzumlActorNum}% +% +\newif\iftikzumlusecaseWithoutCoords% +\newif\iftikzumlactorWithoutCoords% +% +% define a system +% arg : name +% optional : x, y: coordinates of the system +% draw, fill, text: colors +\newenvironment{umlsystem}[2][]{% + \gdef\tikzumlSystemFit{}% + \def\tikzumlSystemName{#2}% + \setcounter{tikzumlSystemUseCaseNum}{0}% + % + \pgfkeys{/tikzuml/system/.cd, x/.initial=\tikzumlDefaultX, y/.initial=\tikzumlDefaultX,% + draw/.initial=\tikzumlDefaultDrawColor, fill/.initial=\tikzumlSystemDefaultFillColor,% + text/.initial=\tikzumlDefaultTextColor,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \errmessage{TIKZUML ERROR : in umlsystem, invalid option \keyname}% + }% + }% + % + \pgfkeys{/tikzuml/system/.cd, #1}% + \pgfkeys{/tikzuml/system/.cd, x/.get=\tikzumlSystemXShift, y/.get=\tikzumlSystemYShift,% + draw/.get=\tikzumlSystemDrawColor, fill/.get=\tikzumlSystemFillColor,% + text/.get=\tikzumlSystemTextColor}% + % + \stepcounter{tikzumlSystemLevel}% + % + \begin{scope}[xshift=\tikzumlSystemXShift cm, yshift=\tikzumlSystemYShift cm]% +}{% + \addtocounter{tikzumlSystemLevel}{-1}% + % if contains no usecase, one define a fictive node to enable the fit option + \ifnum\c@tikzumlSystemUseCaseNum=0% + \node[inner xsep=10ex, inner ysep=1em] (\tikzumlSystemName-root) at (0,0) {};% + \xdef\tikzumlSystemFit{(\tikzumlSystemName-root)}% + \fi% + % + \begin{pgfonlayer}{background}% + \node[inner ysep=1em, inner xsep=2ex, fit = \tikzumlSystemFit] (\tikzumlSystemName-tmp) {};% + \node[text=\tikzumlSystemTextColor, font=\tikzumlDefaultFont] (\tikzumlSystemName-caption-tmp) at (\tikzumlSystemName-tmp.north) {\tikzumlSystemName};% + \node[draw=\tikzumlSystemDrawColor, fill=\tikzumlSystemFillColor, text=\tikzumlSystemTextColor, font=\tikzumlDefaultFont, inner ysep=1em, inner xsep=2ex, fit = (\tikzumlSystemName-tmp) (\tikzumlSystemName-caption-tmp)] (\tikzumlSystemName) {};% + \node[text=\tikzumlSystemTextColor, font=\tikzumlDefaultFont] (\tikzumlSystemName-caption) at (\tikzumlSystemName-caption-tmp.north) {\tikzumlSystemName};% + \end{pgfonlayer}% + \end{scope}% + % +}% +% +% define a use case +% arg : label of the use case +% optional : x, y: coordinates of the use case +% name: name of the node +% width: node width +% draw, fill, text: colors +% style: to manage every default TikZ option +% no coords: to tell that the use case position is defined relatively +% to another node (automatically used with TikZ options above, below, left, right, below left, ...) +\newcommand{\umlusecase}[2][]{% + \stepcounter{tikzumlUseCaseNum}% + \pgfkeys{/tikzuml/usecase/.cd, x/.initial=\tikzumlDefaultX, y/.initial=\tikzumlDefaultX, width/.initial=auto,% + name/.initial=usecase-\thetikzumlUseCaseNum,% + draw/.initial=\tikzumlDefaultDrawColor, fill/.initial=\tikzumlUseCaseDefaultFillColor,% + text/.initial=\tikzumlDefaultTextColor, style/.style={},% + no coords/.is if=tikzumlusecaseWithoutCoords,% + no coords=false,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{above}\OR% + \equal{\keyname}{left}\OR% + \equal{\keyname}{below}\OR% + \equal{\keyname}{right}\OR% + \equal{\keyname}{above left}\OR% + \equal{\keyname}{above right}\OR% + \equal{\keyname}{below left}\OR% + \equal{\keyname}{below right}}{% + \IfSubStr{\keyvalue}{ of }{% + \pgfkeys{/tikzuml/usecase/.cd, no coords}% + }{}% + }{}% + \ifx\keyvalue\pgfkeysnovalue% + \pgfkeys{/tikzuml/usecase/.cd, style/.append style/.expand once={\keyname}}% + \else% + \pgfkeys{/tikzuml/usecase/.cd, style/.append style/.expand twice={\expandafter\keyname\expandafter=\keyvalue}}% + \fi% + %\errmessage{TIKZUML ERROR : in umlusecase, invalid option \keyname}% + }% + }% + \pgfkeys{/tikzuml/usecase/.cd, #1}% + \pgfkeys{/tikzuml/usecase/.cd, x/.get=\tikzumlUseCaseX, y/.get=\tikzumlUseCaseY, width/.get=\tikzumlUseCaseTextWidth,% + name/.get=\tikzumlUseCaseName,% + draw/.get=\tikzumlUseCaseDrawColor, fill/.get=\tikzumlUseCaseFillColor,% + text/.get=\tikzumlUseCaseTextColor% + }% + % + \def\tikzumlUseCaseText{#2}% + % + \def\tikzumlUseCasePos{\tikzumlUseCaseX,\tikzumlUseCaseY}% + % + \ifthenelse{\equal{\tikzumlUseCaseTextWidth}{auto}}{% + \iftikzumlusecaseWithoutCoords% + \node[tikzuml usecase style, draw=\tikzumlUseCaseDrawColor, fill=\tikzumlUseCaseFillColor, text=\tikzumlUseCaseTextColor, font=\tikzumlDefaultFont, /tikzuml/usecase/style] (\tikzumlUseCaseName) {\tikzumlUseCaseText};% + \else% + \node[tikzuml usecase style, draw=\tikzumlUseCaseDrawColor, fill=\tikzumlUseCaseFillColor, text=\tikzumlUseCaseTextColor, font=\tikzumlDefaultFont, /tikzuml/usecase/style] (\tikzumlUseCaseName) at (\tikzumlUseCasePos) {\tikzumlUseCaseText};% + \fi% + }{% + \iftikzumlusecaseWithoutCoords% + \node[tikzuml usecase style, draw=\tikzumlUseCaseDrawColor, fill=\tikzumlUseCaseFillColor, text=\tikzumlUseCaseTextColor, font=\tikzumlDefaultFont, text width=\tikzumlUseCaseTextWidth, /tikzuml/usecase/style] (\tikzumlUseCaseName) {\tikzumlUseCaseText};% + \else% + \node[tikzuml usecase style, draw=\tikzumlUseCaseDrawColor, fill=\tikzumlUseCaseFillColor, text=\tikzumlUseCaseTextColor, font=\tikzumlDefaultFont, text width=\tikzumlUseCaseTextWidth, /tikzuml/usecase/style] (\tikzumlUseCaseName) at (\tikzumlUseCasePos) {\tikzumlUseCaseText};% + \fi% + }% + % + % add to fit + \ifnum\c@tikzumlSystemLevel>0% + \let\tikzumlSystemFitOld\tikzumlSystemFit% + \xdef\tikzumlSystemFit{\tikzumlSystemFitOld (\tikzumlUseCaseName)}% + \stepcounter{tikzumlSystemUseCaseNum}% + \fi% +}% +% +% define the actor symbol +% optional : global tikzpicture styles +\newcommand{\picturedactor}[1]{% + \pgfkeys{/tikzuml/picactor/.cd, scale/.initial=1, .unknown/.code={}}% + \pgfkeys{/tikzuml/picactor/.cd,#1}% + \pgfkeys{/tikzuml/picactor/.cd, scale/.get=\tikzumlPicturedActorScale}% + % + \begin{tikzpicture}[#1]% + \coordinate (head) at (0,4ex);% + \coordinate (left-hand) at (-2ex,2ex);% + \coordinate (right-hand) at (2ex,2ex);% + \coordinate (left-foot) at (-2ex,-2ex);% + \coordinate (right-foot) at (2ex,-2ex);% + \coordinate (empty) at (0,-3ex);% + \draw (empty) (0,0) -- (head);% + \draw (left-hand) -- (right-hand);% + \draw (0,0) -- (left-foot) (0,0) -- (right-foot);% + \node[fill, draw, circle, inner sep=\tikzumlPicturedActorScale*0.3333ex, minimum size=\tikzumlPicturedActorScale*2ex, anchor=base] at (head) {};% + \end{tikzpicture}% +}% +% +% define an actor +% arg : var name +% optional : x, y: coordinates of the actor +% scale: scale factor of the actor symbol +% below: distance between the actor symbol and its name below +% draw, text: colors +% style: to manage every default TikZ option +% no coords: to tell that the actor position is defined relatively +% to another node (automatically used with TikZ options above, below, left, right, below left, ...) +\newcommand{\umlactor}[2][]{% + \stepcounter{tikzumlActorNum}% + \pgfkeys{/tikzuml/actor/.cd, x/.initial=\tikzumlDefaultX, y/.initial=\tikzumlDefaultX, scale/.initial=1, below/.initial=\tikzumlActorDefaultBelow,% + draw/.initial=\tikzumlDefaultDrawColor, text/.initial=\tikzumlDefaultTextColor,% + style/.style={},% + no coords/.is if=tikzumlactorWithoutCoords,% + no coords=false,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{above}\OR% + \equal{\keyname}{left}\OR% + \equal{\keyname}{below}\OR% + \equal{\keyname}{right}\OR% + \equal{\keyname}{above left}\OR% + \equal{\keyname}{above right}\OR% + \equal{\keyname}{below left}\OR% + \equal{\keyname}{below right}}{% + \IfSubStr{\keyvalue}{ of }{% + \pgfkeys{/tikzuml/actor/.cd, no coords}% + }{}% + }{}% + \ifx\keyvalue\pgfkeysnovalue% + \pgfkeys{/tikzuml/actor/.cd, style/.append style/.expand once={\keyname}}% + \else% + \pgfkeys{/tikzuml/actor/.cd, style/.append style/.expand twice={\expandafter\keyname\expandafter=\keyvalue}}% + \fi% + %\errmessage{TIKZUML ERROR : in umlactor, invalid option \keyname}% + }% + }% + \pgfkeys{/tikzuml/actor/.cd, #1}% + \pgfkeys{/tikzuml/actor/.cd,% + x/.get=\tikzumlActorX, y/.get=\tikzumlActorY, scale/.get=\tikzumlActorScale,% + below/.get=\tikzumlActorBelow,% + draw/.get=\tikzumlActorDrawColor, text/.get=\tikzumlActorTextColor}% + % + \def\tikzumlActorName{#2}% + \begingroup% + \def\_{_}\edef\x{\endgroup% + \def\noexpand\tikzumlActorNodeName{\tikzumlActorName}}\x% + % + \def\tikzumlActorPos{\tikzumlActorX,\tikzumlActorY}% + % + \iftikzumlactorWithoutCoords% + \node[tikzuml actor style, text=\tikzumlActorTextColor, font=\tikzumlDefaultFont, /tikzuml/actor/style] (\tikzumlActorNodeName) {\picturedactor{scale=\tikzumlActorScale, fill=white, draw=\tikzumlActorDrawColor, thick}};% + \else% + \node[tikzuml actor style, text=\tikzumlActorTextColor, font=\tikzumlDefaultFont, /tikzuml/actor/style] (\tikzumlActorNodeName) at (\tikzumlActorPos) {\picturedactor{scale=\tikzumlActorScale, fill=white, draw=\tikzumlActorDrawColor, thick}};% + \fi% + \node[text=\tikzumlActorTextColor, font=\tikzumlDefaultFont, below=\tikzumlActorScale*\tikzumlActorBelow] at (\tikzumlActorNodeName) {\tikzumlActorName};% + % +}% + +% shortcuts for include and extend relation +\newcommand{\umlinclude}[3][]{% + \pgfkeys{/tikzuml/includerelation/.cd,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{stereo}}{% + \errmessage{TIKZUML ERROR : in umlinclude, forbidden option stereo}% + }{% + \ifthenelse{\equal{\keyname}{geometry}}{% + \errmessage{TIKZUML ERROR : in umlinclude, forbidden option geometry}% + }{}% + }% + }}% + \pgfkeys{/tikzuml/includerelation/.cd, #1}% + \umlrelation[stereo=include, style={tikzuml dependency style}, #1]{#2}{#3}% +}% +\newcommand{\umlextend}[3][]{% + \pgfkeys{/tikzuml/extendrelation/.cd,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{stereo}}{% + \errmessage{TIKZUML ERROR : in umlextend, forbidden option stereo}% + }{% + \ifthenelse{\equal{\keyname}{geometry}}{% + \errmessage{TIKZUML ERROR : in umlextend, forbidden option geometry}% + }{}% + }% + }}% + \pgfkeys{/tikzuml/extendrelation/.cd, #1}% + \umlrelation[stereo=extend, style={tikzuml dependency style}, #1]{#2}{#3}% +}% +% +\newcommand{\umlHVinclude}[3][]{% + \pgfkeys{/tikzuml/includerelation/.cd,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{stereo}}{% + \errmessage{TIKZUML ERROR : in umlHVinclude, forbidden option stereo}% + }{% + \ifthenelse{\equal{\keyname}{geometry}}{% + \errmessage{TIKZUML ERROR : in umlHVinclude, forbidden option geometry}% + }{}% + }% + }}% + \pgfkeys{/tikzuml/includerelation/.cd, #1}% + \umlrelation[geometry=-|, stereo=include, style={tikzuml dependency style}, #1]{#2}{#3}% +}% +\newcommand{\umlHVextend}[3][]{% + \pgfkeys{/tikzuml/extendrelation/.cd,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{stereo}}{% + \errmessage{TIKZUML ERROR : in umlHVextend, forbidden option stereo}% + }{% + \ifthenelse{\equal{\keyname}{geometry}}{% + \errmessage{TIKZUML ERROR : in umlHVextend, forbidden option geometry}% + }{}% + }% + }}% + \pgfkeys{/tikzuml/extendrelation/.cd, #1}% + \umlrelation[geometry=-|, stereo=extend, style={tikzuml dependency style}, #1]{#2}{#3}% +}% +% +\newcommand{\umlVHinclude}[3][]{% + \pgfkeys{/tikzuml/includerelation/.cd,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{stereo}}{% + \errmessage{TIKZUML ERROR : in umlVHinclude, forbidden option stereo}% + }{% + \ifthenelse{\equal{\keyname}{geometry}}{% + \errmessage{TIKZUML ERROR : in umlVHinclude, forbidden option geometry}% + }{}% + }% + }}% + \pgfkeys{/tikzuml/includerelation/.cd, #1}% + \umlrelation[geometry=|-, stereo=include, style={tikzuml dependency style}, #1]{#2}{#3}% +}% +\newcommand{\umlVHextend}[3][]{% + \pgfkeys{/tikzuml/extendrelation/.cd,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{stereo}}{% + \errmessage{TIKZUML ERROR :in umlVHextend, forbidden option stereo}% + }{% + \ifthenelse{\equal{\keyname}{geometry}}{% + \errmessage{TIKZUML ERROR : in umlVHextend, forbidden option geometry}% + }{}% + }% + }}% + \pgfkeys{/tikzuml/extendrelation/.cd, #1}% + \umlrelation[geometry=|-, stereo=extend, style={tikzuml dependency style}, #1]{#2}{#3}% +}% +% +\newcommand{\umlHVHinclude}[3][]{% + \pgfkeys{/tikzuml/includerelation/.cd,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{stereo}}{% + \errmessage{TIKZUML ERROR : in umlHVHinclude, forbidden option stereo}% + }{% + \ifthenelse{\equal{\keyname}{geometry}}{% + \errmessage{TIKZUML ERROR : in umlHVHinclude, forbidden option geometry}% + }{}% + }% + }}% + \pgfkeys{/tikzuml/includerelation/.cd, #1}% + \umlrelation[geometry=-|-, stereo=include, style={tikzuml dependency style}, #1]{#2}{#3}% +}% +\newcommand{\umlHVHextend}[3][]{% + \pgfkeys{/tikzuml/extendrelation/.cd,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{stereo}}{% + \errmessage{TIKZUML ERROR : in umlHVHextend, forbidden option stereo}% + }{% + \ifthenelse{\equal{\keyname}{geometry}}{% + \errmessage{TIKZUML ERROR : in umlHVHextend, forbidden option geometry}% + }{}% + }% + }}% + \pgfkeys{/tikzuml/extendrelation/.cd, #1}% + \umlrelation[geometry=-|-, stereo=extend, style={tikzuml dependency style}, #1]{#2}{#3}% +}% +% +\newcommand{\umlVHVinclude}[3][]{% + \pgfkeys{/tikzuml/includerelation/.cd,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{stereo}}{% + \errmessage{TIKZUML ERROR : in umlVHVinclude, forbidden option stereo}% + }{% + \ifthenelse{\equal{\keyname}{geometry}}{% + \errmessage{TIKZUML ERROR : in umlVHVinclude, forbidden option geometry}% + }{}% + }% + }}% + \pgfkeys{/tikzuml/includerelation/.cd, #1}% + \umlrelation[geometry=|-|, stereo=include, style={tikzuml dependency style}, #1]{#2}{#3}% +}% +\newcommand{\umlVHVextend}[3][]{% + \pgfkeys{/tikzuml/extendrelation/.cd,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{stereo}}{% + \errmessage{TIKZUML ERROR : in umlVHVextend, forbidden option stereo}% + }{% + \ifthenelse{\equal{\keyname}{geometry}}{% + \errmessage{TIKZUML ERROR : in umlVHVextend, forbidden option geometry}% + }{}% + }% + }}% + \pgfkeys{/tikzuml/extendrelation/.cd, #1}% + \umlrelation[geometry=|-|, stereo=extend, style={tikzuml dependency style}, #1]{#2}{#3}% +}% +% +\newcommand{\umlCNinclude}[4][]{% + \pgfkeys{/tikzuml/includerelation/.cd,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{stereo}}{% + \errmessage{TIKZUML ERROR : in umlCNinclude, forbidden option stereo}% + }{}% + }}% + \pgfkeys{/tikzuml/includerelation/.cd, #1}% + \umlCNrelation[stereo=include, style={tikzuml dependency style}, #1]{#2}{#3}{#4}% +}% +\newcommand{\umlCNextend}[4][]{% + \pgfkeys{/tikzuml/extendrelation/.cd,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{stereo}}{% + \errmessage{TIKZUML ERROR : in umlCNextend, forbidden option stereo}% + }{}% + }}% + \pgfkeys{/tikzuml/extendrelation/.cd, #1}% + \umlCNrelation[stereo=extend, style={tikzuml dependency style}, #1]{#2}{#3}{#4}% +}% +% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% state diagrams % +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +\tikzstyle{tikzuml state style}=[rectangle split, rectangle split parts=2, rounded corners, inner xsep=1.5ex]% +\tikzstyle{tikzuml transition style}=[color=\tikzumlDefaultDrawColor, rounded corners, -angle 45]% +% +\newcounter{tikzumlStateJoinNum}% +\newcounter{tikzumlStateDecisionNum}% +\newcounter{tikzumlStateInitialNum}% +\newcounter{tikzumlStateFinalNum}% +\newcounter{tikzumlStateEnterNum}% +\newcounter{tikzumlStateExitNum}% +\newcounter{tikzumlStateEndNum}% +\newcounter{tikzumlStateHistoryNum}% +\newcounter{tikzumlStateDeepHistoryNum}% +\newcounter{tikzumlStateLevel}% +\newcounter{tikzumlStateSubStateNum}% +\newcounter{tikzumlStateText}% +% +\newif\iftikzumlstatejoinWithoutCoords% +\newif\iftikzumlstatedecisionWithoutCoords% +\newif\iftikzumlstateinitialWithoutCoords% +\newif\iftikzumlstatefinalWithoutCoords% +\newif\iftikzumlstateenterWithoutCoords% +\newif\iftikzumlstateexitWithoutCoords% +\newif\iftikzumlstateendWithoutCoords% +\newif\iftikzumlstatehistoryWithoutCoords% +\newif\iftikzumlstatedeephistoryWithoutCoords% +\newif\iftikzumlstateWithoutCoords% +% +% define a uml join state +% args : name of the state +% optional : x,y: coordinates of the state +% width: width of the state node +% name: name of the state node +% color: color of the join symbol +% style: to manage every default TikZ option +% no coords: to tell that the state position is defined relatively +% to another node (automatically used with TikZ options above, below, left, right, below left, ...) +\newcommand{\umlstatejoin}[1][]{% + \pgfkeys{/tikzuml/statejoin/.cd, x/.initial=\tikzumlDefaultX, y/.initial=\tikzumlDefaultX, width/.initial=\tikzumlStateJoinDefaultWidth,% + name/.initial=statejoin-\thetikzumlStateJoinNum,% + color/.initial=\tikzumlDefaultDrawColor, style/.style={},% + no coords/.is if=tikzumlstatejoinWithoutCoords,% + no coords=false,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{above}\OR% + \equal{\keyname}{left}\OR% + \equal{\keyname}{below}\OR% + \equal{\keyname}{right}\OR% + \equal{\keyname}{above left}\OR% + \equal{\keyname}{above right}\OR% + \equal{\keyname}{below left}\OR% + \equal{\keyname}{below right}}{% + \IfSubStr{\keyvalue}{ of }{% + \pgfkeys{/tikzuml/statejoin/.cd, no coords}% + }{}% + }{}% + \ifx\keyvalue\pgfkeysnovalue% + \pgfkeys{/tikzuml/statejoin/.cd, style/.append style/.expand once={\keyname}}% + \else% + \pgfkeys{/tikzuml/statejoin/.cd, style/.append style/.expand twice={\expandafter\keyname\expandafter=\keyvalue}}% + \fi% + %\errmessage{TIKZUML ERROR : in umlstatejoin, invalid option \keyname}% + }% + }% + \pgfkeys{/tikzuml/statejoin/.cd, #1}% + \pgfkeys{/tikzuml/statejoin/.cd, x/.get=\tikzumlStateJoinX, y/.get=\tikzumlStateJoinY, width/.get=\tikzumlStateJoinMinimumWidth,% + name/.get=\tikzumlStateJoinName, color/.get=\tikzumlStateJoinColor% + }% + % + \def\tikzumlStateJoinPos{\tikzumlStateJoinX,\tikzumlStateJoinY}% + % + \iftikzumlstatejoinWithoutCoords% + \node[circle, minimum size=\tikzumlStateJoinMinimumWidth, draw=\tikzumlStateJoinColor, fill=\tikzumlStateJoinColor, /tikzuml/statejoin/style] (\tikzumlStateJoinName) {};% + \else% + \node[circle, minimum size=\tikzumlStateJoinMinimumWidth, draw=\tikzumlStateJoinColor, fill=\tikzumlStateJoinColor, /tikzuml/statejoin/style] (\tikzumlStateJoinName) at (\tikzumlStateJoinPos) {};% + \fi% + % + % add to fit + \ifnum\c@tikzumlStateLevel>0% + \edef\tikzumlStateFitOld{\csname tikzumlStateFit\tikzumlState@parent @@\tikzumlState@fitname\endcsname}% + \expandafter\xdef\csname tikzumlStateFit\tikzumlState@parent @@\tikzumlState@fitname\endcsname{\tikzumlStateFitOld (\tikzumlStateJoinName)}% + \stepcounter{tikzumlStateSubStateNum}% + \fi% + \stepcounter{tikzumlStateJoinNum}% +}% +% +% define a uml decision state +% args : name of the state +% optional : x,y: coordinates of the state +% width: width of the state node +% name: name of the state node +% color: color of the join symbol +% style: to manage every default TikZ option +% no coords: to tell that the state position is defined relatively +% to another node (automatically used with TikZ options above, below, left, right, below left, ...) +\newcommand{\umlstatedecision}[1][]{% + \pgfkeys{/tikzuml/statedecision/.cd, x/.initial=\tikzumlDefaultX, y/.initial=\tikzumlDefaultX, width/.initial=\tikzumlStateDecisionDefaultWidth,% + name/.initial=statedecision-\thetikzumlStateDecisionNum,% + color/.initial=\tikzumlDefaultDrawColor, style/.style={},% + no coords/.is if=tikzumlstatedecisionWithoutCoords,% + no coords=false,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{above}\OR% + \equal{\keyname}{left}\OR% + \equal{\keyname}{below}\OR% + \equal{\keyname}{right}\OR% + \equal{\keyname}{above left}\OR% + \equal{\keyname}{above right}\OR% + \equal{\keyname}{below left}\OR% + \equal{\keyname}{below right}}{% + \IfSubStr{\keyvalue}{ of }{% + \pgfkeys{/tikzuml/statedecision/.cd, no coords}% + }{}% + }{}% + \ifx\keyvalue\pgfkeysnovalue% + \pgfkeys{/tikzuml/statedecision/.cd, style/.append style/.expand once={\keyname}}% + \else% + \pgfkeys{/tikzuml/statedecision/.cd, style/.append style/.expand twice={\expandafter\keyname\expandafter=\keyvalue}}% + \fi% + %\errmessage{TIKZUML ERROR : in umlstatedecision, invalid option \keyname}% + }% + }% + \pgfkeys{/tikzuml/statedecision/.cd, #1}% + \pgfkeys{/tikzuml/statedecision/.cd, x/.get=\tikzumlStateDecisionX, y/.get=\tikzumlStateDecisionY, width/.get=\tikzumlStateDecisionMinimumWidth,% + name/.get=\tikzumlStateDecisionName, color/.get=\tikzumlStateDecisionColor% + }% + % + \def\tikzumlStateDecisionPos{\tikzumlStateDecisionX,\tikzumlStateDecisionY}% + % + \iftikzumlstatedecisionWithoutCoords% + \node[rectangle, rotate=45, minimum size=\tikzumlStateDecisionMinimumWidth, draw=\tikzumlStateDecisionColor, /tikzuml/statedecision/style] (\tikzumlStateDecisionName) {};% + \else% + \node[rectangle, rotate=45, minimum size=\tikzumlStateDecisionMinimumWidth, draw=\tikzumlStateDecisionColor, /tikzuml/statedecision/style] (\tikzumlStateDecisionName) at (\tikzumlStateDecisionPos) {};% + \fi% + % + % add to fit + \ifnum\c@tikzumlStateLevel>0% + \edef\tikzumlStateFitOld{\csname tikzumlStateFit\tikzumlState@parent @@\tikzumlState@fitname\endcsname}% + \expandafter\xdef\csname tikzumlStateFit\tikzumlState@parent @@\tikzumlState@fitname\endcsname{\tikzumlStateFitOld (\tikzumlStateDecisionName)}% + \stepcounter{tikzumlStateSubStateNum}% + \fi% + \stepcounter{tikzumlStateDecisionNum}% +}% +% +% define a uml initial state +% args : name of the state +% optional : x,y: coordinates of the state +% width: width of the state node +% name: name of the state node +% entry, do, exit: entry/do/exit action of the state +% color: color of the join symbol +% style: to manage every default TikZ option +% no coords: to tell that the state position is defined relatively +% to another node (automatically used with TikZ options above, below, left, right, below left, ...) +\newcommand{\umlstateinitial}[1][]{% + \pgfkeys{/tikzuml/stateinitial/.cd, x/.initial=\tikzumlDefaultX, y/.initial=\tikzumlDefaultX, width/.initial=\tikzumlStateInitialDefaultWidth,% + name/.initial=stateinitial-\thetikzumlStateInitialNum,% + color/.initial=\tikzumlDefaultDrawColor, style/.style={},% + no coords/.is if=tikzumlstateinitialWithoutCoords,% + no coords=false,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{above}\OR% + \equal{\keyname}{left}\OR% + \equal{\keyname}{below}\OR% + \equal{\keyname}{right}\OR% + \equal{\keyname}{above left}\OR% + \equal{\keyname}{above right}\OR% + \equal{\keyname}{below left}\OR% + \equal{\keyname}{below right}}{% + \IfSubStr{\keyvalue}{ of }{% + \pgfkeys{/tikzuml/stateinitial/.cd, no coords}% + }{}% + }{}% + \ifx\keyvalue\pgfkeysnovalue% + \pgfkeys{/tikzuml/stateinitial/.cd, style/.append style/.expand once={\keyname}}% + \else% + \pgfkeys{/tikzuml/stateinitial/.cd, style/.append style/.expand twice={\expandafter\keyname\expandafter=\keyvalue}}% + \fi% + %\errmessage{TIKZUML ERROR : in umlstateinitial, invalid option \keyname}% + }% + }% + \pgfkeys{/tikzuml/stateinitial/.cd, #1}% + \pgfkeys{/tikzuml/stateinitial/.cd, x/.get=\tikzumlStateInitialX, y/.get=\tikzumlStateInitialY, width/.get=\tikzumlStateInitialMinimumWidth,% + name/.get=\tikzumlStateInitialName, color/.get=\tikzumlStateInitialColor% + }% + % + \def\tikzumlStateInitialPos{\tikzumlStateInitialX,\tikzumlStateInitialY}% + % + \iftikzumlstateinitialWithoutCoords% + \node[circle, minimum size=\tikzumlStateInitialMinimumWidth, fill=\tikzumlStateInitialColor, /tikzuml/stateinitial/style] (\tikzumlStateInitialName) {};% + \else% + \node[circle, minimum size=\tikzumlStateInitialMinimumWidth, fill=\tikzumlStateInitialColor, /tikzuml/stateinitial/style] (\tikzumlStateInitialName) at (\tikzumlStateInitialPos) {};% + \fi% + % + % add to fit + \ifnum\c@tikzumlStateLevel>0% + \edef\tikzumlStateFitOld{\csname tikzumlStateFit\tikzumlState@parent @@\tikzumlState@fitname\endcsname}% + \expandafter\xdef\csname tikzumlStateFit\tikzumlState@parent @@\tikzumlState@fitname\endcsname{\tikzumlStateFitOld (\tikzumlStateInitialName)}% + \stepcounter{tikzumlStateSubStateNum}% + \fi% + \stepcounter{tikzumlStateInitialNum}% +}% +% +% define a uml final state +% args : name of the state +% optional : x,y: coordinates of the state +% width: width of the state node +% name: name of the state node +% color: color of the join symbol +% style: to manage every default TikZ option +% no coords: to tell that the state position is defined relatively +% to another node (automatically used with TikZ options above, below, left, right, below left, ...) +\newcommand{\umlstatefinal}[1][]{% + \pgfkeys{/tikzuml/statefinal/.cd, x/.initial=\tikzumlDefaultX, y/.initial=\tikzumlDefaultX, width/.initial=\tikzumlStateFinalDefaultWidth,% + name/.initial=statefinal-\thetikzumlStateFinalNum,% + color/.initial=\tikzumlDefaultDrawColor, style/.style={},% + no coords/.is if=tikzumlstatefinalWithoutCoords,% + no coords=false,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{above}\OR% + \equal{\keyname}{left}\OR% + \equal{\keyname}{below}\OR% + \equal{\keyname}{right}\OR% + \equal{\keyname}{above left}\OR% + \equal{\keyname}{above right}\OR% + \equal{\keyname}{below left}\OR% + \equal{\keyname}{below right}}{% + \IfSubStr{\keyvalue}{ of }{% + \pgfkeys{/tikzuml/statefinal/.cd, no coords}% + }{}% + }{}% + \ifx\keyvalue\pgfkeysnovalue% + \pgfkeys{/tikzuml/statefinal/.cd, style/.append style/.expand once={\keyname}}% + \else% + \pgfkeys{/tikzuml/statefinal/.cd, style/.append style/.expand twice={\expandafter\keyname\expandafter=\keyvalue}}% + \fi% + %\errmessage{TIKZUML ERROR : in umlstatefinal, invalid option \keyname}% + }% + }% + \pgfkeys{/tikzuml/statefinal/.cd, #1}% + \pgfkeys{/tikzuml/statefinal/.cd, x/.get=\tikzumlStateFinalX, y/.get=\tikzumlStateFinalY, width/.get=\tikzumlStateFinalMinimumWidth,% + name/.get=\tikzumlStateFinalName, color/.get=\tikzumlStateFinalColor% + }% + % + \def\tikzumlStateFinalPos{\tikzumlStateFinalX,\tikzumlStateFinalY}% + % + \iftikzumlstatefinalWithoutCoords% + \node[circle, minimum size=\tikzumlStateFinalMinimumWidth, draw=\tikzumlStateFinalColor, fill=\tikzumlStateFinalColor, double, double distance=0.1cm, /tikzuml/statefinal/style] (\tikzumlStateFinalName) {};% + \else% + \node[circle, minimum size=\tikzumlStateFinalMinimumWidth, draw=\tikzumlStateFinalColor, fill=\tikzumlStateFinalColor, double, double distance=0.1cm, /tikzuml/statefinal/style] (\tikzumlStateFinalName) at (\tikzumlStateFinalPos) {};% + \fi% + % + % add to fit + \ifnum\c@tikzumlStateLevel>0% + \edef\tikzumlStateFitOld{\csname tikzumlStateFit\tikzumlState@parent @@\tikzumlState@fitname\endcsname}% + \expandafter\xdef\csname tikzumlStateFit\tikzumlState@parent @@\tikzumlState@fitname\endcsname{\tikzumlStateFitOld (\tikzumlStateFinalName)}% + \stepcounter{tikzumlStateSubStateNum}% + \fi% + \stepcounter{tikzumlStateFinalNum}% +}% +% +% define a uml enter state +% args : name of the state +% optional : x,y: coordinates of the state +% width: width of the state node +% name: name of the state node +% color: color of the join symbol +% style: to manage every default TikZ option +% no coords: to tell that the state position is defined relatively +% to another node (automatically used with TikZ options above, below, left, right, below left, ...) +\newcommand{\umlstateenter}[1][]{% + \pgfkeys{/tikzuml/stateenter/.cd, x/.initial=\tikzumlDefaultX, y/.initial=\tikzumlDefaultX, width/.initial=\tikzumlStateEnterDefaultWidth,% + name/.initial=stateenter-\thetikzumlStateEnterNum,% + color/.initial=\tikzumlDefaultDrawColor, style/.style={},% + no coords/.is if=tikzumlstateenterWithoutCoords,% + no coords=false,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{above}\OR% + \equal{\keyname}{left}\OR% + \equal{\keyname}{below}\OR% + \equal{\keyname}{right}\OR% + \equal{\keyname}{above left}\OR% + \equal{\keyname}{above right}\OR% + \equal{\keyname}{below left}\OR% + \equal{\keyname}{below right}}{% + \IfSubStr{\keyvalue}{ of }{% + \pgfkeys{/tikzuml/stateenter/.cd, no coords}% + }{}% + }{}% + \ifx\keyvalue\pgfkeysnovalue% + \pgfkeys{/tikzuml/stateenter/.cd, style/.append style/.expand once={\keyname}}% + \else% + \pgfkeys{/tikzuml/stateenter/.cd, style/.append style/.expand twice={\expandafter\keyname\expandafter=\keyvalue}}% + \fi% + %\errmessage{TIKZUML ERROR : in umlstateenter, invalid option \keyname}% + }% + }% + \pgfkeys{/tikzuml/stateenter/.cd, #1}% + \pgfkeys{/tikzuml/stateenter/.cd, x/.get=\tikzumlStateEnterX, y/.get=\tikzumlStateEnterY, width/.get=\tikzumlStateEnterMinimumWidth,% + name/.get=\tikzumlStateEnterName, color/.get=\tikzumlStateEnterColor% + }% + % + \def\tikzumlStateEnterPos{\tikzumlStateEnterX,\tikzumlStateEnterY}% + % + \iftikzumlstateenterWithoutCoords% + \node[circle, minimum size=\tikzumlStateEnterMinimumWidth, draw=\tikzumlStateEnterColor, /tikzuml/stateenter/style] (\tikzumlStateEnterName) {};% + \else% + \node[circle, minimum size=\tikzumlStateEnterMinimumWidth, draw=\tikzumlStateEnterColor, /tikzuml/stateenter/style] (\tikzumlStateEnterName) at (\tikzumlStateEnterPos) {};% + \fi% + % + % add to fit + \ifnum\c@tikzumlStateLevel>0% + \edef\tikzumlStateFitOld{\csname tikzumlStateFit\tikzumlState@parent @@\tikzumlState@fitname\endcsname}% + \expandafter\xdef\csname tikzumlStateFit\tikzumlState@parent @@\tikzumlState@fitname\endcsname{\tikzumlStateFitOld (\tikzumlStateEnterName)}% + \stepcounter{tikzumlStateSubStateNum}% + \fi% + \stepcounter{tikzumlStateEnterNum}% +}% +% +% define a uml exit state +% args : name of the state +% optional : x,y: coordinates of the state +% width: width of the state node +% name: name of the state node +% color: color of the join symbol +% style: to manage every default TikZ option +% no coords: to tell that the state position is defined relatively +% to another node (automatically used with TikZ options above, below, left, right, below left, ...) +\newcommand{\umlstateexit}[1][]{% + \pgfkeys{/tikzuml/stateexit/.cd, x/.initial=\tikzumlDefaultX, y/.initial=\tikzumlDefaultX, width/.initial=\tikzumlStateExitDefaultWidth,% + name/.initial=stateexit-\thetikzumlStateExitNum,% + color/.initial=\tikzumlDefaultDrawColor, style/.style={},% + no coords/.is if=tikzumlstateexitWithoutCoords,% + no coords=false,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{above}\OR% + \equal{\keyname}{left}\OR% + \equal{\keyname}{below}\OR% + \equal{\keyname}{right}\OR% + \equal{\keyname}{above left}\OR% + \equal{\keyname}{above right}\OR% + \equal{\keyname}{below left}\OR% + \equal{\keyname}{below right}}{% + \IfSubStr{\keyvalue}{ of }{% + \pgfkeys{/tikzuml/stateexit/.cd, no coords}% + }{}% + }{}% + \ifx\keyvalue\pgfkeysnovalue% + \pgfkeys{/tikzuml/stateexit/.cd, style/.append style/.expand once={\keyname}}% + \else% + \pgfkeys{/tikzuml/stateexit/.cd, style/.append style/.expand twice={\expandafter\keyname\expandafter=\keyvalue}}% + \fi% + %\errmessage{TIKZUML ERROR : in umlstateexit, invalid option \keyname}% + }% + }% + \pgfkeys{/tikzuml/stateexit/.cd, #1}% + \pgfkeys{/tikzuml/stateexit/.cd, x/.get=\tikzumlStateExitX, y/.get=\tikzumlStateExitY, width/.get=\tikzumlStateExitMinimumWidth,% + name/.get=\tikzumlStateExitName, color/.get=\tikzumlStateExitColor% + }% + % + \def\tikzumlStateExitPos{\tikzumlStateExitX,\tikzumlStateExitY}% + % + \iftikzumlstateexitWithoutCoords% + \node[circle, minimum size=\tikzumlStateExitMinimumWidth, draw=\tikzumlStateExitColor, /tikzuml/stateexit/style] (\tikzumlStateExitName) {};% + \else% + \node[circle, minimum size=\tikzumlStateExitMinimumWidth, draw=\tikzumlStateExitColor, /tikzuml/stateexit/style] (\tikzumlStateExitName) at (\tikzumlStateExitPos) {};% + \fi% + \draw[draw=\tikzumlStateExitColor] (\tikzumlStateExitName.north east) -- (\tikzumlStateExitName.south west) (\tikzumlStateExitName.north west) -- (\tikzumlStateExitName.south east); + % + % add to fit + \ifnum\c@tikzumlStateLevel>0% + \edef\tikzumlStateFitOld{\csname tikzumlStateFit\tikzumlState@parent @@\tikzumlState@fitname\endcsname}% + \expandafter\xdef\csname tikzumlStateFit\tikzumlState@parent @@\tikzumlState@fitname\endcsname{\tikzumlStateFitOld (\tikzumlStateExitName)}% + \stepcounter{tikzumlStateSubStateNum}% + \fi% + \stepcounter{tikzumlStateExitNum}% +}% +% +% define a uml end state +% args : name of the state +% optional : x,y: coordinates of the state +% width: width of the state node +% name: name of the state node +% color: color of the join symbol +% style: to manage every default TikZ option +% no coords: to tell that the state position is defined relatively +% to another node (automatically used with TikZ options above, below, left, right, below left, ...) +\newcommand{\umlstateend}[1][]{% + \pgfkeys{/tikzuml/stateend/.cd, x/.initial=\tikzumlDefaultX, y/.initial=\tikzumlDefaultX, width/.initial=\tikzumlStateEndDefaultWidth,% + name/.initial=stateend-\thetikzumlStateEndNum,% + color/.initial=\tikzumlDefaultDrawColor, style/.style={},% + no coords/.is if=tikzumlstateendWithoutCoords,% + no coords=false,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{above}\OR% + \equal{\keyname}{left}\OR% + \equal{\keyname}{below}\OR% + \equal{\keyname}{right}\OR% + \equal{\keyname}{above left}\OR% + \equal{\keyname}{above right}\OR% + \equal{\keyname}{below left}\OR% + \equal{\keyname}{below right}}{% + \IfSubStr{\keyvalue}{ of }{% + \pgfkeys{/tikzuml/stateend/.cd, no coords}% + }{}% + }{}% + \ifx\keyvalue\pgfkeysnovalue% + \pgfkeys{/tikzuml/stateend/.cd, style/.append style/.expand once={\keyname}}% + \else% + \pgfkeys{/tikzuml/stateend/.cd, style/.append style/.expand twice={\expandafter\keyname\expandafter=\keyvalue}}% + \fi% + %\errmessage{TIKZUML ERROR : in umlstateend, invalid option \keyname}% + }% + }% + \pgfkeys{/tikzuml/stateend/.cd, #1}% + \pgfkeys{/tikzuml/stateend/.cd, x/.get=\tikzumlStateEndX, y/.get=\tikzumlStateEndY, width/.get=\tikzumlStateEndMinimumWidth,% + name/.get=\tikzumlStateEndName, color/.get=\tikzumlStateEndColor% + }% + % + \def\tikzumlStateEndPos{\tikzumlStateEndX,\tikzumlStateEndY}% + % + \iftikzumlstateendWithoutCoords% + \node[circle, minimum size=\tikzumlStateEndMinimumWidth, /tikzuml/stateend/style] (\tikzumlStateEndName) {};% + \else% + \node[circle, minimum size=\tikzumlStateEndMinimumWidth, /tikzuml/stateend/style] (\tikzumlStateEndName) at (\tikzumlStateEndPos) {};% + \fi% + \draw[draw=\tikzumlStateEndColor] (\tikzumlStateEndName.north east) -- (\tikzumlStateEndName.south west) (\tikzumlStateEndName.north west) -- (\tikzumlStateEndName.south east); + % + % add to fit + \ifnum\c@tikzumlStateLevel>0% + \edef\tikzumlStateFitOld{\csname tikzumlStateFit\tikzumlState@parent @@\tikzumlState@fitname\endcsname}% + \expandafter\xdef\csname tikzumlStateFit\tikzumlState@parent @@\tikzumlState@fitname\endcsname{\tikzumlStateFitOld (\tikzumlStateEndName)}% + \stepcounter{tikzumlStateSubStateNum}% + \fi% + \stepcounter{tikzumlStateEndNum}% +}% +% +\newcommand{\picturedhistory}[1]{% + \begin{tikzpicture}[#1]% + \draw[thick] (-0.1cm,-0.15cm) -- (-0.1cm,0.15cm) + (-0.1cm,0) -- (0.1cm,0) + (0.1cm,-0.15cm) -- (0.1cm,0.15cm);% + \end{tikzpicture}% +}% +% +% define a uml history state +% args : name of the state +% optional : x,y: coordinates of the state +% width: width of the state node +% name: name of the state node +% color: color of the join symbol +% style: to manage every default TikZ option +% no coords: to tell that the state position is defined relatively +% to another node (automatically used with TikZ options above, below, left, right, below left, ...) +\newcommand{\umlstatehistory}[1][]{% + \pgfkeys{/tikzuml/statehistory/.cd, x/.initial=\tikzumlDefaultX, y/.initial=\tikzumlDefaultX, width/.initial=\tikzumlStateHistoryDefaultWidth,% + name/.initial=statehistory-\thetikzumlStateHistoryNum,% + color/.initial=\tikzumlDefaultDrawColor, style/.style={},% + no coords/.is if=tikzumlstatehistoryWithoutCoords,% + no coords=false,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{above}\OR% + \equal{\keyname}{left}\OR% + \equal{\keyname}{below}\OR% + \equal{\keyname}{right}\OR% + \equal{\keyname}{above left}\OR% + \equal{\keyname}{above right}\OR% + \equal{\keyname}{below left}\OR% + \equal{\keyname}{below right}}{% + \IfSubStr{\keyvalue}{ of }{% + \pgfkeys{/tikzuml/statehistory/.cd, no coords}% + }{}% + }{}% + \ifx\keyvalue\pgfkeysnovalue% + \pgfkeys{/tikzuml/statehistory/.cd, style/.append style/.expand once={\keyname}}% + \else% + \pgfkeys{/tikzuml/statehistory/.cd, style/.append style/.expand twice={\expandafter\keyname\expandafter=\keyvalue}}% + \fi% + %\errmessage{TIKZUML ERROR : in umlstatehistory, invalid option \keyname}% + }% + }% + \pgfkeys{/tikzuml/statehistory/.cd, #1}% + \pgfkeys{/tikzuml/statehistory/.cd, x/.get=\tikzumlStateHistoryX, y/.get=\tikzumlStateHistoryY, width/.get=\tikzumlStateHistoryMinimumWidth,% + name/.get=\tikzumlStateHistoryName, color/.get=\tikzumlStateHistoryColor% + }% + % + \def\tikzumlStateHistoryPos{\tikzumlStateHistoryX,\tikzumlStateHistoryY}% + % + \iftikzumlstatehistoryWithoutCoords% + \node[circle, minimum size=\tikzumlStateHistoryMinimumWidth, draw=\tikzumlStateHistoryColor, /tikzuml/statehistory/style] (\tikzumlStateHistoryName) {\picturedhistory{draw=\tikzumlStateHistoryColor}};% + \else% + \node[circle, minimum size=\tikzumlStateHistoryMinimumWidth, draw=\tikzumlStateHistoryColor, /tikzuml/statehistory/style] (\tikzumlStateHistoryName) at (\tikzumlStateHistoryPos) {\picturedhistory{draw=\tikzumlStateHistoryColor}};% + \fi% + % + % add to fit + \ifnum\c@tikzumlStateLevel>0% + \edef\tikzumlStateFitOld{\csname tikzumlStateFit\tikzumlState@parent @@\tikzumlState@fitname\endcsname}% + \expandafter\xdef\csname tikzumlStateFit\tikzumlState@parent @@\tikzumlState@fitname\endcsname{\tikzumlStateFitOld (\tikzumlStateHistoryName)}% + \stepcounter{tikzumlStateSubStateNum}% + \fi% + \stepcounter{tikzumlStateHistoryNum}% +}% +% +\newcommand{\pictureddeephistory}[1]{% + \begin{tikzpicture}[#1]% + \draw[thick] (-0.1cm,-0.15cm) -- (-0.1cm,0.15cm) + (-0.1cm,0) -- (0.1cm,0) + (0.1cm,-0.15cm) -- (0.1cm,0.15cm) + (0.23cm,0.19cm) -- (0.23cm,0.11cm) + (0.20cm,0.17cm) -- (0.26cm,0.13cm) + (0.20cm,0.13cm) -- (0.26cm,0.17cm);% + \end{tikzpicture}% +}% +% +% define a uml deep-history state +% args : name of the state +% optional : x,y: coordinates of the state +% width: width of the state node +% name: name of the state node +% color: color of the join symbol +% style: to manage every default TikZ option +% no coords: to tell that the state position is defined relatively +% to another node (automatically used with TikZ options above, below, left, right, below left, ...) +\newcommand{\umlstatedeephistory}[1][]{% + \pgfkeys{/tikzuml/statedeephistory/.cd, x/.initial=\tikzumlDefaultX, y/.initial=\tikzumlDefaultX, width/.initial=\tikzumlStateDeepHistoryDefaultWidth,% + name/.initial=statedeephistory-\thetikzumlStateDeepHistoryNum,% + color/.initial=\tikzumlDefaultDrawColor, style/.style={},% + no coords/.is if=tikzumlstatedeephistoryWithoutCoords,% + no coords=false,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{above}\OR% + \equal{\keyname}{left}\OR% + \equal{\keyname}{below}\OR% + \equal{\keyname}{right}\OR% + \equal{\keyname}{above left}\OR% + \equal{\keyname}{above right}\OR% + \equal{\keyname}{below left}\OR% + \equal{\keyname}{below right}}{% + \IfSubStr{\keyvalue}{ of }{% + \pgfkeys{/tikzuml/statedeephistory/.cd, no coords}% + }{}% + }{}% + \ifx\keyvalue\pgfkeysnovalue% + \pgfkeys{/tikzuml/statedeephistory/.cd, style/.append style/.expand once={\keyname}}% + \else% + \pgfkeys{/tikzuml/statedeephistory/.cd, style/.append style/.expand twice={\expandafter\keyname\expandafter=\keyvalue}}% + \fi% + %\errmessage{TIKZUML ERROR : in umlstatedeephistory, invalid option \keyname}% + }% + }% + \pgfkeys{/tikzuml/statedeephistory/.cd, #1}% + \pgfkeys{/tikzuml/statedeephistory/.cd, x/.get=\tikzumlStateDeepHistoryX, y/.get=\tikzumlStateDeepHistoryY, width/.get=\tikzumlStateDeepHistoryMinimumWidth,% + name/.get=\tikzumlStateDeepHistoryName, color/.get=\tikzumlStateDeepHistoryColor% + }% + % + \def\tikzumlStateDeepHistoryPos{\tikzumlStateDeepHistoryX,\tikzumlStateDeepHistoryY}% + % + \iftikzumlstatedeephistoryWithoutCoords% + \node[circle, minimum size=\tikzumlStateDeepHistoryMinimumWidth, draw=\tikzumlStateDeepHistoryColor, /tikzuml/statedeephistory/style] (\tikzumlStateDeepHistoryName) {\pictureddeephistory{draw=\tikzumlStateDeepHistoryColor}};% + \else% + \node[circle, minimum size=\tikzumlStateDeepHistoryMinimumWidth, draw=\tikzumlStateDeepHistoryColor, /tikzuml/statedeephistory/style] (\tikzumlStateDeepHistoryName) at (\tikzumlStateDeepHistoryPos) {\pictureddeephistory{draw=\tikzumlStateDeepHistoryColor}};% + \fi% + % + % add to fit + \ifnum\c@tikzumlStateLevel>0% + \edef\tikzumlStateFitOld{\csname tikzumlStateFit\tikzumlState@parent @@\tikzumlState@fitname\endcsname}% + \expandafter\xdef\csname tikzumlStateFit\tikzumlState@parent @@\tikzumlState@fitname\endcsname{\tikzumlStateFitOld (\tikzumlStateDeepHistoryName)}% + \stepcounter{tikzumlStateSubStateNum}% + \fi% + \stepcounter{tikzumlStateDeepHistoryNum}% +}% +% +% define a uml state +% args : name of the state +% content of the state +% optional : x,y: coordinates of the state +% width: width of the state node +% name: name of the state node +% entry, do, exit: entry/do/exit action of the state +% draw, fill, text: colors +% style: to manage every default TikZ option +% no coords: to tell that the state position is defined relatively +% to another node (automatically used with TikZ options above, below, left, right, below left, ...) +\newenvironment{umlstate}[2][]{% + \ifnum\thetikzumlStateLevel>0% + \let\tikzumlState@nameold\tikzumlState@fitname% + \let\tikzumlState@parentold\tikzumlState@parent% + \edef\tikzumlState@parent{\tikzumlState@parentold @@\tikzumlState@nameold}% + \else% + \def\tikzumlState@parent{}% + \fi% + % + \stepcounter{tikzumlStateLevel}% + % + \pgfkeys{/tikzuml/state/.cd, x/.initial=\tikzumlDefaultX, y/.initial=\tikzumlDefaultX, width/.initial=\tikzumlStateDefaultWidth,% + name/.initial={},% + entry/.initial={}, do/.initial={}, exit/.initial={},% + draw/.initial=\tikzumlDefaultDrawColor, fill/.initial=\tikzumlStateDefaultFillColor,% + text/.initial=\tikzumlDefaultTextColor, style/.style={},% + no coords/.is if=tikzumlstateWithoutCoords,% + no coords=false,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{above}\OR% + \equal{\keyname}{left}\OR% + \equal{\keyname}{below}\OR% + \equal{\keyname}{right}\OR% + \equal{\keyname}{above left}\OR% + \equal{\keyname}{above right}\OR% + \equal{\keyname}{below left}\OR% + \equal{\keyname}{below right}}{% + \IfSubStr{\keyvalue}{ of }{% + \pgfkeys{/tikzuml/state/.cd, no coords}% + }{}% + }{}% + \ifx\keyvalue\pgfkeysnovalue% + \pgfkeys{/tikzuml/state/.cd, style/.append style/.expand once={\keyname}}% + \else% + \pgfkeys{/tikzuml/state/.cd, style/.append style/.expand twice={\expandafter\keyname\expandafter=\keyvalue}}% + \fi% + % \errmessage{TIKZUML ERROR : in umlstate, invalid option \keyname}% + }% + }% + \pgfkeys{/tikzuml/state/.cd, #1}% + \pgfkeys{/tikzuml/state/.cd, x/.get=\tikzumlStateXShift, y/.get=\tikzumlStateYShift, width/.get=\tikzumlStateMinimumWidth, name/.get=\tikzumlStateName,% + entry/.get=\tikzumlStateEntry, do/.get=\tikzumlStateDo, exit/.get=\tikzumlStateExit,% + draw/.get=\tikzumlStateDrawColor, fill/.get=\tikzumlStateFillColor,% + text/.get=\tikzumlStateTextColor% + }% + % + \ifthenelse{\equal{\tikzumlStateName}{}}{% + \edef\tikzumlState@name{#2}% + }{% + \edef\tikzumlState@name{\tikzumlStateName}% + }% + % + \begingroup% + \def\_{@}\edef\x{\endgroup% + \def\noexpand\tikzumlState@fitname{\tikzumlState@name}}\x% + % + \let\tikzumlState@nodeNameold\tikzumlState@nodeName% + \def\tikzumlState@caption{#2}% + % + \begingroup% + \def\_{_}\edef\x{\endgroup% + \def\noexpand\tikzumlState@nodeName{\tikzumlState@name}}\x% + % + \expandafter\gdef\csname tikzumlStateFit\tikzumlState@parent @@\tikzumlState@fitname\endcsname{}% + % + \setcounter{tikzumlStateSubStateNum}{0}% + \setcounter{tikzumlStateText}{0}% + % + \def\tikzumlStateText{tikzumlEmpty}% + \begin{scope}[xshift=\tikzumlStateXShift cm, yshift=\tikzumlStateYShift cm]% +}{% + % + \def\tikzumlstaterootlabel{\phantom{\tikzumlState@nodeName}}% + % + \def\tikzumlstaterootinnerysep{0.5ex}% + \def\tikzumlstatebodyinnerysep{2ex}% + % + \ifthenelse{\equal{\tikzumlStateEntry}{}}{}{% + \ifnum\c@tikzumlStateText=0% + \def\tikzumlStateText{entry/\tikzumlStateEntry}% + \else% + \let\tikzumlStateTextOld\tikzumlStateText% + \ifthenelse{\equal{\tikzumlStateText}{tikzumlEmpty}}{% + \def\tikzumlStateText{entry/\tikzumlStateEntry}% + }{% + \expandafter\def\expandafter\tikzumlStateText\expandafter{\tikzumlStateTextOld \\ entry/\tikzumlStateEntry}% + }% + \fi% + \setcounter{tikzumlStateText}{1}% + \ifnum\c@tikzumlStateSubStateNum=0% + \def\tikzumlstatebodyinnerysep{0}% + \def\tikzumlstaterootinnerysep{0}% + \fi% + }% + \ifthenelse{\equal{\tikzumlStateDo}{}}{}{% + \ifnum\c@tikzumlStateText=0% + \def\tikzumlStateText{do/\tikzumlStateDo}% + \else% + \let\tikzumlStateTextOld\tikzumlStateText% + \ifthenelse{\equal{\tikzumlStateText}{tikzumlEmpty}}{% + \def\tikzumlStateText{do/\tikzumlStateDo}% + }{% + \expandafter\def\expandafter\tikzumlStateText\expandafter{\tikzumlStateTextOld \\ do/\tikzumlStateDo}% + }% + \fi% + \setcounter{tikzumlStateText}{1}% + \ifnum\c@tikzumlStateSubStateNum=0% + \def\tikzumlstatebodyinnerysep{0}% + \def\tikzumlstaterootinnerysep{0}% + \fi% + }% + \ifthenelse{\equal{\tikzumlStateExit}{}}{}{% + \ifnum\c@tikzumlStateText=0% + \def\tikzumlStateText{exit/\tikzumlStateExit}% + \else% + \let\tikzumlStateTextOld\tikzumlStateText% + \ifthenelse{\equal{\tikzumlStateText}{tikzumlEmpty}}{% + \def\tikzumlStateText{exit/\tikzumlStateExit}% + }{% + \expandafter\def\expandafter\tikzumlStateText\expandafter{\tikzumlStateTextOld \\ exit/\tikzumlStateExit}% + }% + \fi% + \setcounter{tikzumlStateText}{1}% + \ifnum\c@tikzumlStateSubStateNum=0% + \def\tikzumlstatebodyinnerysep{0}% + \def\tikzumlstaterootinnerysep{0}% + \fi% + }% + % + \addtocounter{tikzumlStateLevel}{-1}% + \begin{pgfonlayer}{state\thetikzumlStateLevel}% + % + % if contains nothing, one define a fictive node to enable the fit option + \ifnum\c@tikzumlStateSubStateNum=0% + \iftikzumlstateWithoutCoords% + \node[inner ysep=\tikzumlstaterootinnerysep, minimum width=\tikzumlStateMinimumWidth, /tikzuml/state/style] (\tikzumlState@nodeName-root) {\tikzumlstaterootlabel};% + \else% + \node[inner ysep=\tikzumlstaterootinnerysep, minimum width=\tikzumlStateMinimumWidth, /tikzuml/state/style] (\tikzumlState@nodeName-root) at (0,0) {\tikzumlstaterootlabel};% + \fi% + \expandafter\xdef\csname tikzumlStateFit\tikzumlState@parent @@\tikzumlState@fitname\endcsname{(\tikzumlState@nodeName-root)}% + \fi% + % + \ifnum\c@tikzumlStateLevel>0% + \def\tikzumlStateFitTmp{\csname tikzumlStateFit\tikzumlState@parent\endcsname}% + \expandafter\xdef\csname tikzumlStateFit\tikzumlState@parent\endcsname{\tikzumlStateFitTmp (\tikzumlState@nodeName-body) (\tikzumlState@nodeName-caption)}% + \stepcounter{tikzumlStateSubStateNum}% + \fi% + % + \node[inner xsep=2ex, inner ysep=\tikzumlstatebodyinnerysep, fit = \csname tikzumlStateFit\tikzumlState@parent @@\tikzumlState@fitname\endcsname, /tikzuml/state/style ] (\tikzumlState@nodeName-body) {};% + \def\tikzumlState@orig{body}% + \ifnum\c@tikzumlStateText=1% + \node[above=0] (\tikzumlState@nodeName-texttmp) at (\tikzumlState@nodeName-\tikzumlState@orig.north) {\begin{tabular}{l}\tikzumlStateText \end{tabular}};% + \def\tikzumlState@orig{texttmp}% + \fi% + \node[above] (\tikzumlState@nodeName-captiontmp) at (\tikzumlState@nodeName-\tikzumlState@orig.north) {\tikzumlState@caption};% + \ifnum\c@tikzumlStateText=1% + \node[rounded corners, draw=\tikzumlStateDrawColor, fill=\tikzumlStateFillColor, name=\tikzumlState@nodeName, fit=(\tikzumlState@nodeName-body) (\tikzumlState@nodeName-texttmp) (\tikzumlState@nodeName-captiontmp)] {};% + \else% + \node[rounded corners, draw=\tikzumlStateDrawColor, fill=\tikzumlStateFillColor, name=\tikzumlState@nodeName, fit=(\tikzumlState@nodeName-body) (\tikzumlState@nodeName-captiontmp)] {};% + \fi% + \ifnum\c@tikzumlStateText=1% + \node (\tikzumlState@nodeName-text) at (\tikzumlState@nodeName-texttmp) {\begin{tabular}{l}\tikzumlStateText \end{tabular}};% + \fi% + \node (\tikzumlState@nodeName-caption) at (\tikzumlState@nodeName-captiontmp) {\tikzumlState@caption};% + \draw (\tikzumlState@nodeName-caption.south -| \tikzumlState@nodeName.north west) -- (\tikzumlState@nodeName-caption.south -| \tikzumlState@nodeName.north east);% + \end{pgfonlayer}% + \end{scope}% +}% +% +% shortcut for empty state +\newcommand{\umlbasicstate}[2][]{\begin{umlstate}[#1]{#2}\end{umlstate}}% +% +% command to add text in a state, to be used inside umlstate environment +\newcommand{\umlstatetext}[1]{% + \def\tikzumlStateText{#1}% + \setcounter{tikzumlStateText}{1}% +}% +% +% shortcuts for state transitions macros +\newcommand{\umltrans}[3][]{% + \ifthenelse{\equal{#2}{#3}}{% + \umlrelation[style={tikzuml transition style}, recursive mode=transition, #1]{#2}{#3}% + }{% + \umlrelation[style={tikzuml transition style}, #1]{#2}{#3}% + }% +}% +\newcommand{\umlHVtrans}[3][]{\umlHVrelation[style={tikzuml transition style}, #1]{#2}{#3}}% +\newcommand{\umlVHtrans}[3][]{\umlVHrelation[style={tikzuml transition style}, #1]{#2}{#3}}% +\newcommand{\umlVHVtrans}[3][]{\umlVHVrelation[style={tikzuml transition style}, #1]{#2}{#3}}% +\newcommand{\umlHVHtrans}[3][]{\umlHVHrelation[style={tikzuml transition style}, #1]{#2}{#3}}% +\newcommand{\umlCNtrans}[4][]{\umlCNrelation[style={tikzuml transition style}, #1]{#2}{#3}{#4}}% +% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% sequence diagrams % +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +\tikzstyle{tikzuml synchron-msg style}=[color=\tikzumlDefaultDrawColor, -triangle 45]% +\tikzstyle{tikzuml asynchron-msg style}=[color=\tikzumlDefaultDrawColor, -angle 45]% +\tikzstyle{tikzuml return-msg style}=[color=\tikzumlDefaultDrawColor, dashed, -angle 45]% +\tikzstyle{tikzuml call return style}=[color=\tikzumlDefaultDrawColor, dashed, -angle 45]% +\tikzstyle{tikzuml activity style}=[inner xsep=1ex, inner ysep=1ex]% +% +\newcounter{tikzumlObjectNum}% +\newcounter{tikzumlCallLevel}% +\newcounter{tikzumlCallNum}% +\newcounter{tikzumlFragmentLevel}% +\newcounter{tikzumlFragmentLevelNum}% +\newcounter{tikzumlFragmentNum}% +\newcounter{tikzumlFragmentPartNum}% +\newcounter{tikzumlCallStartFragmentNum}% +\newcounter{tikzumlCallEndFragmentNum}% +% +\newif\iftikzumlobjectNoDDots% +\newif\iftikzumlcreatecallNoDDots% +% +% define a sequence diagram +% +\newenvironment{umlseqdiag}{% + \gdef\tikzumlInCreateCall{0}% + \setcounter{tikzumlObjectNum}{0}% + \setcounter{tikzumlCallLevel}{0}% + \setcounter{tikzumlCallNum}{0}% + \setcounter{tikzumlFragmentLevel}{0}% + \setcounter{tikzumlFragmentLevelNum}{0}% + \setcounter{tikzumlFragmentNum}{0}% + \setcounter{tikzumlFragmentPartNum}{0}% + \setcounter{tikzumlCallStartFragmentNum}{0}% + \setcounter{tikzumlCallEndFragmentNum}{0}% + % + \ifx \@umlactor \@empty + \newcommand{\umlactor}[2][]{% + \pgfkeys{/tikzuml/actorobj/.cd, .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{stereo}}{% + \errmessage{TIKZUML ERROR : in umlactor, forbidden option stereo}% + }{}% + }% + }% + % + \pgfkeys{/tikzuml/actorobj/.cd, ##1}% + \umlobject[stereo=actor, ##1]{##2}% + }% + \else% + \renewcommand{\umlactor}[2][]{% + \pgfkeys{/tikzuml/actorobj/.cd, .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{stereo}}{% + \errmessage{TIKZUML ERROR : in umlactor, forbidden option stereo}% + }{}% + }% + }% + % + \pgfkeys{/tikzuml/actorobj/.cd, ##1}% + \umlobject[stereo=actor, ##1]{##2}% + }% + \fi% + \begin{scope}[font=\tikzumlDefaultFont]% +}{% + % draw lifelines of each object + \begin{pgfonlayer}{lifelines}% + \foreach \id in \tikzumlIdList {% + \draw (\csname tikzumlLastChild@\id \endcsname)+(0,-2.5ex) node[inner sep=0, name=end-\id] {};% + \draw[dotted] (\id) -- (end-\id);% + }% + \end{pgfonlayer}% + \end{scope}% +}% +% +% define the database symbol +% optional : global tikzpicture styles +\newcommand{\pictureddatabase}[1]{% + \pgfkeys{/tikzuml/database/.cd, scale/.initial=1, .unknown/.code={}}% + \pgfkeys{/tikzuml/database/.cd,#1}% + \pgfkeys{/tikzuml/database/.cd, scale/.get=\tikzumlDatabaseScale}% + % + \begin{tikzpicture}[#1]% + \node[fill, draw, ellipse, minimum width=\tikzumlDatabaseScale*4ex, minimum height=\tikzumlDatabaseScale*2ex, inner sep=0] (bottom) at (0,-2ex) {};% + \node[fill, draw, ellipse, minimum width=\tikzumlDatabaseScale*4ex, minimum height=\tikzumlDatabaseScale*2ex, inner sep=0] (top) at (0,4ex) {};% + \fill (bottom.west) rectangle (top.east);% + \begin{scope}% + \clip (-3.5ex,-0.5ex) rectangle (3.5ex,2.5ex);% + \node[draw, dashed, ellipse, minimum width=\tikzumlDatabaseScale*4ex, minimum height=\tikzumlDatabaseScale*2ex, inner sep=0] (bottom2) at (0,-2ex) {};% + \end{scope}% + \node[draw, ellipse, minimum width=\tikzumlDatabaseScale*4ex, minimum height=\tikzumlDatabaseScale*2ex, inner sep=0] (top2) at (0,4ex) {};% + \draw (bottom.west) -- (top.west) (bottom.east) -- (top.east);% + \end{tikzpicture}% +}% +% +% define the entity symbol +% optional : global tikzpicture styles +\newcommand{\picturedentity}[1]{% + \pgfkeys{/tikzuml/entity/.cd, scale/.initial=1, .unknown/.code={}}% + \pgfkeys{/tikzuml/entity/.cd,#1}% + \pgfkeys{/tikzuml/entity/.cd, scale/.get=\tikzumlEntityScale}% + % + \begin{tikzpicture}[#1]% + \node[fill, draw, circle, inner sep=0, minimum size=\tikzumlEntityScale*5ex] (center) at (0,0) {};% + \draw (center.south) node[coordinate, name=bottom] {};% + \draw (bottom)+(-2ex,0) node[coordinate, name=bottom-left] {};% + \draw (bottom)+(2ex,0) node[coordinate, name=bottom-right] {};% + \draw (center) -- (bottom);% + \draw (bottom-left) -- (bottom-right);% + \end{tikzpicture}% +}% +% +% define the boundary symbol +% optional : global tikzpicture styles +\newcommand{\picturedboundary}[1]{% + \pgfkeys{/tikzuml/boundary/.cd, scale/.initial=1, .unknown/.code={}}% + \pgfkeys{/tikzuml/boundary/.cd,#1}% + \pgfkeys{/tikzuml/boundary/.cd, scale/.get=\tikzumlBoundaryScale}% + % + \begin{tikzpicture}[#1] + \node[fill, draw, circle, inner sep=0, minimum size=\tikzumlBoundaryScale*5ex] (center) at (0,0) {}; + \draw (center.west)+(-0.8ex,0) node[coordinate, name=left] {}; + \draw (left)+(0,0.2ex) node[coordinate, name=left-top] {}; + \draw (left)+(0,-0.2ex) node[coordinate, name=left-bottom] {}; + \draw (center) -- (left); + \draw (left-top) -- (left-bottom); + \end{tikzpicture} +}% +% +% define the control symbol +% optional : global tikzpicture styles +\newcommand{\picturedcontrol}[1]{% + \pgfkeys{/tikzuml/control/.cd, scale/.initial=1, .unknown/.code={}}% + \pgfkeys{/tikzuml/control/.cd,#1}% + \pgfkeys{/tikzuml/control/.cd, scale/.get=\tikzumlControlScale}% + % + \begin{tikzpicture}[#1, decoration={markings, mark=at position 0.25 with {\arrow{>}}}] + \node[fill, draw, circle, inner sep=0, minimum size=\tikzumlControlScale*5ex, postaction={decorate}] (center) at (0,0) {}; + \end{tikzpicture} +}% +% +% define a uml object for a sequence diagram +% args : name of the object +% optional : x, y: coordinates of the object +% stereo: stereotype of the object (object, actor, database, boundary, control, entity, multiobject) +% class: class of the object +% scale: scale factor of the object symbol +% draw, fill, text; colors +% no ddots: when used, disable printing of double dots +\newcommand{\umlobject}[2][]{ + \stepcounter{tikzumlObjectNum}% + % + \edef\tikzumlobject@ddot{:}% + \pgfkeys{/tikzuml/obj/.cd, x/.initial=tikzumlEmpty, y/.initial=\tikzumlDefaultX, stereo/.initial=\tikzumlObjectDefaultStereo,% + class/.initial={}, scale/.initial=1,% + draw/.initial=\tikzumlDefaultDrawColor, fill/.initial=\tikzumlObjectDefaultFillColor,% + text/.initial=\tikzumlDefaultTextColor,% + no ddots/.is if=tikzumlobjectNoDDots,% + no ddots=false,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \errmessage{TIKZUML ERROR : in umlobject, invalid option \keyname}% + }% + }% + \pgfkeys{/tikzuml/obj/.cd, #1}% + \pgfkeys{/tikzuml/obj/.cd, x/.get=\tikzumlObjectX, y/.get=\tikzumlObjectY,% + stereo/.get=\tikzumlObjectStereo, class/.get=\tikzumlObjectClass,% + scale/.get=\tikzumlObjectScale,% + draw/.get=\tikzumlObjectDrawColor, fill/.get=\tikzumlObjectFillColor,% + text/.get=\tikzumlObjectTextColor% + }% + % + \iftikzumlobjectNoDDots% + \edef\tikzumlobject@ddot{}% + \fi% + % + \ifthenelse{\equal{\tikzumlObjectX}{tikzumlEmpty}}{% + \pgfmathsetmacro{\tikzumlObjectX}{4*(\thetikzumlObjectNum-1)}% + }{}% + % + \def\tikzumlObjectName{#2}% + \expandafter\xdef\csname tikzumlLastChild@\tikzumlObjectName \endcsname{\tikzumlObjectName}% + % + \ifnum\thetikzumlObjectNum=1% + \xdef\tikzumlIdList{\tikzumlObjectName}% + \else% + \let\tikzumlIdListOld\tikzumlIdList% + \expandafter\xdef\expandafter\tikzumlIdList\expandafter{\tikzumlIdListOld,\tikzumlObjectName}% + \fi% + % + \tikzstyle{tikzuml object box style}=[rectangle, text=\tikzumlObjectTextColor, font=\tikzumlDefaultFont]% + % + \ifthenelse{\equal{\tikzumlObjectStereo}{object}}{% + \tikzstyle{tikzuml object box style}+=[draw=\tikzumlObjectDrawColor, fill=\tikzumlObjectFillColor]% + }{% + \ifthenelse{\equal{\tikzumlObjectStereo}{multi}}{% + \tikzstyle{tikzuml object box style}+=[fill=\tikzumlObjectFillColor]% + }{}% + }% + % + \ifnum\tikzumlInCreateCall=1% + \draw (\tikzumlCreateCallObjectSrc -| \tikzumlObjectX,0) node[tikzuml object box style] (\tikzumlObjectName) {\tikzumlObjectName\tikzumlobject@ddot\tikzumlObjectClass};% + \else% + \node[tikzuml object box style] (\tikzumlObjectName) at (\tikzumlObjectX,\tikzumlObjectY) {\tikzumlObjectName\tikzumlobject@ddot\tikzumlObjectClass};% + \fi% + % + \ifthenelse{\equal{\tikzumlObjectStereo}{multi}}{% + \draw (\tikzumlObjectName.north east)+(0.4ex,0.4ex) node[name=\tikzumlObjectName-tr, coordinate] {}; + \draw (\tikzumlObjectName.north west)+(0.4ex,0.4ex) node[name=\tikzumlObjectName-tl, coordinate] {}; + \draw (\tikzumlObjectName.south east)+(0.4ex,0.4ex) node[name=\tikzumlObjectName-br, coordinate] {}; + \draw (\tikzumlObjectName-tr)+(0.4ex,0.4ex) node[name=\tikzumlObjectName-ttr, coordinate] {}; + \draw (\tikzumlObjectName-tl)+(0.4ex,0.4ex) node[name=\tikzumlObjectName-ttl, coordinate] {}; + \draw (\tikzumlObjectName-br)+(0.4ex,0.4ex) node[name=\tikzumlObjectName-tbr, coordinate] {}; + \fill[fill=\tikzumlObjectFillColor] (\tikzumlObjectName-ttl |- \tikzumlObjectName.north) -- (\tikzumlObjectName-ttl) -- (\tikzumlObjectName-ttr) -- (\tikzumlObjectName-tbr) -- (\tikzumlObjectName-tbr -| \tikzumlObjectName.east) -- (\tikzumlObjectName.north east) -- (\tikzumlObjectName-ttl |- \tikzumlObjectName.north); + \draw[draw=\tikzumlObjectDrawColor] (\tikzumlObjectName-ttl |- \tikzumlObjectName.north) -- (\tikzumlObjectName-ttl) -- (\tikzumlObjectName-ttr) -- (\tikzumlObjectName-tbr) -- (\tikzumlObjectName-tbr -| \tikzumlObjectName.east); + \fill[fill=\tikzumlObjectFillColor] (\tikzumlObjectName-tl |- \tikzumlObjectName.north) -- (\tikzumlObjectName-tl) -- (\tikzumlObjectName-tr) -- (\tikzumlObjectName-br) -- (\tikzumlObjectName-br -| \tikzumlObjectName.east) -- (\tikzumlObjectName.north east) -- (\tikzumlObjectName-tl |- \tikzumlObjectName.north); + \draw[draw=\tikzumlObjectDrawColor] (\tikzumlObjectName-tl |- \tikzumlObjectName.north) -- (\tikzumlObjectName-tl) -- (\tikzumlObjectName-tr) -- (\tikzumlObjectName-br) -- (\tikzumlObjectName-br -| \tikzumlObjectName.east); + \draw[draw=\tikzumlObjectDrawColor] (\tikzumlObjectName.north west) rectangle (\tikzumlObjectName.south east); + }{% + \ifthenelse{\equal{\tikzumlObjectStereo}{object}}{}{% + \node[above=1ex, name=\tikzumlObjectName-picture] at (\tikzumlObjectName) {\csname pictured\tikzumlObjectStereo \endcsname{draw=\tikzumlObjectDrawColor, fill=\tikzumlObjectFillColor, scale=\tikzumlObjectScale}}; + }% + }% +}% +% +% shortcuts for objects +\newcommand{\umlbasicobject}[2][]{% + \pgfkeys{/tikzuml/basicobj/.cd, .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{no ddots}}{% + \errmessage{TIKZUML ERROR : in umlbasicobject, forbidden option no ddots}% + }{}% + }% + }% + \pgfkeys{/tikzuml/basicobj/.cd, #1}% + \umlobject[no ddots, #1]{#2}% +}% +% +\newcommand{\umldatabase}[2][]{% + \pgfkeys{/tikzuml/databaseobj/.cd, .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{stereo}}{% + \errmessage{TIKZUML ERROR : in umldatabase, forbidden option stereo}% + }{}% + }% + }% + \pgfkeys{/tikzuml/databaseobj/.cd, #1}% + \umlobject[stereo=database, #1]{#2}% +}% +\newcommand{\umlentity}[2][]{% + \pgfkeys{/tikzuml/entityobj/.cd, .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{stereo}}{% + \errmessage{TIKZUML ERROR : in umlentity, forbidden option stereo}% + }{}% + }% + }% + \pgfkeys{/tikzuml/entityobj/.cd, #1}% + \umlobject[stereo=entity, #1]{#2}% +}% +\newcommand{\umlcontrol}[2][]{% + \pgfkeys{/tikzuml/controlobj/.cd, .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{stereo}}{% + \errmessage{TIKZUML ERROR : in umlcontrol, forbidden option stereo}% + }{}% + }% + }% + \pgfkeys{/tikzuml/controlobj/.cd, #1}% + \umlobject[stereo=control, #1]{#2}% +}% +\newcommand{\umlboundary}[2][]{% + \pgfkeys{/tikzuml/boundaryobj/.cd, .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{stereo}}{% + \errmessage{TIKZUML ERROR : in umlboundary, forbidden option stereo}% + }{}% + }% + }% + \pgfkeys{/tikzuml/boundaryobj/.cd, #1}% + \umlobject[stereo=boundary, #1]{#2}% +}% +\newcommand{\umlmulti}[2][]{% + \pgfkeys{/tikzuml/multiobj/.cd, .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{stereo}}{% + \errmessage{TIKZUML ERROR : in umlmulti, forbidden option stereo}% + }{}% + }% + }% + \pgfkeys{/tikzuml/multiobj/.cd, #1}% + \umlobject[stereo=multi, #1]{#2}% +}% +% +\newcounter{tikzumlSDNodeNum}% +% +% define a hidden node to lengthen lifeline of a object +% args : object node +% optional : dt: distance between the sdnode and the last call defined on the lifeline of the object +% name: name of the sdnode +\newcommand{\umlsdnode}[2][]{% + \pgfkeys{/tikzuml/sdnode/.cd, dt/.initial=0, name/.initial=tikzumlEmpty}% + \pgfkeys{/tikzuml/sdnode/.cd, #1}% + \pgfkeys{/tikzuml/sdnode/.cd, dt/.get=\tikzumlSDNodeDT, name/.get=\tikzumlSDNodeName}% + % + \ifthenelse{\equal{\tikzumlSDNodeName}{tikzumlEmpty}}{% + \expandafter\def\expandafter\tikzumlSDNode@nodeName{sdnode-\thetikzumlSDNodeNum}% + }{% + \expandafter\def\expandafter\tikzumlSDNode@nodeName{\tikzumlSDNodeName}% + }% + % + \stepcounter{tikzumlSDNodeNum}% + % + \begingroup% + \def\_{_}\edef\x{\endgroup% + \def\noexpand\tikzumlSDNode@objnodeName{#2}}\x% + % + \draw (\expandafter\csname tikzumlLastChild@\tikzumlSDNode@objnodeName \endcsname)+(0,-\tikzumlSDNodeDT ex) node[name=\tikzumlSDNode@nodeName,coordinate] {};% + % + % update last node drawn on sender lifeline + \expandafter\xdef\csname tikzumlLastChild@\tikzumlSDNode@objnodeName \endcsname{\tikzumlSDNode@nodeName}% +}% +% +\newlength{\tikzumlCall@xa}% +\newlength{\tikzumlCall@xb}% +% +% define a uml operation call for sequence diagrams +% args : call sender +% call receiver +% optional : dt: time delay from precedent event end +% name: name of the call +% op: operation name and input args +% return: return value +% type: type of the call (synchron, asynchron) +% draw, fill, text: colors +% padding: time padding from call start and to call end +\newenvironment{umlcall}[3][]{% + \stepcounter{tikzumlCallNum}% + \def\tikzumlCallWithReturn{tikzumlFalse}% + \edef\tikzumlCall@lastchildNum{\thetikzumlCallNum}% for testing presence of sub-calls + \gdef\tikzumlCallBottom{0}% + % + \pgfkeys{/tikzuml/call/.cd, dt/.initial=\tikzumlCallDefaultDT, name/.initial={call-\thetikzumlCallNum},% + op/.initial={}, return/.initial={}, type/.initial=\tikzumlCallDefaultType,% + draw/.initial=\tikzumlDefaultDrawColor, fill/.initial=\tikzumlCallDefaultFillColor,% + text/.initial=\tikzumlDefaultTextColor,% + padding/.initial=\tikzumlCallDefaultPadding,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{with return}}{% + \def\tikzumlCallWithReturn{tikzumlTrue}% + }{% + \errmessage{TIKZUML ERROR : in umlcall, invalid option \keyname}% + }% + }% + }% + \pgfkeys{/tikzuml/call/.cd, #1}% + \pgfkeys{/tikzuml/call/.cd, dt/.get=\tikzumlCallDT, name/.get=\tikzumlCallName, op/.get=\tikzumlCallOp,% + return/.get=\tikzumlCallReturn, type/.get=\tikzumlCallType,% + padding/.get=\tikzumlCallPadding,% + draw/.get=\tikzumlCallDrawColor, fill/.get=\tikzumlCallFillColor,% + text/.get=\tikzumlCallTextColor% + }% + % + \edef\tikzumlfillcall{\tikzumlCallFillColor}% + \edef\tikzumldrawcall{\tikzumlCallDrawColor}% + \edef\tikzumltextcall{\tikzumlCallTextColor}% + \edef\tikzumltypecall{\tikzumlCallType}% + % + \ifthenelse{\equal{\tikzumlCallDT}{tikzumlEmpty}}{% + \ifnum\thetikzumlCallNum=1% + \def\tikzumlCallDT{2}% + \def\tikzumlcallSrc{2}% + \else% + \def\tikzumlCallDT{2}% + \def\tikzumlcallSrc{1}% + \fi% + }{% + \def\tikzumlcallSrc{0}% + }% + % + \let\tikzumlCallStartNodeNameold\tikzumlCallStartNodeName% + \def\tikzumlCallStartNodeName{#2}% + \let\tikzumlCallEndNodeNameold\tikzumlCallEndNodeName% + \def\tikzumlCallEndNodeName{#3}% + \def\tikzumlcallheight{\tikzumlCallPadding}% + % + % managing time delays from previous/parent fragments + \ifnum\thetikzumlCallStartFragmentNum>0% + \let\tikzumlCallDTold\tikzumlCallDT% + \pgfmathparse{0.5*\tikzumlFragment@paddingy+\tikzumlCallDTold}% + \edef\tikzumlCallDT{\pgfmathresult}% + \addtocounter{tikzumlCallStartFragmentNum}{-1} + \fi% + \ifnum\thetikzumlCallEndFragmentNum>0% + \let\tikzumlCallDTold\tikzumlCallDT% + \pgfmathparse{0.5*\tikzumlFragment@paddingy+\tikzumlCallDTold}% + \edef\tikzumlCallDT{\pgfmathresult}% + \addtocounter{tikzumlCallEndFragmentNum}{-1} + \fi% + \ifnum\thetikzumlFragmentPartNum>0% + \let\tikzumlCallDTold\tikzumlCallDT% + \pgfmathparse{0.5*\tikzumlFragment@paddingy+\tikzumlCallDTold}% + \edef\tikzumlCallDT{\pgfmathresult}% + \fi% + % + % managing parent-child structure + \ifnum\thetikzumlCallLevel>0% + \let\tikzumlCall@nameold\tikzumlCall@name% + \edef\tikzumlCall@name{\tikzumlCallName}% + \let\tikzumlCall@parentold\tikzumlCall@parent% + \edef\tikzumlCall@parent{\tikzumlCall@parentold @@\tikzumlCall@nameold}% + \else% + \edef\tikzumlCall@parent{}% + \edef\tikzumlCall@parentold{}% + \edef\tikzumlCall@nameold{} + \edef\tikzumlCall@name{\tikzumlCallName}% + \fi% + % + \begingroup% + \def\_{_}\edef\x{\endgroup% + \def\noexpand\tikzumlCall@nodeName{\tikzumlCall@name}}\x% + % + \let\tikzumlCall@nodeNameold\tikzumlCall@nodeName% + % + \def\tikzumlcallstyle{tikzuml \tikzumlCallType-msg style}% + % + % top node of activity period of call sender + \begin{pgfonlayer}{connections}% + \pgfmathparse{\tikzumlCallDT+\tikzumlcallSrc}% + \draw (\csname tikzumlLastChild@\tikzumlCallStartNodeName \endcsname)+(0,-\pgfmathresult ex) node[coordinate, name=tikzumlTmpNode] {};% + \node[tikzuml activity style] (st-\tikzumlCall@nodeName) at (\tikzumlCallStartNodeName |- tikzumlTmpNode) {};% + % + % update last node drawn on sender lifeline + \expandafter\xdef\csname tikzumlLastChild@\tikzumlCallStartNodeName \endcsname{st-\tikzumlCall@nodeName}% + % + % top node of activity period of call receiver + \ifstrequal{\tikzumlCallStartNodeName}{\tikzumlCallEndNodeName}{% + \draw (st-\tikzumlCall@nodeName)+(0,-0.75*\tikzumlCallPadding ex) node[coordinate, name=tikzumlTmpNode] {};% + \node[tikzuml activity style] (et-\tikzumlCall@nodeName) at (\tikzumlCallStartNodeName |- tikzumlTmpNode) {};% + }{% + \node[tikzuml activity style] (et-\tikzumlCall@nodeName) at (\tikzumlCallEndNodeName |- st-\tikzumlCall@nodeName) {};% + }% + % + % update last node drawn on receiver lifeline + \expandafter\xdef\csname tikzumlLastChild@\tikzumlCallEndNodeName \endcsname{et-\tikzumlCall@nodeName}% + \xdef\tikzumlCallBottomSrc{et-\tikzumlCall@nodeName}% + \end{pgfonlayer}% + % + \stepcounter{tikzumlCallLevel}% +}{% + \addtocounter{tikzumlCallLevel}{-1}% + % + % bottom nodes of activity periods of call sender and receiver + \begin{pgfonlayer}{connections}% + \ifnum\tikzumlCall@lastchildNum=\thetikzumlCallNum% + % + % this test occurs a bug with latex package preview + \ifstrequal{\tikzumlCallStartNodeName}{\tikzumlCallEndNodeName}{% + \draw (\tikzumlCallBottomSrc)+(0,-\tikzumlCallPadding ex) node[coordinate, name=tikzumlTmpNode] {};% + \node[tikzuml activity style] (eb-\tikzumlCall@nodeName) at (\tikzumlCallEndNodeName |- tikzumlTmpNode) {};% + \draw (eb-\tikzumlCall@nodeName)+(0,-0.75*\tikzumlCallPadding ex) node[coordinate, name=tikzumlTmpNode] {};% + \node[tikzuml activity style] (sb-\tikzumlCall@nodeName) at (\tikzumlCallStartNodeName |- tikzumlTmpNode) {};% + }{% + \ifthenelse{\equal{\tikzumlCallReturn}{tikzumlEmpty}}{% + \pgfmathsetmacro{\tikzumlCallPaddingd}{0.5*\tikzumlCallPadding}% + }{% + \pgfmathsetmacro{\tikzumlCallPaddingd}{1.2*\tikzumlCallPadding}% + }% + \draw (\tikzumlCallBottomSrc)+(0,-\tikzumlCallPaddingd ex) node[coordinate, name=tikzumlTmpNode] {};% + \node[tikzuml activity style] (eb-\tikzumlCall@nodeName) at (\tikzumlCallEndNodeName |- tikzumlTmpNode) {};% + \node[tikzuml activity style] (sb-\tikzumlCall@nodeName) at (\tikzumlCallStartNodeName |- eb-\tikzumlCall@nodeName) {};% + }% + \xdef\tikzumlCallBottomSrc{sb-\tikzumlCall@nodeName}% + \else% + % + % managing time delays from previous/parent fragments + \ifnum\thetikzumlCallStartFragmentNum>0% + \let\tikzumlcallheightold\tikzumlCallPadding% + \pgfmathparse{\tikzumlcallheightold+0.5*\tikzumlFragment@paddingy}% + \edef\tikzumlcallheight{\pgfmathresult}% + \addtocounter{tikzumlCallStartFragmentNum}{-1}% + \fi% + \ifnum\thetikzumlCallEndFragmentNum>0% + \let\tikzumlcallheightold\tikzumlCallPadding% + \pgfmathparse{\tikzumlcallheightold+0.5*\tikzumlFragment@paddingy}% + \edef\tikzumlcallheight{\pgfmathresult}% + \addtocounter{tikzumlCallEndFragmentNum}{-1}% + \fi% + % + \ifstrequal{\tikzumlCallStartNodeName}{\tikzumlCallEndNodeName}{% + \draw (\tikzumlCallBottomSrc)+(0,-\tikzumlcallheight ex) node[coordinate, name=tikzumlTmpNode] {};% + \node[tikzuml activity style] (eb-\tikzumlCall@nodeName) at (\tikzumlCallEndNodeName |- tikzumlTmpNode) {};% + \draw (eb-\tikzumlCall@nodeName)+(0,-0.75*\tikzumlCallPadding ex) node[coordinate, name=tikzumlTmpNode] {};% + \node[tikzuml activity style] (sb-\tikzumlCall@nodeName) at (\tikzumlCallStartNodeName |- tikzumlTmpNode) {};% + }{% + \draw (\tikzumlCallBottomSrc)+(0,-\tikzumlcallheight ex) node[coordinate, name=tikzumlTmpNode] {};% + \node[tikzuml activity style] (eb-\tikzumlCall@nodeName) at (\tikzumlCallEndNodeName |- tikzumlTmpNode) {};% + \node[tikzuml activity style] (sb-\tikzumlCall@nodeName) at (\tikzumlCallStartNodeName |- eb-\tikzumlCall@nodeName) {};% + }% + % + \xdef\tikzumlCallBottomSrc{sb-\tikzumlCall@nodeName}% + \fi% + \end{pgfonlayer}% + % + % draw activity periods + \begin{pgfonlayer}{activity}% + \ifstrequal{\tikzumlCallStartNodeName}{\tikzumlCallEndNodeName}{% + % draw root activity period only + \ifnum\thetikzumlCallLevel=0% + \draw[draw=\tikzumldrawcall, fill=\tikzumlfillcall] (st-\tikzumlCall@nodeName.north west) rectangle (sb-\tikzumlCall@nodeName.south east);% + \else% + % draw root activity from inner call + \ifthenelse{\equal{\tikzumlCallStartNodeName}{\tikzumlCallEndNodeNameold}}{}{% + \draw[draw=\tikzumldrawcall, fill=\tikzumlfillcall] (st-\tikzumlCall@nodeName.north west) rectangle (sb-\tikzumlCall@nodeName.south east);% + }% + \fi% + }{% + % draw root activity period + \ifnum\thetikzumlCallLevel=0% + \draw[draw=\tikzumldrawcall, fill=\tikzumlfillcall] (st-\tikzumlCall@nodeName.north west) rectangle (sb-\tikzumlCall@nodeName.south east);% + \else% + % draw root activity from inner call + \ifthenelse{\equal{\tikzumlCallStartNodeName}{\tikzumlCallEndNodeNameold}}{}{% + \draw[draw=\tikzumldrawcall, fill=\tikzumlfillcall] (st-\tikzumlCall@nodeName.north west) rectangle (sb-\tikzumlCall@nodeName.south east);% + }% + \fi% + % draw receiver activity period + \draw[draw=\tikzumldrawcall, fill=\tikzumlfillcall] (et-\tikzumlCall@nodeName.north west) rectangle (eb-\tikzumlCall@nodeName.south east);% + }% + \end{pgfonlayer}% + \ifthenelse{\equal{\tikzumlCallDefaultFillColor}{\tikzumlCallFillColor}}{}{% + \fill[\tikzumlfillcall] (st-\tikzumlCall@nodeName.north west) rectangle (sb-\tikzumlCall@nodeName.south east);% + \draw[\tikzumldrawcall] (st-\tikzumlCall@nodeName.north west) rectangle (sb-\tikzumlCall@nodeName.south west) (st-\tikzumlCall@nodeName.north east) rectangle (sb-\tikzumlCall@nodeName.south east);% + }% + % + % update last nodes drawn on sender and receiver lifelines + \expandafter\xdef\csname tikzumlLastChild@\tikzumlCallEndNodeName \endcsname{eb-\tikzumlCall@nodeName}% + \expandafter\xdef\csname tikzumlLastChild@\tikzumlCallStartNodeName \endcsname{sb-\tikzumlCall@nodeName}% + % + % draw call arrows + \begin{pgfonlayer}{connections}% + \ifstrequal{\tikzumlCallStartNodeName}{\tikzumlCallEndNodeName}{% + \draw[\tikzumlcallstyle, \tikzumldrawcall] (st-\tikzumlCall@nodeName.east) -- ++(2.5*\tikzumlCallPadding ex,0) % + -- ++(0,-0.75*\tikzumlCallPadding ex) % + node[font=\tikzumlDefaultFont, text=\tikzumltextcall, midway, right, name=\tikzumlCall@nodeName-op] {\tikzumlCallOp} % + -- (et-\tikzumlCall@nodeName.east);% + % + % draw return arrow and update fit for parent fragment + \ifthenelse{\equal{\tikzumltypecall}{synchron}}{% + \ifthenelse{\NOT\equal{\tikzumlCallReturn}{}\OR\equal{\tikzumlCallWithReturn}{tikzumlTrue}}{% + \ifnum\c@tikzumlFragmentLevel>0% + \edef\tikzumlFragmentFitOld{\csname tikzumlFragmentFit\tikzumlFragment@name \endcsname}% + \expandafter\xdef\csname tikzumlFragmentFit\tikzumlFragment@name \endcsname{\tikzumlFragmentFitOld (st-\tikzumlCall@nodeName) (et-\tikzumlCall@nodeName) (eb-\tikzumlCall@nodeName) (sb-\tikzumlCall@nodeName) (\tikzumlCall@nodeName-op) (\tikzumlCall@nodeName-return)}% + \fi% + % + \draw[tikzuml call return style, \tikzumldrawcall] (eb-\tikzumlCall@nodeName.east) -- ++(2.5*\tikzumlCallPadding ex,0) + -- ++(0,-0.75*\tikzumlCallPadding ex) % + node[font=\tikzumlDefaultFont, text=\tikzumltextcall, midway, right, name=\tikzumlCall@nodeName-return] {\tikzumlCallReturn} % + -- (sb-\tikzumlCall@nodeName.east);% + }{% + \ifnum\c@tikzumlFragmentLevel>0% + \edef\tikzumlFragmentFitOld{\csname tikzumlFragmentFit\tikzumlFragment@name \endcsname}% + \expandafter\xdef\csname tikzumlFragmentFit\tikzumlFragment@name \endcsname{\tikzumlFragmentFitOld (st-\tikzumlCall@nodeName) (et-\tikzumlCall@nodeName) (eb-\tikzumlCall@nodeName) (sb-\tikzumlCall@nodeName) (\tikzumlCall@nodeName-op)}% + \fi% + }% + }{% + \ifnum\c@tikzumlFragmentLevel>0% + \edef\tikzumlFragmentFitOld{\csname tikzumlFragmentFit\tikzumlFragment@name \endcsname}% + \expandafter\xdef\csname tikzumlFragmentFit\tikzumlFragment@name \endcsname{\tikzumlFragmentFitOld (st-\tikzumlCall@nodeName) (et-\tikzumlCall@nodeName) (eb-\tikzumlCall@nodeName) (sb-\tikzumlCall@nodeName) (\tikzumlCall@nodeName-op)}% + \fi% + }% + }{% + % draw call arrows + \pgfextractx{\tikzumlCall@xa}{\pgfpointanchor{\tikzumlCallStartNodeName}{center}}% + \pgfextractx{\tikzumlCall@xb}{\pgfpointanchor{\tikzumlCallEndNodeName}{center}}% + % + \ifthenelse{\tikzumlCall@xb>\tikzumlCall@xa}{% + \draw[\tikzumlcallstyle, \tikzumldrawcall] (st-\tikzumlCall@nodeName.east) -- (et-\tikzumlCall@nodeName.west) % + node[font=\tikzumlDefaultFont, text=\tikzumltextcall, midway, above=-0.4ex, name=\tikzumlCall@nodeName-op] {\tikzumlCallOp};% + }{% + \draw[\tikzumlcallstyle, \tikzumldrawcall] (st-\tikzumlCall@nodeName.west) -- (et-\tikzumlCall@nodeName.east) % + node[font=\tikzumlDefaultFont, text=\tikzumltextcall, midway, above=-0.4ex, name=\tikzumlCall@nodeName-op] {\tikzumlCallOp};% + }% + % + % draw return arrow and update fit for parent fragment + \ifthenelse{\equal{\tikzumltypecall}{synchron}}{% + \ifthenelse{\NOT\equal{\tikzumlCallReturn}{}\OR\equal{\tikzumlCallWithReturn}{tikzumlTrue}}{% + \ifnum\c@tikzumlFragmentLevel>0% + \edef\tikzumlFragmentFitOld{\csname tikzumlFragmentFit\tikzumlFragment@name \endcsname}% + \expandafter\xdef\csname tikzumlFragmentFit\tikzumlFragment@name \endcsname{\tikzumlFragmentFitOld (st-\tikzumlCall@nodeName) (et-\tikzumlCall@nodeName) (eb-\tikzumlCall@nodeName) (sb-\tikzumlCall@nodeName) (\tikzumlCall@nodeName-op) (\tikzumlCall@nodeName-return)}% + \fi% + % + \ifthenelse{\tikzumlCall@xb>\tikzumlCall@xa}{% + \draw[tikzuml call return style, \tikzumldrawcall] (eb-\tikzumlCall@nodeName.west) -- (sb-\tikzumlCall@nodeName.east) % + node[font=\tikzumlDefaultFont, text=\tikzumltextcall, midway, above=-0.4ex, name=\tikzumlCall@nodeName-return] {\tikzumlCallReturn};% + }{% + \draw[tikzuml call return style, \tikzumldrawcall] (eb-\tikzumlCall@nodeName.east) -- (sb-\tikzumlCall@nodeName.west) % + node[font=\tikzumlDefaultFont, text=\tikzumltextcall, midway, above=-0.4ex, name=\tikzumlCall@nodeName-return] {\tikzumlCallReturn};% + }% + }{% + \ifnum\c@tikzumlFragmentLevel>0% + \edef\tikzumlFragmentFitOld{\csname tikzumlFragmentFit\tikzumlFragment@name \endcsname}% + \expandafter\xdef\csname tikzumlFragmentFit\tikzumlFragment@name \endcsname{\tikzumlFragmentFitOld (st-\tikzumlCall@nodeName) (et-\tikzumlCall@nodeName) (eb-\tikzumlCall@nodeName) (sb-\tikzumlCall@nodeName) (\tikzumlCall@nodeName-op)}% + \fi% + }% + }{% + \ifnum\c@tikzumlFragmentLevel>0% + \edef\tikzumlFragmentFitOld{\csname tikzumlFragmentFit\tikzumlFragment@name \endcsname}% + \expandafter\xdef\csname tikzumlFragmentFit\tikzumlFragment@name \endcsname{\tikzumlFragmentFitOld (st-\tikzumlCall@nodeName) (et-\tikzumlCall@nodeName) (eb-\tikzumlCall@nodeName) (sb-\tikzumlCall@nodeName) (\tikzumlCall@nodeName-op)}% + \fi% + }% + }% + \end{pgfonlayer}% +}% +% +% alias for function self call +\newenvironment{umlcallself}[2][]{\begin{umlcall}[#1]{#2}{#2} }{\end{umlcall}}% +% +% define a combined fragment +% optional : name: name of fragment +% type: type of fragment (opt, alt, break, loop, par, critical, ignore, consider, assert, neg, weak, strict, ref) +% label: label of fragment (ex : condition for opt, iterator for loop, ...) +% inner xsep, inner ysep: padding of the fragment box +% draw, fill, text: colors +\newenvironment{umlfragment}[1][]{% + % define a fragment separator + % optional : label of the fragment part (ex : else for alt) + \providecommand{\umlfpart}[1][]{% + \stepcounter{tikzumlFragmentPartNum}% + % + \node[outer sep=0, inner xsep=\tikzumlFragmentXSep ex, inner ysep=\tikzumlFragmentYSep ex, fit=\csname tikzumlFragmentFit\tikzumlFragment@name \endcsname, name=\tikzumlFragment@name-Part-tmp] {};% + \node[anchor=east, name=\tikzumlFragment@name-PartType-\thetikzumlFragmentPartNum] at (\tikzumlFragment@name-Part-tmp.north west |- \tikzumlCallBottomSrc) {\phantom{\tikzumlFragmentType}};% + \draw (\tikzumlFragment@name-PartType-\thetikzumlFragmentPartNum.north west |- \tikzumlCallBottomSrc)+(0,-0.4*\tikzumlFragment@paddingy ex) node[name=\tikzumlFragment@name-PartWest-\thetikzumlFragmentPartNum] {};% + \draw (\tikzumlFragment@name-Part-tmp.north east |- \tikzumlCallBottomSrc)+(0,-0.4*\tikzumlFragment@paddingy ex) node[name=\tikzumlFragment@name-PartEast-\thetikzumlFragmentPartNum] {};% + \draw[dashed] (\tikzumlFragment@name-PartWest-\thetikzumlFragmentPartNum) -- (\tikzumlFragment@name-PartEast-\thetikzumlFragmentPartNum); + \draw (\tikzumlFragment@name-PartType-\thetikzumlFragmentPartNum)+(0,-0.4*\tikzumlFragment@paddingy ex) node[name=tikzumlTmpNode] {\phantom{\tikzumlFragmentType}};% + \node[anchor=north west] at (tikzumlTmpNode.south west) {[##1]};% + }% + % + \stepcounter{tikzumlFragmentNum}% + % + \pgfkeys{/tikzuml/fragment/.cd, name/.initial=fragment@\alph{tikzumlFragmentNum}, type/.initial=\tikzumlFragmentDefaultType,% + label/.initial=tikzumlEmpty,% + inner xsep/.initial=\tikzumlFragmentDefaultXSep, inner ysep/.initial=\tikzumlFragmentDefaultYSep,% + draw/.initial=\tikzumlDefaultDrawColor, fill/.initial=\tikzumlFragmentDefaultFillColor,% + text/.initial=\tikzumlDefaultTextColor,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \errmessage{TIKZUML ERROR : in umlfragment, invalid option \keyname}% + }% + }% + \pgfkeys{/tikzuml/fragment/.cd, #1}% + \pgfkeys{/tikzuml/fragment/.cd, name/.get=\tikzumlFragmentName, type/.get=\tikzumlFragmentType,% + label/.get=\tikzumlFragmentLabel,% + inner xsep/.get=\tikzumlFragmentXSep, inner ysep/.get=\tikzumlFragmentYSep,% + draw/.get=\tikzumlFragmentDrawColor, fill/.get=\tikzumlFragmentFillColor,% + text/.get=\tikzumlFragmentTextColor% + }% + % + \ifthenelse{\equal{\tikzumlFragmentLabel}{tikzumlEmpty}}{% + \def\tikzumlFragmentLabel{}% + }{% + \let\tikzumlFragmentLabelold\tikzumlFragmentLabel% + \def\tikzumlFragmentLabel{[\tikzumlFragmentLabelold]}% + }% + % + \ifnum\thetikzumlFragmentLevel>0% + \let\tikzumlFragment@parentold\tikzumlFragment@parent% + \let\tikzumlFragment@nameold\tikzumlFragment@name% + \edef\tikzumlFragment@parent{\tikzumlFragment@nameold}% + \else% + \setcounter{tikzumlFragmentPartNum}{0}% + \edef\tikzumlFragment@parent{}% + \edef\tikzumlFragment@parentold{}% + \edef\tikzumlFragment@nameold{}% + \fi% + % + \edef\tikzumlFragment@name{\tikzumlFragmentName}% + \expandafter\gdef\csname tikzumlFragmentFit\tikzumlFragment@name \endcsname{}% + % + \stepcounter{tikzumlFragmentLevel}% + % + \ifnum\thetikzumlCallLevel>0% + \stepcounter{tikzumlCallStartFragmentNum}% + \fi% + % + \pgfmathparse{6*\tikzumlFragmentYSep}% + \xdef\tikzumlFragment@paddingy{\pgfmathresult}% + \if\c@tikzumlFragmentLevelNum=0% + \setcounter{tikzumlFragmentLevelNum}{\thetikzumlFragmentLevel}% + \fi% + % + % time delay adjustment for two consecutive fragments + \ifnum\thetikzumlCallEndFragmentNum>0% + \addtocounter{tikzumlCallEndFragmentNum}{-1} + \fi% +}{% + % + \addtocounter{tikzumlFragmentLevel}{-1}% + % + \ifnum\thetikzumlFragmentLevel>0% + \edef\tikzumlFragmentFitOld{\csname tikzumlFragmentFit\tikzumlFragment@parent \endcsname}% + \expandafter\xdef\csname tikzumlFragmentFit\tikzumlFragment@parent \endcsname{\tikzumlFragmentFitOld (\tikzumlFragment@name)}% + \fi% + % + % draw working fragment box + \begin{pgfonlayer}{fragment\thetikzumlFragmentLevel}% + \node[outer sep=0, inner xsep=\tikzumlFragmentXSep ex, inner ysep=\tikzumlFragmentYSep ex, fit=\csname tikzumlFragmentFit\tikzumlFragment@name \endcsname, name=\tikzumlFragment@name-back] {};% + \end{pgfonlayer}% + % + % draw type and label + \node[text=\tikzumlFragmentTextColor, font=\tikzumlDefaultFont, anchor=north east, name=\tikzumlFragment@name-type] % + at (\tikzumlFragment@name-back.north west) {\tikzumlFragmentType};% + \node[text=\tikzumlFragmentTextColor, font=\tikzumlDefaultFont, anchor=north west, name=\tikzumlFragment@name-label] % + at (\tikzumlFragment@name-type.south west) {\tikzumlFragmentLabel};% + % + % draw final fragment box + \begin{pgfonlayer}{fragment\thetikzumlFragmentLevel}% + \node[draw=\tikzumlFragmentDrawColor, fill=\tikzumlFragmentFillColor, outer sep=0, inner sep=0, font=\tikzumlDefaultFont, fit=(\tikzumlFragment@name-back) (\tikzumlFragment@name-type) (\tikzumlFragment@name-label), name=\tikzumlFragment@name] {};% + \end{pgfonlayer}% + % + \draw[draw=\tikzumlFragmentDrawColor] (\tikzumlFragment@name.north west) rectangle (\tikzumlFragment@name.south east);% + \draw (\tikzumlFragment@name-type.south east)+(0,1ex) node[name=\tikzumlFragment@name-typetop, inner sep=0] {};% + \draw (\tikzumlFragment@name-type.south east)+(-1ex,0) node[name=\tikzumlFragment@name-typeleft, inner sep=0] {};% + \draw (\tikzumlFragment@name.north west) -| (\tikzumlFragment@name-typetop.center) -- (\tikzumlFragment@name-typeleft.center) -| (\tikzumlFragment@name.north west);% + % + \ifnum\thetikzumlCallLevel>0% + \stepcounter{tikzumlCallEndFragmentNum}% + \fi% +}% +% +% define a constructor call +% arg : call sender +% name of constructed object +% optional : x: coordinate of the new object +% stereo: stereotype of the new object +% class: class type of the new object +% dt: time delay from last event +% name: name of the call +% draw, fill, text: colors +% no ddots: when used, disable printing of double dots +\newcommand{\umlcreatecall}[3][]{% + \stepcounter{tikzumlCallNum}% + \edef\tikzumlCall@lastchildNum{\thetikzumlCallNum}% for testing presence of sub-calls + \gdef\tikzumlInCreateCall{1}% + \pgfkeys{/tikzuml/createcall/.cd, x/.initial=tikzumlEmpty, stereo/.initial=\tikzumlObjectDefaultStereo, class/.initial={},% + dt/.initial=\tikzumlCreateCallDefaultDT, name/.initial=call-\thetikzumlCallNum,% + draw/.initial=\tikzumlDefaultDrawColor, fill/.initial=\tikzumlCallDefaultFillColor,% + text/.initial=\tikzumlDefaultTextColor,% + draw obj/.initial=\tikzumlDefaultDrawColor, fill obj/.initial=\tikzumlObjectDefaultFillColor,% + text obj/.initial=\tikzumlDefaultTextColor,% + no ddots/.is if=tikzumlcreatecallNoDDots,% + no ddots=false,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \errmessage{TIKZUML ERROR : in umlcreatecall, invalid option \keyname}% + }% + }% + \pgfkeys{/tikzuml/createcall/.cd, #1}% + \pgfkeys{/tikzuml/createcall/.cd, x/.get=\tikzumlCreateCallX, stereo/.get=\tikzumlCreateCallStereo,% + class/.get=\tikzumlCreateCallClass,% + dt/.get=\tikzumlCreateCallDT, name/.get=\tikzumlCreateCallName,% + draw/.get=\tikzumlCreateCallDrawColor, fill/.get=\tikzumlCreateCallFillColor,% + text/.get=\tikzumlCreateCallTextColor,% + draw obj/.get=\tikzumlCreateCallObjectDrawColor, fill obj/.get=\tikzumlCreateCallObjectFillColor,% + text obj/.get=\tikzumlCreateCallObjectTextColor% + }% + % + \def\tikzumlCreateCallSrc@name{#2}% + % + \begingroup% + \def\_{_}\edef\x{\endgroup% + \def\noexpand\tikzumlCreateCallSrc@nodeName{\tikzumlCreateCallSrc@name}}\x% + % + % managing time delays from previous/parent fragments + \ifnum\thetikzumlCallStartFragmentNum>0% + \let\tikzumlCreateCallDTold\tikzumlCreateCallDT% + \pgfmathparse{0.5*\tikzumlFragment@paddingy+\tikzumlCreateCallDTold}% + \edef\tikzumlCreateCallDT{\pgfmathresult}% + \addtocounter{tikzumlCallStartFragmentNum}{-1} + \fi% + \ifnum\thetikzumlCallEndFragmentNum>0% + \let\tikzumlCreateCallDTold\tikzumlCreateCallDT% + \pgfmathparse{0.5*\tikzumlFragment@paddingy+\tikzumlCreateCallDTold}% + \edef\tikzumlCreateCallDT{\pgfmathresult}% + \addtocounter{tikzumlCallEndFragmentNum}{-1} + \fi% + \ifnum\thetikzumlFragmentPartNum>0% + \let\tikzumlCreateCallDTold\tikzumlCreateCallDT% + \pgfmathparse{0.5*\tikzumlFragment@paddingy+\tikzumlCreateCallDTold}% + \edef\tikzumlCreateCallDT{\pgfmathresult}% + \fi% + % + % managing parent-child structure + \ifnum\thetikzumlCallLevel>0% + \let\tikzumlCall@nameold\tikzumlCall@name% + \def\tikzumlCall@name{\tikzumlCreateCallName}% + \let\tikzumlCall@parentold\tikzumlCall@parent% + \edef\tikzumlCall@parent{\tikzumlCall@parentold @@\tikzumlCall@nameold}% + \else% + \edef\tikzumlCall@parent{}% + \edef\tikzumlCall@parentold{}% + \edef\tikzumlCall@nameold{} + \edef\tikzumlCall@name{\tikzumlCreateCallName}% + \fi% + % + \begingroup% + \def\_{_}\edef\x{\endgroup% + \def\noexpand\tikzumlCreateCall@nodeName{\tikzumlCall@name}}\x% + % + \draw (\csname tikzumlLastChild@\tikzumlCreateCallSrc@nodeName \endcsname)+(0,-\tikzumlCreateCallDT ex) node[name=st-\tikzumlCreateCall@nodeName, tikzuml activity style] {};% + % + \xdef\tikzumlCreateCallObjectSrc{st-\tikzumlCreateCall@nodeName}% + % + \iftikzumlcreatecallNoDDots% + \umlobject[x=\tikzumlCreateCallX, stereo=\tikzumlCreateCallStereo, class=\tikzumlCreateCallClass, draw=\tikzumlCreateCallObjectDrawColor, fill=\tikzumlCreateCallObjectFillColor, text=\tikzumlCreateCallObjectTextColor, no ddots]{#3}% + \else + \umlobject[x=\tikzumlCreateCallX, stereo=\tikzumlCreateCallStereo, class=\tikzumlCreateCallClass, draw=\tikzumlCreateCallObjectDrawColor, fill=\tikzumlCreateCallObjectFillColor, text=\tikzumlCreateCallObjectTextColor]{#3}% + \fi + % + \draw (\csname tikzumlLastChild@\tikzumlCreateCallSrc@nodeName \endcsname |- #3)+(0,-0.5*\tikzumlCreateCallDT ex) node[name=sb-\tikzumlCreateCall@nodeName, tikzuml activity style] {};% + % + \expandafter\xdef\csname tikzumlLastChild@\tikzumlCreateCallSrc@nodeName \endcsname{sb-\tikzumlCreateCall@nodeName}% + \xdef\tikzumlCallBottomSrc{sb-\tikzumlCreateCall@nodeName}% + % + \begin{pgfonlayer}{connections}% + \draw[tikzuml synchron-msg style, \tikzumlCreateCallDrawColor] (st-\tikzumlCreateCall@nodeName) -- (#3) node[midway, above, font=\tikzumlDefaultFont, text=\tikzumlCreateCallTextColor, name=\tikzumlCreateCall@nodeName-op] {create};% + \end{pgfonlayer}% + % + \ifnum\thetikzumlCallLevel=0% + \begin{pgfonlayer}{activity}% + \draw[draw=\tikzumlCreateCallDrawColor, fill=\tikzumlCreateCallFillColor] (st-\tikzumlCreateCall@nodeName.north west) rectangle (sb-\tikzumlCreateCall@nodeName.south east);% + \end{pgfonlayer}% + \fi% + % add to fit fragment + \ifnum\c@tikzumlFragmentLevel>0% + \edef\tikzumlFragmentFitOld{\csname tikzumlFragmentFit\tikzumlFragment@name \endcsname}% + \expandafter\xdef\csname tikzumlFragmentFit\tikzumlFragment@name \endcsname{\tikzumlFragmentFitOld (st-\tikzumlCreateCall@nodeName) (sb-\tikzumlCreateCall@nodeName) (\tikzumlCreateCall@nodeName-op) (#3) }% + \fi% +} +% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% component diagrams % +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +\tikzstyle{tikzuml connector style}=[color=\tikzumlDefaultDrawColor, -]% +% +\newcounter{tikzumlComponentLevel}% +\newcounter{tikzumlComponentSubComponentNum}% +\newcounter{tikzumlConnectorNum}% +\setcounter{tikzumlConnectorNum}{1}% +% +\newif\iftikzumlcomponentWithoutCoords% +% +\newcommand{\picturedcomponent}[1]{% + \pgfkeys{/tikzuml/component/picture/.cd, scale/.initial=1, .unknown/.code={}}% + \pgfkeys{/tikzuml/component/picture/.cd,#1}% + \pgfkeys{/tikzuml/component/picture/.cd, scale/.get=\tikzumlComponentScale}% + \begin{tikzpicture}[#1]% + \filldraw (0,0) rectangle (1ex,1.5ex);% + \filldraw (-0.2ex,0.4ex) rectangle (0.2ex,0.6ex);% + \filldraw (-0.2ex,0.9ex) rectangle (0.2ex,1.1ex);% + \end{tikzpicture}% +}% +% +% define a uml component +% args : name of the component +% content of the component +% optional args : x,y coordinates of the component +% width of the component node +\newenvironment{umlcomponent}[2][]{% + \ifnum\thetikzumlComponentLevel>0% + \let\tikzumlComponent@nameold\tikzumlComponent@fitname% + \let\tikzumlComponent@parentold\tikzumlComponent@parent% + \edef\tikzumlComponent@parent{\tikzumlComponent@parentold @@\tikzumlComponent@nameold}% + \else% + \def\tikzumlComponent@parent{}% + \fi% + % + \stepcounter{tikzumlComponentLevel}% + % + \pgfkeys{/tikzuml/component/.cd, x/.initial=\tikzumlDefaultX, y/.initial=\tikzumlDefaultX, width/.initial=\tikzumlComponentDefaultWidth, name/.initial={},% + draw/.initial=\tikzumlDefaultDrawColor, fill/.initial=\tikzumlComponentDefaultFillColor,% + text/.initial=\tikzumlDefaultTextColor, style/.style={},% + no coords/.is if=tikzumlcomponentWithoutCoords,% + no coords=false,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{above}\OR% + \equal{\keyname}{left}\OR% + \equal{\keyname}{below}\OR% + \equal{\keyname}{right}\OR% + \equal{\keyname}{above left}\OR% + \equal{\keyname}{above right}\OR% + \equal{\keyname}{below left}\OR% + \equal{\keyname}{below right}}{% + \IfSubStr{\keyvalue}{ of }{% + \pgfkeys{/tikzuml/component/.cd, no coords}% + }{}% + }{}% + \ifx\keyvalue\pgfkeysnovalue% + \pgfkeys{/tikzuml/component/.cd, style/.append style/.expand once={\keyname}}% + \else% + \pgfkeys{/tikzuml/component/.cd, style/.append style/.expand twice={\expandafter\keyname\expandafter=\keyvalue}}% + \fi% + %\errmessage{TIKZUML ERROR : in umlcomponent, invalid option \keyname}% + }% + }% + \pgfkeys{/tikzuml/component/.cd, #1}% + \pgfkeys{/tikzuml/component/.cd, x/.get=\tikzumlComponentXShift, y/.get=\tikzumlComponentYShift,% + width/.get=\tikzumlComponentMinimumWidth, name/.get=\tikzumlComponentName,% + draw/.get=\tikzumlComponentDrawColor, fill/.get=\tikzumlComponentFillColor,% + text/.get=\tikzumlComponentTextColor% + }% + % + \ifthenelse{\equal{\tikzumlComponentName}{}}{% + \edef\tikzumlComponent@name{#2}% + }{% + \edef\tikzumlComponent@name{\tikzumlComponentName}% + }% + % + \begingroup% + \def\_{@}\edef\x{\endgroup% + \def\noexpand\tikzumlComponent@fitname{\tikzumlComponent@name}}\x% + % + \let\tikzumlComponent@nodeNameold\tikzumlComponent@nodeName% + \def\tikzumlComponent@caption{#2}% + % + \begingroup% + \def\_{_}\edef\x{\endgroup% + \def\noexpand\tikzumlComponent@nodeName{\tikzumlComponent@name}}\x% + % + \expandafter\gdef\csname tikzumlComponentFit\tikzumlComponent@parent @@\tikzumlComponent@fitname\endcsname{}% + % + \setcounter{tikzumlComponentSubComponentNum}{0}% + % + \begin{scope}[xshift=\tikzumlComponentXShift cm, yshift=\tikzumlComponentYShift cm]% +}{% + \addtocounter{tikzumlComponentLevel}{-1}% + \begin{pgfonlayer}{component\thetikzumlComponentLevel}% + % + % if contains nothing, one define a fictive node to enable the fit option + \ifnum\c@tikzumlComponentSubComponentNum=0% + \iftikzumlcomponentWithoutCoords% + \node[inner ysep=0.5ex, minimum width=\tikzumlComponentMinimumWidth, font=\tikzumlDefaultFont, /tikzuml/component/style] (\tikzumlComponent@nodeName-root) {\phantom{\tikzumlComponent@nodeName}};% + \else% + \node[inner ysep=0.5ex, minimum width=\tikzumlComponentMinimumWidth, font=\tikzumlDefaultFont, /tikzuml/component/style] (\tikzumlComponent@nodeName-root) at (0,0) {\phantom{\tikzumlComponent@nodeName}};% + \fi% + \expandafter\xdef\csname tikzumlComponentFit\tikzumlComponent@parent @@\tikzumlComponent@fitname\endcsname{(\tikzumlComponent@nodeName-root)}% + \fi% + % + \ifnum\c@tikzumlComponentLevel>0% + \def\tikzumlComponentFitTmp{\csname tikzumlComponentFit\tikzumlComponent@parent\endcsname}% + \expandafter\xdef\csname tikzumlComponentFit\tikzumlComponent@parent\endcsname{\tikzumlComponentFitTmp (\tikzumlComponent@nodeName-body) (\tikzumlComponent@nodeName-caption)}% + \stepcounter{tikzumlComponentSubComponentNum}% + \fi% + % + \node[inner sep=2ex, font=\tikzumlDefaultFont, fit = \csname tikzumlComponentFit\tikzumlComponent@parent @@\tikzumlComponent@fitname\endcsname] (\tikzumlComponent@nodeName-body) {};% + \node[above, font=\tikzumlDefaultFont] (\tikzumlComponent@nodeName-captiontmp) at (\tikzumlComponent@nodeName-body.north) {\tikzumlComponent@caption};% + \node (\tikzumlComponent@nodeName-logotmp) at (\tikzumlComponent@nodeName-captiontmp.north -| \tikzumlComponent@nodeName-body.east) {\picturedcomponent{draw=\tikzumlComponentDrawColor, fill=\tikzumlComponentFillColor, font=\tikzumlDefaultFont} };% + \node[draw=\tikzumlComponentDrawColor, fill=\tikzumlComponentFillColor, name=\tikzumlComponent@nodeName, /tikzuml/component/style, fit=(\tikzumlComponent@nodeName-body) (\tikzumlComponent@nodeName-captiontmp)] {};% + \node[font=\tikzumlDefaultFont] (\tikzumlComponent@nodeName-caption) at (\tikzumlComponent@nodeName-captiontmp) {\tikzumlComponent@caption};% + \draw (\tikzumlComponent@nodeName-caption.north -| \tikzumlComponent@nodeName.east) node[font=\tikzumlDefaultFont, xshift=-1ex, below=-1ex, name=\tikzumlComponent@nodeName-logo] {\picturedcomponent{draw=\tikzumlComponentDrawColor, fill=\tikzumlComponentFillColor, font=\tikzumlDefaultFont} };% + \draw (\tikzumlComponent@nodeName-caption.south -| \tikzumlComponent@nodeName.north west) -- (\tikzumlComponent@nodeName-caption.south -| \tikzumlComponent@nodeName.north east);% + \coordinate (\tikzumlComponent@nodeName-west-port) at (\tikzumlComponent@nodeName.west); + \coordinate (\tikzumlComponent@nodeName-east-port) at (\tikzumlComponent@nodeName.east); + \coordinate (\tikzumlComponent@nodeName-south-port) at (\tikzumlComponent@nodeName.south); + \coordinate (\tikzumlComponent@nodeName-north-port) at (\tikzumlComponent@nodeName.north); + \end{pgfonlayer}% + \end{scope}% + % + % add to fit + \ifnum\c@tikzumlPackageLevel>0% + \edef\tikzumlPackageFitOld{\csname tikzumlPackageFit\tikzumlPackage@parent @@\tikzumlPackage@fitname\endcsname}% + \expandafter\xdef\csname tikzumlPackageFit\tikzumlPackage@parent @@\tikzumlPackage@fitname\endcsname{\tikzumlPackageFitOld (\tikzumlComponent@nodeName)}% + \stepcounter{tikzumlPackageClassNum}% + \fi% +}% +% +% shortcut for empty component +\newcommand{\umlbasiccomponent}[2][]{\begin{umlcomponent}[#1]{#2} \end{umlcomponent}}% +% +\newcommand{\umlrequiredinterface}[2][]{% + \def\tikzumlInterfaceWithPort{tikzumlFalse}% + \pgfkeys{/tikzuml/requiredinterfacerelation/.cd, interface/.initial={}, distance/.initial=\tikzumlRequiredInterfaceDefaultDistance,% + name/.initial=tikzumlEmpty, width/.initial=\tikzumlRequiredInterfaceDefaultWidth,% + padding/.initial=\tikzumlRequiredInterfaceDefaultPadding,% + draw/.initial=\tikzumlDefaultDrawColor, fill/.initial=\tikzumlComponentDefaultFillColor,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{with port}}{% + \def\tikzumlInterfaceWithPort{tikzumlTrue}% + }{}% + }% + }% + \pgfkeys{/tikzuml/requiredinterfacerelation/.cd, #1}% + \pgfkeys{/tikzuml/requiredinterfacerelation/.cd, interface/.get=\tikzumlRequiredInterfaceLabel,% + distance/.get=\tikzumlRequiredInterfaceDistance,% + name/.get=\tikzumlRequiredInterfaceName,% + width/.get=\tikzumlRequiredInterfaceWidth,% + padding/.get=\tikzumlRequiredInterfacePadding,% + draw/.get=\tikzumlRequiredInterfaceDrawColor,% + fill/.get=\tikzumlRequiredInterfaceFillColor% + }% + % + \ifthenelse{\equal{\tikzumlRequiredInterfaceName}{tikzumlEmpty}}{% + \edef\tikzumlRequiredInterface@interfacename{#2-east-interface}% + \edef\tikzumlRequiredInterface@portname{#2-east-port}% + \edef\tikzumlRequiredInterface@paddingname{#2-east-padding}% + }{% + \edef\tikzumlRequiredInterface@interfacename{\tikzumlRequiredInterfaceName}% + \edef\tikzumlRequiredInterface@portname{\tikzumlRequiredInterfaceName-port}% + \edef\tikzumlRequiredInterface@paddingname{\tikzumlRequiredInterfaceName-padding}% + }% + % + \edef\tikzumlRequiredInterface@name{#2}% + % + \begingroup% + \def\_{_}\edef\x{\endgroup% + \def\noexpand\tikzumlRequiredInterface@nodeName{\tikzumlRequiredInterface@name}}\x% + % + \begingroup% + \def\_{_}\edef\x{\endgroup% + \def\noexpand\tikzumlRequiredInterface@interfacenodeName{\tikzumlRequiredInterface@interfacename}}\x% + % + \begingroup% + \def\_{_}\edef\x{\endgroup% + \def\noexpand\tikzumlRequiredInterface@portnodeName{\tikzumlRequiredInterface@portname}}\x% + % + \begingroup% + \def\_{_}\edef\x{\endgroup% + \def\noexpand\tikzumlRequiredInterface@paddingnodeName{\tikzumlRequiredInterface@paddingname}}\x% + % + \ifthenelse{\equal{\tikzumlInterfaceWithPort}{tikzumlTrue}}{% + \node[inner sep=0.5*\tikzumlRequiredInterfaceWidth, rectangle, draw=\tikzumlRequiredInterfaceDrawColor, fill=\tikzumlRequiredInterfaceFillColor] (\tikzumlRequiredInterface@portnodeName) at (\tikzumlRequiredInterface@nodeName.east) {};% + }{% + \node[inner sep=0] (\tikzumlRequiredInterface@nodeName-east-port) at (\tikzumlRequiredInterface@nodeName.east) {};% + }% + \begin{scope}% + \draw (\tikzumlRequiredInterface@nodeName)+(\tikzumlRequiredInterfaceDistance,0) node[inner sep=0, text width=\tikzumlRequiredInterfaceWidth, circle, name=\tikzumlRequiredInterface@interfacenodeName-tmp] {};% + \clip (\tikzumlRequiredInterface@interfacenodeName-tmp.north) rectangle (\tikzumlRequiredInterface@interfacenodeName-tmp.south -| \tikzumlRequiredInterface@interfacenodeName-tmp.west);% + \node[inner sep=0, text width=\tikzumlRequiredInterfaceWidth, circle, draw=\tikzumlRequiredInterfaceDrawColor] (\tikzumlRequiredInterface@interfacenodeName) at (\tikzumlRequiredInterface@interfacenodeName-tmp) {};% + \end{scope}% + \node[above] at (\tikzumlRequiredInterface@interfacenodeName.north) {\tikzumlRequiredInterfaceLabel};% + % + \umlrelation[style={tikzuml connector style}, #1]{\tikzumlRequiredInterface@portnodeName}{\tikzumlRequiredInterface@interfacenodeName}% + % + \draw (\tikzumlRequiredInterface@interfacenodeName)+(\tikzumlRequiredInterfacePadding,0) node[name=\tikzumlRequiredInterface@paddingnodeName] {};% + % + % add to fit + \ifnum\c@tikzumlComponentLevel>0% + \def\tikzumlComponentFitTmp{\csname tikzumlComponentFit\tikzumlComponent@parent @@\tikzumlComponent@fitname\endcsname}% + \expandafter\xdef\csname tikzumlComponentFit\tikzumlComponent@parent @@\tikzumlComponent@fitname\endcsname{\tikzumlComponentFitTmp (\tikzumlRequiredInterface@paddingnodeName) (\tikzumlRequiredInterface@portnodeName) }% + \fi% +}% +% +\newcommand{\umlprovidedinterface}[2][]{% + \def\tikzumlInterfaceWithPort{tikzumlFalse}% + \pgfkeys{/tikzuml/providedinterfacerelation/.cd, interface/.initial={}, distance/.initial=\tikzumlProvidedInterfaceDefaultDistance,% + name/.initial=tikzumlEmpty, width/.initial=\tikzumlProvidedInterfaceDefaultWidth,% + padding/.initial=\tikzumlProvidedInterfaceDefaultPadding,% + draw/.initial=\tikzumlDefaultDrawColor, fill/.initial=\tikzumlComponentDefaultFillColor,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{with port}}{% + \def\tikzumlInterfaceWithPort{tikzumlTrue}% + }{}% + }% + }% + \pgfkeys{/tikzuml/providedinterfacerelation/.cd, #1}% + \pgfkeys{/tikzuml/providedinterfacerelation/.cd, interface/.get=\tikzumlProvidedInterfaceLabel,% + distance/.get=\tikzumlProvidedInterfaceDistance,% + name/.get=\tikzumlProvidedInterfaceName,% + width/.get=\tikzumlProvidedInterfaceWidth,% + padding/.get=\tikzumlProvidedInterfacePadding,% + draw/.get=\tikzumlProvidedInterfaceDrawColor,% + fill/.get=\tikzumlProvidedInterfaceFillColor% + }% + % + \ifthenelse{\equal{\tikzumlProvidedInterfaceName}{tikzumlEmpty}}{% + \edef\tikzumlProvidedInterface@interfacename{#2-west-interface}% + \edef\tikzumlProvidedInterface@portname{#2-west-port}% + \edef\tikzumlProvidedInterface@paddingname{#2-west-padding}% + }{% + \edef\tikzumlProvidedInterface@interfacename{\tikzumlProvidedInterfaceName}% + \edef\tikzumlProvidedInterface@portname{\tikzumlProvidedInterfaceName-port}% + \edef\tikzumlProvidedInterface@paddingname{\tikzumlProvidedInterfaceName-padding}% + }% + % + \edef\tikzumlProvidedInterface@name{#2}% + % + \begingroup% + \def\_{_}\edef\x{\endgroup% + \def\noexpand\tikzumlProvidedInterface@nodeName{\tikzumlProvidedInterface@name}}\x% + % + \begingroup% + \def\_{_}\edef\x{\endgroup% + \def\noexpand\tikzumlProvidedInterface@interfacenodeName{\tikzumlProvidedInterface@interfacename}}\x% + % + \begingroup% + \def\_{_}\edef\x{\endgroup% + \def\noexpand\tikzumlProvidedInterface@portnodeName{\tikzumlProvidedInterface@portname}}\x% + % + \begingroup% + \def\_{_}\edef\x{\endgroup% + \def\noexpand\tikzumlProvidedInterface@paddingnodeName{\tikzumlProvidedInterface@paddingname}}\x% + % + \ifthenelse{\equal{\tikzumlInterfaceWithPort}{tikzumlTrue}}{% + \node[inner sep=0.5*\tikzumlProvidedInterfaceWidth, rectangle, draw=\tikzumlProvidedInterfaceDrawColor, fill=\tikzumlProvidedInterfaceFillColor] (\tikzumlProvidedInterface@portnodeName) at (\tikzumlProvidedInterface@nodeName.west) {};% + }{% + \node[inner sep=0] (\tikzumlProvidedInterface@portnodeName) at (\tikzumlProvidedInterface@nodeName.west) {};% + }% + \draw (\tikzumlProvidedInterface@nodeName)+(-\tikzumlProvidedInterfaceDistance,0) node[inner sep=0, text width=\tikzumlProvidedInterfaceWidth, circle, draw=\tikzumlProvidedInterfaceDrawColor, fill=\tikzumlProvidedInterfaceFillColor, name=\tikzumlProvidedInterface@interfacenodeName] {};% + \node[above] at (\tikzumlProvidedInterface@interfacenodeName.north) + {\tikzumlProvidedInterfaceLabel};% + % + \umlrelation[style={tikzuml connector style}, #1]{\tikzumlProvidedInterface@portnodeName}{\tikzumlProvidedInterface@interfacenodeName}% + % + \draw (\tikzumlProvidedInterface@interfacenodeName)+(-\tikzumlProvidedInterfacePadding,0) node[name=\tikzumlProvidedInterface@paddingnodeName] {};% + % + % add to fit + \ifnum\thetikzumlComponentLevel>0% + \def\tikzumlComponentFitTmp{\csname tikzumlComponentFit\tikzumlComponent@parent @@\tikzumlComponent@fitname\endcsname}% + \expandafter\xdef\csname tikzumlComponentFit\tikzumlComponent@parent @@\tikzumlComponent@fitname\endcsname{\tikzumlComponentFitTmp (\tikzumlProvidedInterface@paddingnodeName) (\tikzumlProvidedInterface@portnodeName) }% + \fi% +}% +% +\newlength{\tikzuml@AC@xa}% +\newlength{\tikzuml@AC@ya}% +\newlength{\tikzuml@AC@xb}% +\newlength{\tikzuml@AC@yb}% +\newlength{\tikzuml@AC@xi}% +\newlength{\tikzuml@AC@yi}% +\newlength{\tikzuml@AC@xic}% +\newlength{\tikzuml@AC@yic}% +\newlength{\tikzuml@AC@xio}% +\newlength{\tikzuml@AC@yio}% +\newlength{\tikzuml@AC@AB}% +\newlength{\tikzuml@AC@lambda}% +\newlength{\tikzuml@AC@xtrc}% +\newlength{\tikzuml@AC@ytrc}% +\newlength{\tikzuml@AC@xtlc}% +\newlength{\tikzuml@AC@ytlc}% +\newlength{\tikzuml@AC@xblc}% +\newlength{\tikzuml@AC@yblc}% +\newlength{\tikzuml@AC@xbrc}% +\newlength{\tikzuml@AC@ybrc}% +\newlength{\tikzuml@AC@middleArm}% +% +\newcommand{\umlassemblyconnectorsymbol}[2]{% + \ifthenelse{\NOT\equal{\tikzumlAssemblyConnectorLabel}{}}{% + \edef\tikzuml@ACStart@name{#1}% + \edef\tikzuml@ACEnd@name{#2}% + \edef\tikzuml@AC@width{\tikzumlAssemblyConnectorWidth}% + % + \begingroup% + \def\_{_}\edef\x{\endgroup% + \def\noexpand\tikzuml@ACStart@nodeName{\tikzuml@ACStart@name}}\x% + % + \begingroup% + \def\_{_}\edef\x{\endgroup% + \def\noexpand\tikzuml@ACEnd@nodeName{\tikzuml@ACEnd@name}}\x% + % + \begingroup% + \def\_{_}\edef\x{\endgroup% + \def\noexpand\tikzuml@ACInterface@nodeName{\tikzumlAssemblyConnectorSymbolName}}\x% + % + \pgfextractx{\tikzuml@AC@xa}{\pgfpointanchor{\tikzuml@ACStart@nodeName}{\tikzumlAssemblyConnectorStartAnchor}}% + \pgfextracty{\tikzuml@AC@ya}{\pgfpointanchor{\tikzuml@ACStart@nodeName}{\tikzumlAssemblyConnectorStartAnchor}}% + \pgfextractx{\tikzuml@AC@xb}{\pgfpointanchor{\tikzuml@ACEnd@nodeName}{\tikzumlAssemblyConnectorEndAnchor}}% + \pgfextracty{\tikzuml@AC@yb}{\pgfpointanchor{\tikzuml@ACEnd@nodeName}{\tikzumlAssemblyConnectorEndAnchor}}% + \pgfmathsetlength{\tikzuml@AC@xi}{0.5*\tikzuml@AC@xa+0.5*\tikzuml@AC@xb}% + \pgfmathsetlength{\tikzuml@AC@yi}{0.5*\tikzuml@AC@ya+0.5*\tikzuml@AC@yb}% + \pgfmathsetlength{\tikzuml@AC@AB}{veclen(\tikzuml@AC@xa-\tikzuml@AC@xb,\tikzuml@AC@ya-\tikzuml@AC@yb)}% + \pgfmathsetlength{\tikzuml@AC@lambda}{0.25*\tikzuml@AC@width/\tikzuml@AC@AB}% + \pgfmathsetlength{\tikzuml@AC@xic}{\tikzuml@AC@xi-\tikzuml@AC@lambda*(\tikzuml@AC@xb-\tikzuml@AC@xa)}% + \pgfmathsetlength{\tikzuml@AC@yic}{\tikzuml@AC@yi-\tikzuml@AC@lambda*(\tikzuml@AC@yb-\tikzuml@AC@ya)}% + \pgfmathsetlength{\tikzuml@AC@xio}{\tikzuml@AC@xi+\tikzuml@AC@lambda*(\tikzuml@AC@xb-\tikzuml@AC@xa)}% + \pgfmathsetlength{\tikzuml@AC@yio}{\tikzuml@AC@yi+\tikzuml@AC@lambda*(\tikzuml@AC@yb-\tikzuml@AC@ya)}% + \node[inner sep=0.5*\tikzuml@AC@width] (\tikzuml@ACInterface@nodeName-interface) at (\tikzuml@AC@xi,\tikzuml@AC@yi) {};% + \node[inner sep=0, text width=\tikzuml@AC@width, circle, draw=\tikzumlAssemblyConnectorDrawColor, fill=\tikzumlAssemblyConnectorFillColor] (\tikzuml@ACInterface@nodeName-io) at (\tikzuml@AC@xio,\tikzuml@AC@yio) {};% + \begin{scope}% + \pgfmathsetlength{\tikzuml@AC@xtrc}{\tikzuml@AC@xic-2*\tikzuml@AC@lambda*(\tikzuml@AC@yb-\tikzuml@AC@ya)}% + \pgfmathsetlength{\tikzuml@AC@ytrc}{\tikzuml@AC@yic+2*\tikzuml@AC@lambda*(\tikzuml@AC@xb-\tikzuml@AC@xa)}% + \pgfmathsetlength{\tikzuml@AC@xbrc}{\tikzuml@AC@xic+2*\tikzuml@AC@lambda*(\tikzuml@AC@yb-\tikzuml@AC@ya)}% + \pgfmathsetlength{\tikzuml@AC@ybrc}{\tikzuml@AC@yic-2*\tikzuml@AC@lambda*(\tikzuml@AC@xb-\tikzuml@AC@xa)}% + \pgfmathsetlength{\tikzuml@AC@xtlc}{\tikzuml@AC@xic-3*\tikzuml@AC@lambda*(\tikzuml@AC@yb-\tikzuml@AC@ya+\tikzuml@AC@xb-\tikzuml@AC@xa)}% + \pgfmathsetlength{\tikzuml@AC@ytlc}{\tikzuml@AC@yic+3*\tikzuml@AC@lambda*(\tikzuml@AC@xb-\tikzuml@AC@xa+\tikzuml@AC@ya-\tikzuml@AC@yb)}% + \pgfmathsetlength{\tikzuml@AC@xblc}{\tikzuml@AC@xic+3*\tikzuml@AC@lambda*(\tikzuml@AC@yb-\tikzuml@AC@ya+\tikzuml@AC@xa-\tikzuml@AC@xb)}% + \pgfmathsetlength{\tikzuml@AC@yblc}{\tikzuml@AC@yic-3*\tikzuml@AC@lambda*(\tikzuml@AC@xb-\tikzuml@AC@xa+\tikzuml@AC@yb-\tikzuml@AC@ya)}% + \coordinate (\tikzuml@ACInterface@nodeName-trc) at (\tikzuml@AC@xtrc,\tikzuml@AC@ytrc);% + \coordinate (\tikzuml@ACInterface@nodeName-brc) at (\tikzuml@AC@xbrc,\tikzuml@AC@ybrc);% + \coordinate (\tikzuml@ACInterface@nodeName-tlc) at (\tikzuml@AC@xtlc,\tikzuml@AC@ytlc);% + \coordinate (\tikzuml@ACInterface@nodeName-blc) at (\tikzuml@AC@xblc,\tikzuml@AC@yblc);% + \clip (\tikzuml@ACInterface@nodeName-trc) -- (\tikzuml@ACInterface@nodeName-tlc) -- (\tikzuml@ACInterface@nodeName-blc) -- (\tikzuml@ACInterface@nodeName-brc) -- cycle;% + \node[inner sep=0, text width=\tikzuml@AC@width, circle, draw=\tikzumlAssemblyConnectorDrawColor, fill=\tikzumlAssemblyConnectorFillColor] (\tikzuml@ACInterface@nodeName-ic) at (\tikzuml@AC@xic,\tikzuml@AC@yic) {};% + \end{scope}% + \node[above, font=\tikzumlDefaultFont] at (\tikzuml@ACInterface@nodeName-interface.north) + {\tikzumlAssemblyConnectorLabel};% + }{}% +}% +% +\newcommand{\umlassemblyconnector}[3][]{% + \def\tikzumlAssemblyConnectorWithPort{tikzumlFalse}% + \def\tikzumlAssemblyConnectorFirstArm{tikzumlFalse}% + \def\tikzumlAssemblyConnectorSecondArm{tikzumlFalse}% + \def\tikzumlAssemblyConnectorMiddleArm{tikzumlFalse}% + \def\tikzumlAssemblyConnectorLastArm{tikzumlFalse}% + \pgfkeys{/tikzuml/assemblyconnectorrelation/.cd, geometry/.initial=--, interface/.initial={},% + arm1/.initial={auto}, arm2/.initial={auto},% + name/.initial=connector-\thetikzumlConnectorNum, width/.initial=1em,% + anchor1/.initial={}, anchor2/.initial={},% + draw/.initial=\tikzumlDefaultDrawColor,% + fill assembly connector/.initial=\tikzumlAssemblyConnectorDefaultFillColor,% + fill port/.initial=\tikzumlPortDefaultFillColor,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{with port}}{% + \def\tikzumlAssemblyConnectorWithPort{tikzumlTrue}% + }{% + \ifthenelse{\equal{\keyname}{first arm}}{% + \def\tikzumlAssemblyConnectorFirstArm{tikzumlTrue}% + }{% + \ifthenelse{\equal{\keyname}{second arm}}{% + \def\tikzumlAssemblyConnectorSecondArm{tikzumlTrue}% + }{% + \ifthenelse{\equal{\keyname}{middle arm}}{% + \def\tikzumlAssemblyConnectorMiddleArm{tikzumlTrue}% + }{% + \ifthenelse{\equal{\keyname}{last arm}}{% + \def\tikzumlAssemblyConnectorLastArm{tikzumlTrue}% + }{% + }% + }% + }% + }% + }% + }% + }% + \pgfkeys{/tikzuml/assemblyconnectorrelation/.cd, #1}% + \pgfkeys{/tikzuml/assemblyconnectorrelation/.cd, geometry/.get=\tikzumlAssemblyConnectorGeometry,% + name/.get=\tikzumlAssemblyConnectorName,% + interface/.get=\tikzumlAssemblyConnectorLabel,% + width/.get=\tikzumlAssemblyConnectorWidth,% + arm1/.get=\tikzumlAssemblyConnectorStartArm,% + arm2/.get=\tikzumlAssemblyConnectorEndArm,% + anchor1/.get=\tikzumlAssemblyConnectorStartAnchorTmp,% + anchor2/.get=\tikzumlAssemblyConnectorEndAnchorTmp,% + draw/.get=\tikzumlAssemblyConnectorDrawColor,% + fill assembly connector/.get=\tikzumlAssemblyConnectorFillColor,% + fill port/.get=\tikzumlAssemblyConnectorPortFillColor% + }% + % + \edef\tikzumlAssemblyConnectorStart@name{#2}% + \edef\tikzumlAssemblyConnectorEnd@name{#3}% + % + \begingroup% + \def\_{_}\edef\x{\endgroup% + \def\noexpand\tikzumlAssemblyConnectorStart@nodeName{\tikzumlAssemblyConnectorStart@name}}\x% + % + \begingroup% + \def\_{_}\edef\x{\endgroup% + \def\noexpand\tikzumlAssemblyConnectorEnd@nodeName{\tikzumlAssemblyConnectorEnd@name}}\x% + % + \begingroup% + \def\_{_}\edef\x{\endgroup% + \def\noexpand\tikzumlAssemblyConnectorLabel@nodeName{\tikzumlAssemblyConnectorLabel}}\x% + % + \pgfextractx{\tikzuml@AC@xa}{\pgfpointanchor{\tikzumlAssemblyConnectorStart@nodeName}{center}}% + \pgfextracty{\tikzuml@AC@ya}{\pgfpointanchor{\tikzumlAssemblyConnectorStart@nodeName}{center}}% + \pgfextractx{\tikzuml@AC@xb}{\pgfpointanchor{\tikzumlAssemblyConnectorEnd@nodeName}{center}}% + \pgfextracty{\tikzuml@AC@yb}{\pgfpointanchor{\tikzumlAssemblyConnectorEnd@nodeName}{center}}% + % + \ifthenelse{\equal{\tikzumlAssemblyConnectorGeometry}{--}}{% + \ifthenelse{\tikzuml@AC@xb>\tikzuml@AC@xa}{% + \def\tikzumlAssemblyConnectorStartAnchor{east}% + \def\tikzumlAssemblyConnectorEndAnchor{west}% + }{% + \def\tikzumlAssemblyConnectorStartAnchor{west}% + \def\tikzumlAssemblyConnectorEndAnchor{east}% + } + }{}% + % + \ifthenelse{\equal{\tikzumlAssemblyConnectorGeometry}{-|}}{% + \ifthenelse{\tikzuml@AC@xb>\tikzuml@AC@xa}{% + \def\tikzumlAssemblyConnectorStartAnchor{east}% + }{% + \def\tikzumlAssemblyConnectorStartAnchor{west}% + } + \ifthenelse{\tikzuml@AC@yb>\tikzuml@AC@ya}{% + \def\tikzumlAssemblyConnectorEndAnchor{south}% + }{% + \def\tikzumlAssemblyConnectorEndAnchor{north}% + } + }{}% + % + \ifthenelse{\equal{\tikzumlAssemblyConnectorGeometry}{|-}}{% + \ifthenelse{\tikzuml@AC@xb>\tikzuml@AC@xa}{% + \def\tikzumlAssemblyConnectorEndAnchor{west}% + }{% + \def\tikzumlAssemblyConnectorEndAnchor{east}% + } + \ifthenelse{\tikzuml@AC@yb>\tikzuml@AC@ya}{% + \def\tikzumlAssemblyConnectorStartAnchor{north}% + }{% + \def\tikzumlAssemblyConnectorStartAnchor{south}% + } + }{}% + % + \ifthenelse{\equal{\tikzumlAssemblyConnectorGeometry}{-|-}}{% + \ifthenelse{\equal{\tikzumlAssemblyConnectorStartArm}{auto}}{% + \ifthenelse{\equal{\tikzumlAssemblyConnectorEndArm}{auto}}{% + \pgfmathsetlength{\tikzuml@AC@middleArm}{0.5 * \tikzuml@AC@xa + 0.5 * \tikzuml@AC@xb}% + }{% + \pgfmathsetlength{\tikzuml@AC@middleArm}{\tikzuml@AC@xb+\tikzumlAssemblyConnectorEndArm}% + }% + }{% + \pgfmathsetlength{\tikzuml@AC@middleArm}{\tikzuml@AC@xa+\tikzumlAssemblyConnectorStartArm}% + }% + \pgfmathparse{\tikzuml@AC@middleArm>\tikzuml@AC@xa} + \pgfmathparse{\tikzuml@AC@middleArm>\tikzuml@AC@xb} + \ifthenelse{\lengthtest{\tikzuml@AC@middleArm>\tikzuml@AC@xa}}{% + \def\tikzumlAssemblyConnectorStartAnchor{east}% + }{% + \def\tikzumlAssemblyConnectorStartAnchor{west}% + } + \ifthenelse{\lengthtest{\tikzuml@AC@middleArm>\tikzuml@AC@xb}}{% + \def\tikzumlAssemblyConnectorEndAnchor{east}% + }{% + \def\tikzumlAssemblyConnectorEndAnchor{west}% + } + }{}% + % + \ifthenelse{\equal{\tikzumlAssemblyConnectorGeometry}{|-|}}{% + \ifthenelse{\equal{\tikzumlAssemblyConnectorStartArm}{auto}}{% + \ifthenelse{\equal{\tikzumlAssemblyConnectorEndArm}{auto}}{% + \pgfmathsetlength{\tikzuml@AC@middleArm}{0.5 * \tikzuml@AC@ya + 0.5 * \tikzuml@AC@yb}% + }{% + \pgfmathsetlength{\tikzuml@AC@middleArm}{\tikzuml@AC@yb+\tikzumlAssemblyConnectorEndArm}% + }% + }{% + \pgfmathsetlength{\tikzuml@AC@middleArm}{\tikzuml@AC@ya+\tikzumlAssemblyConnectorStartArm}% + }% + \ifthenelse{\tikzuml@AC@middleArm>\tikzuml@AC@ya}{% + \def\tikzumlAssemblyConnectorStartAnchor{north}% + }{% + \def\tikzumlAssemblyConnectorStartAnchor{south}% + } + \ifthenelse{\tikzuml@AC@middleArm>\tikzuml@AC@yb}{% + \def\tikzumlAssemblyConnectorEndAnchor{north}% + }{% + \def\tikzumlAssemblyConnectorEndAnchor{south}% + } + }{}% + % + \ifthenelse{\equal{\tikzumlAssemblyConnectorStartAnchorTmp}{}}{% + }{% + \def\tikzumlAssemblyConnectorStartAnchor{\tikzumlAssemblyConnectorStartAnchorTmp}% + }% + \ifthenelse{\equal{\tikzumlAssemblyConnectorEndAnchorTmp}{}}{% + }{% + \def\tikzumlAssemblyConnectorEndAnchor{\tikzumlAssemblyConnectorEndAnchorTmp}% + }% + % + \node[inner sep=0] (\tikzumlAssemblyConnectorStart@nodeName-\tikzumlAssemblyConnectorLabel@nodeName-port-tmp) at (\tikzumlAssemblyConnectorStart@nodeName.\tikzumlAssemblyConnectorStartAnchor) {};% + \node[inner sep=0] (\tikzumlAssemblyConnectorEnd@nodeName-\tikzumlAssemblyConnectorLabel@nodeName-port-tmp) at (\tikzumlAssemblyConnectorEnd@nodeName.\tikzumlAssemblyConnectorEndAnchor) {};% + % + \umlrelation[style={tikzuml connector style}, #1]{\tikzumlAssemblyConnectorStart@nodeName-\tikzumlAssemblyConnectorLabel@nodeName-port-tmp}{\tikzumlAssemblyConnectorEnd@nodeName-\tikzumlAssemblyConnectorLabel@nodeName-port-tmp}% + % + \ifthenelse{\equal{\tikzumlAssemblyConnectorWithPort}{tikzumlTrue}}{% + \node[inner sep=0.5*\tikzumlAssemblyConnectorWidth, rectangle, draw=\tikzumlAssemblyConnectorDrawColor, fill=\tikzumlAssemblyConnectorPortFillColor] (\tikzumlAssemblyConnectorStart@nodeName-\tikzumlAssemblyConnectorLabel@nodeName-port) at (\tikzumlAssemblyConnectorStart@nodeName-\tikzumlAssemblyConnectorLabel@nodeName-port-tmp) {};% + \node[inner sep=0.5*\tikzumlAssemblyConnectorWidth, rectangle, draw=\tikzumlAssemblyConnectorDrawColor, fill=\tikzumlAssemblyConnectorPortFillColor] (\tikzumlAssemblyConnectorEnd@nodeName-\tikzumlAssemblyConnectorLabel@nodeName-port) at (\tikzumlAssemblyConnectorEnd@nodeName-\tikzumlAssemblyConnectorLabel@nodeName-port-tmp) {};% + }{% + \node[inner sep=0] (\tikzumlAssemblyConnectorStart@nodeName-\tikzumlAssemblyConnectorLabel@nodeName-port) at (\tikzumlAssemblyConnectorStart@nodeName.\tikzumlAssemblyConnectorStartAnchor) {};% + \node[inner sep=0] (\tikzumlAssemblyConnectorEnd@nodeName-\tikzumlAssemblyConnectorLabel@nodeName-port) at (\tikzumlAssemblyConnectorEnd@nodeName.\tikzumlAssemblyConnectorEndAnchor) {};% + }% + % + \addtocounter{tikzumlRelationNum}{-1}% + \ifthenelse{\equal{\tikzumlAssemblyConnectorName}{connector-\thetikzumlConnectorNum}}{% + \edef\tikzumlAssemblyConnectorName{relation-\thetikzumlRelationNum}% + \edef\tikzumlAssemblyConnectorSymbolName{\tikzumlAssemblyConnectorLabel@nodeName}% + }{% + \edef\tikzumlAssemblyConnectorSymbolName{\tikzumlAssemblyConnectorName}% + }% + % + \stepcounter{tikzumlRelationNum}% + % + \ifthenelse{\equal{\tikzumlAssemblyConnectorGeometry}{--}}{% + \umlassemblyconnectorsymbol{\tikzumlAssemblyConnectorStart@nodeName}{\tikzumlAssemblyConnectorEnd@nodeName}% + }{% + \ifthenelse{\equal{\tikzumlAssemblyConnectorGeometry}{-|}}{% + \ifthenelse{\equal{\tikzumlAssemblyConnectorFirstArm}{tikzumlTrue}}{% + \umlassemblyconnectorsymbol{\tikzumlAssemblyConnectorStart@nodeName}{\tikzumlAssemblyConnectorName-2}% + }{% + \umlassemblyconnectorsymbol{\tikzumlAssemblyConnectorName-2}{\tikzumlAssemblyConnectorEnd@nodeName}% + }% + }{% + \ifthenelse{\equal{\tikzumlAssemblyConnectorGeometry}{|-}}{% + \ifthenelse{\equal{\tikzumlAssemblyConnectorFirstArm}{tikzumlTrue}}{% + \umlassemblyconnectorsymbol{\tikzumlAssemblyConnectorStart@nodeName}{\tikzumlAssemblyConnectorName-2}% + }{% + \umlassemblyconnectorsymbol{\tikzumlAssemblyConnectorName-2}{\tikzumlAssemblyConnectorEnd@nodeName}% + }% + }{% + \ifthenelse{\equal{\tikzumlAssemblyConnectorGeometry}{-|-}}{% + \ifthenelse{\equal{\tikzumlAssemblyConnectorFirstArm}{tikzumlTrue}}{% + \umlassemblyconnectorsymbol{\tikzumlAssemblyConnectorStart@nodeName}{\tikzumlAssemblyConnectorName-2}% + }{% + \ifthenelse{\equal{\tikzumlAssemblyConnectorLastArm}{tikzumlTrue}}{% + \umlassemblyconnectorsymbol{\tikzumlAssemblyConnectorName-4}{\tikzumlAssemblyConnectorEnd@nodeName}% + }{% + \umlassemblyconnectorsymbol{\tikzumlAssemblyConnectorName-2}{\tikzumlAssemblyConnectorName-4}% + }% + }% + }{% + \ifthenelse{\equal{\tikzumlAssemblyConnectorGeometry}{|-|}}{% + \ifthenelse{\equal{\tikzumlAssemblyConnectorFirstArm}{tikzumlTrue}}{% + \umlassemblyconnectorsymbol{\tikzumlAssemblyConnectorStart@nodeName}{\tikzumlAssemblyConnectorName-2}% + }{% + \ifthenelse{\equal{\tikzumlAssemblyConnectorLastArm}{tikzumlTrue}}{% + \umlassemblyconnectorsymbol{\tikzumlAssemblyConnectorName-4}{\tikzumlAssemblyConnectorEnd@nodeName}% + }{% + \umlassemblyconnectorsymbol{\tikzumlAssemblyConnectorName-2}{\tikzumlAssemblyConnectorName-4}% + }% + }% + }{}% + }% + }% + }% + }% + \stepcounter{tikzumlConnectorNum}% +}% +% +% shortcuts of \umlassemblyconnector +\newcommand{\umlHVassemblyconnector}[3][]{% + \pgfkeys{/tikzuml/HVassemblyconnector/.cd,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{geometry}}{% + \errmessage{TIKZUML ERROR : in umlHVassemblyconnector, forbidden option geometry}% + }{}% + }}% + \pgfkeys{/tikzuml/HVassemblyconnector/.cd, #1}% + \umlassemblyconnector[geometry=-|, #1]{#2}{#3}% +}% +% +\newcommand{\umlVHassemblyconnector}[3][]{% + \pgfkeys{/tikzuml/VHassemblyconnector/.cd,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{geometry}}{% + \errmessage{TIKZUML ERROR : in umlVHassemblyconnector, forbidden option geometry}% + }{}% + }% + }% + \pgfkeys{/tikzuml/VHassemblyconnector/.cd, #1}% + \umlassemblyconnector[geometry=|-, #1]{#2}{#3}% +}% +% +\newcommand{\umlHVHassemblyconnector}[3][]{% + \pgfkeys{/tikzuml/HVHassemblyconnector/.cd,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{geometry}}{% + \errmessage{TIKZUML ERROR : in umlHVHassemblyconnector, forbidden option geometry}% + }{}% + }}% + \pgfkeys{/tikzuml/HVHassemblyconnector/.cd, #1}% + \umlassemblyconnector[geometry=-|-, #1]{#2}{#3}% +}% +% +\newcommand{\umlVHVassemblyconnector}[3][]{% + \pgfkeys{/tikzuml/VHVassemblyconnector/.cd,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{geometry}}{% + \errmessage{TIKZUML ERROR : in umlVHVassemblyconnector, forbidden option geometry}% + }{}% + }}% + \pgfkeys{/tikzuml/VHVassemblyconnector/.cd, #1}% + \umlassemblyconnector[geometry=|-|, #1]{#2}{#3}% +}% +% +\newcommand{\umlport}[3][]{% + \pgfkeys{/tikzuml/port/.cd, draw/.initial=\tikzumlDefaultDrawColor, fill/.initial=\tikzumlPortDefaultFillColor,% + width/.initial=\tikzumlPortDefaultWidth,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \errmessage{TIKZUML ERROR : in umlport forbidden option \keyname}% + }% + }% + \pgfkeys{/tikzuml/port/.cd, #1}% + \pgfkeys{/tikzuml/port/.cd, width/.get=\tikzumlPortWidth,% + draw/.get=\tikzumlPortDrawColor, fill/.get=\tikzumlPortFillColor}% + \edef\tikzumlPort@name{#2}% + \edef\tikzumlPort@anchor{#3}% + \begingroup% + \def\_{_}\edef\x{\endgroup% + \def\noexpand\tikzumlPort@nodeName{\tikzumlPort@name}}\x% + % + \node[inner sep=0.5*\tikzumlPortWidth, rectangle, draw=\tikzumlPortDrawColor, fill=\tikzumlPortFillColor] (\tikzumlPort@nodeName-\tikzumlPort@anchor-port) at (\tikzumlPort@nodeName.\tikzumlPort@anchor) {};% +}% +% +\newcommand{\umldelegateconnector}[3][]{% + \def\tikzumlDelegateConnectorWithStartPort{tikzumlFalse}% + \def\tikzumlDelegateConnectorWithEndPort{tikzumlFalse}% + \pgfkeys{/tikzuml/delegateconnector/.cd, + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{stereo}}{% + \errmessage{TIKZUML ERROR : in umldelegateconnector, forbidden option stereo}% + }{}% + }}% + \pgfkeys{/tikzuml/delegateconnector/.cd, #1}% + \umlrelation[style={tikzuml connector style}, stereo=delegate, #1]{#2}{#3}% +}% +% +% shortcuts of \umldelegateconnector +\newcommand{\umlHVdelegateconnector}[3][]{% + \pgfkeys{/tikzuml/HVdelegateconnector/.cd,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{geometry}\OR\equal{\keyname}{stereo}}{% + \errmessage{TIKZUML ERROR : in umlHVdelegateconnector, forbidden option \keyname}% + }{}% + }}% + \pgfkeys{/tikzuml/HVdelegateconnector/.cd, #1}% + \umldelegateconnector[geometry=-|, #1]{#2}{#3}% +}% +% +\newcommand{\umlVHdelegateconnector}[3][]{% + \pgfkeys{/tikzuml/VHdelegateconnector/.cd,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{geometry}\OR\equal{\keyname}{stereo}}{% + \errmessage{TIKZUML ERROR : in umlVHdelegateconnector, forbidden option \keyname}% + }{}% + }}% + \pgfkeys{/tikzuml/VHdelegateconnector/.cd, #1}% + \umldelegateconnector[geometry=|-, #1]{#2}{#3}% +}% +% +\newcommand{\umlHVHdelegateconnector}[3][]{% + \pgfkeys{/tikzuml/HVHdelegateconnector/.cd,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{geometry}\OR\equal{\keyname}{stereo}}{% + \errmessage{TIKZUML ERROR : in umlHVHdelegateconnector, forbidden option \keyname}% + }{}% + }}% + \pgfkeys{/tikzuml/HVHdelegateconnector/.cd, #1}% + \umldelegateconnector[geometry=-|-, #1]{#2}{#3}% +}% +% +\newcommand{\umlVHVdelegateconnector}[3][]{% + \pgfkeys{/tikzuml/VHVdelegateconnector/.cd,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{geometry}\OR\equal{\keyname}{stereo}}{% + \errmessage{TIKZUML ERROR : in umlVHVdelegateconnector, forbidden option \keyname}% + }{}% + }}% + \pgfkeys{/tikzuml/VHVdelegateconnector/.cd, #1}% + \umldelegateconnector[geometry=|-|, #1]{#2}{#3}% +}% +%%% End of tikz-uml.sty +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \ No newline at end of file diff --git a/10-entwurfsheft/.gitignore b/10-entwurfsheft/.gitignore new file mode 100644 index 0000000..87ec682 --- /dev/null +++ b/10-entwurfsheft/.gitignore @@ -0,0 +1,302 @@ +## Core latex/pdflatex auxiliary files: +*.aux +*.lof +*.log +*.lot +*.fls +*.out +*.toc +*.fmt +*.fot +*.cb +*.cb2 +.*.lb + +## Intermediate documents: +*.dvi +*.xdv +*-converted-to.* +# these rules might exclude image files for figures etc. +# *.ps +# *.eps +*.pdf +!assets/*.pdf + +## Generated if empty string is given at "Please type another file name for output:" +.pdf + +## Bibliography auxiliary files (bibtex/biblatex/biber): +*.bbl +*.bcf +*.blg +*-blx.aux +*-blx.bib +*.run.xml + +## Build tool auxiliary files: +*.fdb_latexmk +*.synctex +*.synctex(busy) +*.synctex.gz +*.synctex.gz(busy) +*.pdfsync + +## Build tool directories for auxiliary files +# latexrun +latex.out/ + +## Auxiliary and intermediate files from other packages: +# algorithms +*.alg +*.loa + +# achemso +acs-*.bib + +# amsthm +*.thm + +# beamer +*.nav +*.pre +*.snm +*.vrb + +# changes +*.soc + +# comment +*.cut + +# cprotect +*.cpt + +# elsarticle (documentclass of Elsevier journals) +*.spl + +# endnotes +*.ent + +# fixme +*.lox + +# feynmf/feynmp +*.mf +*.mp +*.t[1-9] +*.t[1-9][0-9] +*.tfm + +#(r)(e)ledmac/(r)(e)ledpar +*.end +*.?end +*.[1-9] +*.[1-9][0-9] +*.[1-9][0-9][0-9] +*.[1-9]R +*.[1-9][0-9]R +*.[1-9][0-9][0-9]R +*.eledsec[1-9] +*.eledsec[1-9]R +*.eledsec[1-9][0-9] +*.eledsec[1-9][0-9]R +*.eledsec[1-9][0-9][0-9] +*.eledsec[1-9][0-9][0-9]R + +# glossaries +*.acn +*.acr +*.glg +*.glo +*.gls +*.glsdefs +*.lzo +*.lzs +*.slg +*.slo +*.sls + +# uncomment this for glossaries-extra (will ignore makeindex's style files!) +*.ist + +# gnuplot +*.gnuplot +*.table + +# gnuplottex +*-gnuplottex-* + +# gregoriotex +*.gaux +*.glog +*.gtex + +# htlatex +*.4ct +*.4tc +*.idv +*.lg +*.trc +*.xref + +# hyperref +*.brf + +# knitr +*-concordance.tex +# TODO Uncomment the next line if you use knitr and want to ignore its generated tikz files +# *.tikz +*-tikzDictionary + +# listings +*.lol + +# luatexja-ruby +*.ltjruby + +# makeidx +*.idx +*.ilg +*.ind + +# minitoc +*.maf +*.mlf +*.mlt +*.mtc[0-9]* +*.slf[0-9]* +*.slt[0-9]* +*.stc[0-9]* + +# minted +_minted* +*.pyg + +# morewrites +*.mw + +# newpax +*.newpax + +# nomencl +*.nlg +*.nlo +*.nls + +# pax +*.pax + +# pdfpcnotes +*.pdfpc + +# sagetex +*.sagetex.sage +*.sagetex.py +*.sagetex.scmd + +# scrwfile +*.wrt + +# svg +svg-inkscape/ + +# sympy +*.sout +*.sympy +sympy-plots-for-*.tex/ + +# pdfcomment +*.upa +*.upb + +# pythontex +*.pytxcode +pythontex-files-*/ + +# tcolorbox +*.listing + +# thmtools +*.loe + +# TikZ & PGF +*.dpth +*.md5 +*.auxlock + +# titletoc +*.ptc + +# todonotes +*.tdo + +# vhistory +*.hst +*.ver + +# easy-todo +*.lod + +# xcolor +*.xcp + +# xmpincl +*.xmpi + +# xindy +*.xdy + +# xypic precompiled matrices and outlines +*.xyc +*.xyd + +# endfloat +*.ttt +*.fff + +# Latexian +TSWLatexianTemp* + +## Editors: +# WinEdt +*.bak +*.sav + +# Texpad +.texpadtmp + +# LyX +*.lyx~ + +# Kile +*.backup + +# gummi +.*.swp + +# KBibTeX +*~[0-9]* + +# TeXnicCenter +*.tps + +# auto folder when using emacs and auctex +./auto/* +*.el + +# expex forward references with \gathertags +*-tags.tex + +# standalone packages +*.sta + +# Makeindex log files +*.lpz + +# xwatermark package +*.xwm + +# REVTeX puts footnotes in the bibliography by default, unless the nofootinbib +# option is specified. Footnotes are the stored in a file with suffix Notes.bib. +# Uncomment the next line to have this generated file ignored. +#*Notes.bib diff --git a/10-entwurfsheft/.gitlab-ci.yml b/10-entwurfsheft/.gitlab-ci.yml new file mode 100644 index 0000000..27d0617 --- /dev/null +++ b/10-entwurfsheft/.gitlab-ci.yml @@ -0,0 +1,36 @@ +plantuml: + stage: .pre + image: + name: plantuml/plantuml + entrypoint: [""] + script: + - java -jar plantuml.jar -tpdf assets/diagrams/*.puml + artifacts: + paths: + - assets + +tex: + stage: build + image: texlive/texlive + script: + - mkdir public + - make tex + - mv *.pdf public + artifacts: + paths: + - public + dependencies: + - plantuml + +pages: + stage: deploy + script: + - echo Hello, World! + artifacts: + paths: + - public + rules: + - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH + dependencies: + - tex + diff --git a/10-entwurfsheft/.latexmkrc b/10-entwurfsheft/.latexmkrc new file mode 100644 index 0000000..bec2b38 --- /dev/null +++ b/10-entwurfsheft/.latexmkrc @@ -0,0 +1,7 @@ +# https://tex.stackexchange.com/questions/1226/how-to-make-latexmk-use-makeglossaries +add_cus_dep('glo', 'gls', 0, 'makeglo2gls'); +add_cus_dep('acn', 'acr', 0, 'makeglo2gls'); +sub makeglo2gls { + system("makeglossaries $_[0]"); +} + diff --git a/10-entwurfsheft/Makefile b/10-entwurfsheft/Makefile new file mode 100644 index 0000000..75a951a --- /dev/null +++ b/10-entwurfsheft/Makefile @@ -0,0 +1,18 @@ +MAIN = entwurfsheft +FLAGS = -pdf + +all: clean compile +compile: diagram tex +clean: clean-diagram clean-tex + +dev: + latexmk $(FLAGS) -pvc $(MAIN) +tex: + latexmk $(FLAGS) $(MAIN) +diagram: + java -jar plantuml.jar -tpdf assets/diagrams/*.puml +clean-tex: + latexmk -C +clean-diagram: + find assets/diagrams -type f -not -name '*.puml' -delete + diff --git a/10-entwurfsheft/README.md b/10-entwurfsheft/README.md new file mode 100644 index 0000000..2c08f29 --- /dev/null +++ b/10-entwurfsheft/README.md @@ -0,0 +1,31 @@ +# Entwurfsheft + +> Systemdesign und -spezifikation + +## Diagramme + +Installiere [PlantUML](https://plantuml.com/starting) (oder über einen Paketmanager). + +Arbeiten an Diagrammen mit Echtzeit-Vorschau (Anzeige wird beim Speichern der +puml-Datei aktualisiert): +```sh +java -jar plantuml.jar -gui assets/classdiagram.puml + +# bzw (wenn zu PATH hinzugefügt oder Linux) +plantuml -gui assets/classdiagram.puml +``` + +Bauen der Diagramme: +```sh +java -jar plantuml.jar -teps assets/*.puml + +# bzw (wenn zu PATH hinzugefügt oder Linux) +plantuml -teps assets/*.puml + +# bzw über Makefile (Linux) +make diagram + +# oder zum Erstellen von Diagrammen und LaTeX: +make +``` + diff --git a/10-entwurfsheft/assets/.gitignore b/10-entwurfsheft/assets/.gitignore new file mode 100644 index 0000000..16252a4 --- /dev/null +++ b/10-entwurfsheft/assets/.gitignore @@ -0,0 +1,3 @@ +diagrams/* +!diagrams/*.puml + diff --git a/10-entwurfsheft/assets/KIT_Deckblatt.pdf b/10-entwurfsheft/assets/KIT_Deckblatt.pdf new file mode 100644 index 0000000..7de8ed4 Binary files /dev/null and b/10-entwurfsheft/assets/KIT_Deckblatt.pdf differ diff --git a/10-entwurfsheft/assets/diagrams/backendComponentDiagram.puml b/10-entwurfsheft/assets/diagrams/backendComponentDiagram.puml new file mode 100644 index 0000000..806522c --- /dev/null +++ b/10-entwurfsheft/assets/diagrams/backendComponentDiagram.puml @@ -0,0 +1,61 @@ +@startuml +' skinparam linetype ortho + +'######################################################################### +'SubscriptionsAPI +component SubscriptionsAPI { + + component SubscriptionService + component SubscriptionController + component SubscriptionDataAccessLayer + + portout "Webserver" as wSub + portin "Database" as dSub + } + +dSub --0)- SubscriptionDataAccessLayer +SubscriptionDataAccessLayer --0)- SubscriptionService +SubscriptionService --0)- SubscriptionController +SubscriptionController --0)- wSub + +'######################################################################### + + +'######################################################################### +'EpisodeActionsAPI + +component EpisodeActionsAPI { + component EpisodeActionService + component EpisodeActionController + component EpisodeActionDataAccessLayer + + portout "Webserver" as wEpisode + portin "Database" as dEpisode +} + +dEpisode --0)- EpisodeActionDataAccessLayer +EpisodeActionController --0)- wEpisode +EpisodeActionDataAccessLayer --0)- EpisodeActionService +EpisodeActionService --0)- EpisodeActionController + +'######################################################################### + + +'######################################################################### +'AuthenticationAPI + +component AuthenticationAPI { + component AuthenticationService + component AuthenticationController + component AuthenticationDataAccessLayer + + portout "Webserver" as wAuth + portin "Database" as dAuth +} + +dAuth --0)- AuthenticationDataAccessLayer +AuthenticationController --0)- wAuth +AuthenticationDataAccessLayer --0)- AuthenticationService +AuthenticationService --0)- AuthenticationController + +@enduml diff --git a/10-entwurfsheft/assets/diagrams/classdiagram.puml b/10-entwurfsheft/assets/diagrams/classdiagram.puml new file mode 100644 index 0000000..4b1970a --- /dev/null +++ b/10-entwurfsheft/assets/diagrams/classdiagram.puml @@ -0,0 +1,463 @@ +@startuml +' skinparam linetype ortho +' skinparam groupInheritance 2 +allowmixing + +package subscriptionsAPI <> { + package subscriptionDataAccessLayer <> { + class SubscriptionDataAccessService <<@Repository>> { + <> SubscriptionDataAccessService(JpaTemplate jpaTemplate) + int uploadSubscriptions(String username, List subscriptions) + List getSubscriptions(String username) + List getSubscriptionsSince(String username, LocalDateTime time) + int addSubscriptions(String username, List addedSubscriptions) + int removeSubscriptions(String username, List removedSubscriptions) + List getTitles(String username) + } + + interface SubscriptionDao { + int uploadSubscriptions(String username, List subscriptions) + List getSubscriptions(String username) + List getSubscriptionsSince(String username, LocalDateTime time) + int addSubscriptions(String username, List addedSubscriptions) + int removeSubscriptions(String username, List removedSubscriptions) + List getTitles(String username) + } + } + + package subscriptionService <> { + class SubscriptionService <<@Service>> { + <> SubscriptionService(SubscriptionDao subscriptionDao) + int uploadSubscriptions(String username, List subscriptions) + List getSubscriptions(String username) + List getSubscriptionsSince(String username, LocalDateTime time) + int addSubscriptions(String username, List addedSubscriptions) + int removeSubscriptions(String username, List removedSubscriptions) + List getTitles(String username) + } + } + + package subscriptionController <> { + class SubscriptionController <<@Controller>>{ + ' @Autowired + <> SubscriptionController(SubscriptionService subscriptionService) + ' @GetMapping + ResponseEntity> getSubscriptions(String username, String deviceID, String functionJSONP) + ' @PutMapping + ResponseEntity uploadSubscriptions(String username, String deviceID, List subscriptions) + ' @PostMapping + ResponseEntity applySubscriptionDelta(String username, String deviceID, SubscriptionDelta delta) + ' @GetMapping + ResponseEntity getSubscriptionDelta(String username, String deviceID, long since) + ResponseEntity> getTitles(String username, String deviceID) + } + + class SubscriptionTitles { + <> SubscriptionTitles(Subscription subscription, List episodeTitles) + Subscription getSubscription() + List getEpisodesTitles() + } + + class SubscriptionDelta { + <> SubscriptionDelta(List add, List remove) + List getRemove() + LocalDate getTimestamp() + List> getUpdate_urls() + } + } + +} + +package episodeActionsAPI <> { + package episodeActionDataAccessLayer <> { + class EpisodeActionDataAccessService <<@Repository>> { + <> EpisodeActionDataAccessService (JpaTemplate jpaTemplate) + long addEpisodeActions(String username, List episodeActionPosts) + List getEpisodeActions(String username) + List getEpisodeActionsOfPodcast(String username, String podcastURL) + List getEpisodeActionsSince(String username, LocalDateTime since) + List getEpisodeActionsOfPodcastSince(String username, String podcastURL, LocalDateTime since) + } + + interface EpisodeActionDao { + long addEpisodeActions(String username, List episodeActionPosts) + List getEpisodeActions(String username) + List getEpisodeActionsOfPodcast(String username, String podcastURL) + List getEpisodeActionsSince(String username, LocalDateTime since) + List getEpisodeActionsOfPodcastSince(String username, String podcastURL, LocalDateTime since) + } + } + + package episodeActionService <> { + class EpisodeActionService <<@Service>> { + <> EpisodeActionService (EpisodeActionDao episodeActionDao) + LocalDateTime addEpisodeActions(String username, List episodeActionPosts) + List getEpisodeActions(String username) + List getEpisodeActionsOfPodcast(String username, String podcastURL) + List getEpisodeActionsSince(String username, LocalDateTime since) + List getEpisodeActionsOfPodcastSince(String username, String podcastURL, LocalDateTime since) + } + } + + package episodeActionController <> { + class EpisodeActionController <<@Controller>>{ + <> EpisodeActionController (EpisodeActionService episodeActionService) + ResponseEntity addEpisodeActions(String username, EpisodeActionPostRequest episodeActionPostRequest) + ResponseEntity getEpisodeActions(String username, String deviceID, boolean aggregated) + ResponseEntity getEpisodeActionsOfPodcast(String username, String podcastURL, String deviceID, boolean aggregated) + ResponseEntity getEpisodeActionsSince(String username, String deviceID, long since, boolean aggregated) + ResponseEntity getEpisodeActionsOfPodcastSince(String username, String podcastURL, String deviceID, long since, boolean aggregated) + } + + class EpisodeActionPostResponse { + <> EpisodeActionPostResponse(List> updateURLs) + long getTimestamp() + List> getUpdatedURLs() + } + + class EpisodeActionPost { + <> EpisodeActionPost(String podcastURL, String episodeURL, Action action, LocalDateTime timestamp, int started, int position) + String getPodcastURL() + String getEpisodeURL() + int getGUID() + Action getAction() + LocalDateTime getTimestamp() + int getStarted() + int getPosition() + EpisodeAction getEpisodeAction() + } + + class EpisodeActionPostRequest { + <> EpisodeActionPostRequest(List episodeActionPosts) + List getEpisodeActionPosts() + } + + class EpisodeActionGetResponse { + <> EpisodeActionGetResponse(List episodeActionPosts) + List getEpisodeActionPosts() + long getTimestamp() + } + } +} + +package authenticationAPI <> { + package authenticationDataAccessLayer <> { + ' interface AuthenticationDao { + ' String login(String username) + ' int logout(String username) + ' } + + ' class AuthenticationDataAccessService <<@Respository>> { + ' <> AuthenticationDataAccessService(JpaTemplate jpaTemplate) + ' String login(String username) + ' int logout(String username) + ' } + + interface UserDetailsManager { + void createUser(UserDetails userDetails) + void changePassword(String oldPassword, String newPassword) + void deleteUser(String username) + void updateUser(UserDetails user) + boolean userExists(String username) + } + note left + Aus org.springframework.security.provisioning + - liefert Methoden zum Erstellen neuer User + und zum Aktualisieren bestehender. + end note + + class JdbcUserDetailsManager <<@Repository>> { + <> JdbcUserDetailsManager(DataSource dataSource) + void createUser(UserDetails user) + void changePassword(String oldPassword, String newPassword) + void deleteUser(String username) + void updateUser(UserDetails user) + boolean userExists(String username) + } + note right + User Management Service aus dem Paket + org.springframework.security.provisioning + der CRUD Operationen für User bereitstellt. + Hier sind nur die relevanten Methoden modelliert. + end note + } + + package authenticationService <> { + class AuthenticationService <<@Service>> { + -- + <> AuthenticationService(UserDetailsManager userDetailsManager) + List verifyLogin(String username) + int logout(String username) + int forgotPassword(ForgotPasswordRequest forgotPasswordRequest) + .. via JdbcUserDetailsManager .. + int resetPassword(String username, RequestWithPassword requestWithPassword) + int registerUser(UserDetails user) + int changePassword(String username, ChangePasswordRequest changePasswordRequest) + int deleteUser(String username, RequestWithPassword requestWithPassword) + } + + class JavaMailSenderImpl {} + note left + Aus org.springframework.mail.javamail. + Implementierung des JavaMailSender Interfaces, + welches das MailSender Interface durch Unterstützung + von MIME Nachrichten erweitert. + Das MailSender Interface definiert dabei eine + Strategie zum Versenden einfacher Mails. + Unterstützt sowohl JavaMail MimeMessages und + Spring SimpleMailMessages. + end note + } + + package authenticationController <> { + class AuthenticationController <<@Controller>> { + <> AuthenticationController(AuthenticationService authenticationService) + ResponseEntity> verifyLogin(String username) + ResponseEntity logout(String username) + ResponseEntity forgotPassword(ForgotPasswordRequest forgotPasswordRequest) + ResponseEntity resetPassword(String username, RequestWithPassword requestWithPassword) + ResponseEntity registerUser(UserDetails user) + ResponseEntity changePassword(String username, ChangePasswordRequest changePasswordRequest) + ResponseEntity deleteUser(String username, RequestWithPassword requestWithPassword) + } + + class ChangePasswordRequest { + <> ChangePasswordRequest(String oldPassword, String newPassword) + String getOldPassword() + String getNewPassword() + } + + class ForgotPasswordRequest { + <> ForgotPasswordRequest(String email) + String getEmail() + } + + class RequestWithPassword { + <> ResetPasswordRequest(String password) + String getPassword() + } + } +} + +package model <> { + class Subscription { + <> Subscription(String url, String title) + int getID() + String getURL() + long getLastActionTimestamp() + String getTitle() + } + + class SubscriptionAction { + <> SubscriptionAction(int userID, int subscriptionID) + int getID() + int getUserID() + int getSubscriptionID() + long getTimestamp() + boolean getAdded() + } + + class Episode { + <> Episode(int subscriptionID, int id, String url, String title, String thumbnailURL, int total) + int getSubscriptionID() + int getID() + int getGUID() + String getURL() + String getTitle() + int getTotal() + } + + enum Action { + Download + Play + Delete + New + Flattr + String getJsonProperty() + } + + class EpisodeAction { + <> EpisodeAction(Action action, LocalDateTime timestamp, int started, int position) + int getEpisodeID() + Action getAction() + long getTimestamp() + int getStarted() + int getPosition() + void setEpisodeID() + EpisodeActionPost getEpisodeActionPost(String podcastURL, String episodeURL) + } + + interface UserDetails { + String getUsername() + String getPassword() + Collection getAuthorities() + boolean isAccountExpired() + boolean isAccountLocked() + boolean isCredentialsNonExpired() + boolean isEnabled() + } + note left + Aus org.springframework.security.core.userdetails. + Wird für die Schnittstelle UserDetailsManager benötigt. + Stellt wichtige Informationen eines Users bereit. + Diese werden nur indirekt von Spring Security + benutzt, indem sie vorher in Authentication Objekten + gekapselt werden. + end note + + class User { + -- + <> User(String username, String password) + int getID() + String getSessionToken() + boolean getEmailIsValidated() + .. interface methods .. + String getUsername() + String getPassword() + Collection getAuthorities() + boolean isAccountExpired() + boolean isAccountLocked() + boolean isCredentialsNonExpired() + boolean isEnabled() + } + + interface GrantedAuthority { + String getAuthority() + } + note right + Aus org.springframework.security.core. + Wird für die Schnittstelle UserDetails benötigt. + Repräsentiert eine Autorisierung, die einem + Authentication Objekt gewährt wird. + end note + + class Authority { + <> Authority() + String getAuthority() + } +} + +package util <> { + class RSSParser { + <> RSSParser(String subscriptionURL) + String getSubscriptionTitle() + List getEpisodes() + Episode getEpisodeForURL(String episodeURL) + } + note bottom + Verwendet intern Spring um + HTTP-Anfragen zu erstellen. + end note + + class CleanCronJob { + <> CleanCronJob(JdbcUserDetailsManager jdbcUserDetailsManager) + void cleanInvalidUsers() + } + note bottom + Hintergrundservice, der in periodischen Abständen + Nutzer, die ihre E-Mail-Adresse nicht nach 24 Stunden + bestätigt haben, wieder aus der Datenbank löscht. + (Auf die Assoziation zu JdbcUserDetailsManager wird + im Sinne der Übersichtlichkeit verzichtet.) + end note + + class ResponseEntity { + <> ResponseEntity(T body, HttpStatusCode status) + T getBody() + HttpStatusCode getStatusCode() + } + note bottom + Aus org.springframework.http. + Erweitert die Klasse HttpEntity, welche + ein HTTP Anfrage- oder Antwort-Objekt + repräsentiert, durch einen HttpStatusCode. + Wird von den Controller-Methoden als + Rückgabewert verwendet. + end note +} + +class SecurityConfigurationBasicAuth { + <> SecurityConfigurationBasicAuth() + PasswordEncoder encoder() + UserDetailsManager userDetailsService() + SecuryFilterChain fiterChain(HTTPSecurity http) throws Excpetion +} +note top + Erstellt einen Servlet Filter (springSecurityFilterChain) + der für die gesamte Sicherheit zuständig ist (Schutz der URLs, + Validierung von Anmeldedaten, Weiterleitung zur Anmeldung, etc.). +end note + +class PSEApplication { + <> PSEApplication() + void main(String[] args) +} + +database Datenbank +Datenbank <-[hidden]d- subscriptionsAPI +Datenbank <-[hidden]d- episodeActionsAPI +Datenbank <-[hidden]d- authenticationAPI +() SQL as SQLSub +() SQL as SQLAuth +() SQL as SQLEpisode + +Datenbank -- SQLSub +Datenbank -- SQLAuth +Datenbank -- SQLEpisode + +SubscriptionController ..o PSEApplication +AuthenticationController ..o PSEApplication +EpisodeActionController ..o PSEApplication +SecurityConfigurationBasicAuth ..o PSEApplication + +PSEApplication --() HTTP + +SQLSub )-- SubscriptionDataAccessService: JPA +' SQLAuth )-- AuthenticationDataAccessService: JPA +SQLAuth )-- JdbcUserDetailsManager: JDBC +SQLEpisode )-- EpisodeActionDataAccessService: JPA + +Subscription <. SubscriptionAction: ID +' Subscription <.. SubscriptionDataAccessService: DB +' SubscriptionAction <.. SubscriptionDataAccessService: DB +SubscriptionService --o SubscriptionController +SubscriptionDao <.. SubscriptionService: <> +Subscription --o SubscriptionTitles +EpisodeActionPost -o SubscriptionTitles +SubscriptionDao <|. SubscriptionDataAccessService: <> + +' User <.. AuthenticationDataAccessService: DB +' User <.. JdbcUserDetailsManager: DB +UserDetailsManager <.. AuthenticationService: <> +' AuthenticationDao <.. AuthenticationService: <> +AuthenticationService --o AuthenticationController +' AuthenticationDao <|. AuthenticationDataAccessService: <> +UserDetailsManager <|. JdbcUserDetailsManager: <> +UserDetailsManager <.. SecurityConfigurationBasicAuth: <> +UserDetails <|.. User: <> +User -> Authority +GrantedAuthority <|.. Authority: <> +JavaMailSenderImpl <. AuthenticationService: <> + +Action <-- EpisodeAction +EpisodeActionPost -o EpisodeActionGetResponse +EpisodeActionPost -o EpisodeActionPostRequest +EpisodeAction .> Episode: ID +' EpisodeAction <.. EpisodeActionDataAccessService: DB +' Episode <.. EpisodeActionDataAccessService: DB +EpisodeActionDao <.. EpisodeActionService: <> +EpisodeActionService --o EpisodeActionController +EpisodeActionDao <|. EpisodeActionDataAccessService: <> + +RSSParser <. SubscriptionDataAccessService: <> +RSSParser <. EpisodeActionDataAccessService: <> +' JdbcUserDetailsManager <-- CleanCronJob + +model .o Datenbank: ORM (User, SubscriptionAction, Subscription, EpisodeAction, Episode) +' Datenbank o.. Subscription: ORM +' Datenbank o.. SubscriptionAction: ORM +' Datenbank o.. Episode: ORM +' Datenbank o.. EpisodeAction: ORM +' Datenbank o.. User: ORM + +@enduml diff --git a/10-entwurfsheft/assets/diagrams/componentdiagram.puml b/10-entwurfsheft/assets/diagrams/componentdiagram.puml new file mode 100644 index 0000000..dea4a1d --- /dev/null +++ b/10-entwurfsheft/assets/diagrams/componentdiagram.puml @@ -0,0 +1,53 @@ +@startuml + +[App] as app +[VueRouter] as router +[NavbarComponent] as navbar +[LoginPage] as login_page +[SubscriptionsPage] as abo_page +[EpisodesPage] as episodes_page +[SettingsPage] as settings_page +[ForgotPasswordPage] as forgot_page +[ResetPasswordPage] as reset_page +note top + Wird in der E-Mail zum Zurücksetzen des Passworts mit dem JWT-Token verlinkt. + Sendet das alte und neue Passwort und den JWT an die API. +end note +[RegistrationPage] as registration_page + +[SubscriptionComponent] as sub +[EpisodeComponent] as episode +[LastUpdateComponent] as last_update +[PasswordValidatorComponent] as password + +app --> router + +app --> navbar +router --> login_page +router --> forgot_page +router --> reset_page +router --> registration_page +router --> abo_page +router --> episodes_page +router --> settings_page + +navbar -[hidden] router + +episodes_page -[hidden] abo_page +login_page -[hidden] forgot_page +registration_page -[hidden] reset_page +abo_page -[hidden] settings_page +forgot_page -[hidden] episodes_page +' forgot_page -[hidden] settings_page + +abo_page --> sub +episodes_page --> episode + +sub --> last_update +episode --> last_update + +settings_page --> password +reset_page --> password +registration_page --> password + +@enduml diff --git a/10-entwurfsheft/assets/diagrams/db.puml b/10-entwurfsheft/assets/diagrams/db.puml new file mode 100644 index 0000000..bdefaea --- /dev/null +++ b/10-entwurfsheft/assets/diagrams/db.puml @@ -0,0 +1,78 @@ +@startuml +' Type Symbol +' Zero or One |o-- +' Exactly One ||-- +' Zero or Many }o-- +' One or Many }|-- + +skinparam linetype ortho + +entity User { + * int id <> + * String email + * String password + * boolean verified + * long created_at +} + +entity SubscriptionAction { + * int id <> + * int user_id + * long timestamp + * int subscription_id + * boolean added +} + +entity Subscription { + * int id <> + * String url + * long timestamp + * String title +} + +entity Episode { + * int id <> + * int guid <> + * String url + * String title + * int total + * int subscription_id +} +note right + Wenn der Client eine GUID aus dem Feed mitsendet, wird + diese statt der URL verwendet um die Episode zu finden. + So wird die Episode auch noch gefunden, nachdem sich + die URL geändert hat. +end note +note bottom of Episode + Wenn für die Episoden-URL einer EpisodeAction noch keine Episode in der Datenbank steht, + dann schreibe dafür ein Dummy-Objekt in Datenbank und lade asynchron die Episoden der Subscription. + Ersetze dann die Dummy-Objekte durch die Episoden und setze den Timestamp der Subscription auf + die aktuelle Zeit. + Um DoS-Angriffe auf den Backend-Server abzuwenden, können die Episoden einer Subscription erst + nach einer Stunde erneut gefetched werden. Bis dahin werden für EpisodeActions, die sich auf noch + nicht geladene Episoden beziehen, nur Dummy-Objekte für die Episoden in die Datenbank geschrieben. + Es sei noch darauf hingewiesen, dass diese Dummy-Episoden bei Anfragen nicht mit ausgegeben werden. +end note + +entity EpisodeAction { + * int id <> + * int user_id + * int episode_id + * long timestamp + * int action + * int started + * int position +} +note right + Speichere für jede Episode + nur letzte Play-Action. +endnote + +User ||--o{ EpisodeAction +User ||--o{ SubscriptionAction +SubscriptionAction }|--|| Subscription +EpisodeAction }|--|| Episode +Subscription ||-right-|{ Episode + +@enduml diff --git a/10-entwurfsheft/assets/diagrams/deployment.puml b/10-entwurfsheft/assets/diagrams/deployment.puml new file mode 100644 index 0000000..26918e2 --- /dev/null +++ b/10-entwurfsheft/assets/diagrams/deployment.puml @@ -0,0 +1,59 @@ +@startuml + +node "<> \nBackend Server" as backendServer{ + database " <> \n MariaDB Server 10.6" as database { + rectangle rectangle1 [ + <> + User + ] + rectangle rectangle2 [ + <> + SubscriptionAction + ] + rectangle rectangle3 [ + <> + EpisodeAction + ] + rectangle rectangle4 [ + <> + Subscription + ] + rectangle rectangle5 [ + <> + Episode + ] + } + + node "<> \nJava Spring" as javaSpring{ + node " <> \n Tomcat Webserver" + } +} + +node "<> \nFrontend" as frontendServer { + +} + +node "<> \nEndgerät" as terminal { + node "<> \nBrowser" as browser + node "<> \nPodcatcher" as podcatcher +} + +backendServer "1" - "*" podcatcher + +node "<> \nFrontend Server" as frontendServer{ + node "<> \nVue.js" as vuejs { + + } +} + +podcatcher -[hidden] browser + +backendServer - "1" frontendServer + +database "1" -- "1" javaSpring + +browser "*" -- frontendServer + + + +@enduml diff --git a/10-entwurfsheft/assets/diagrams/sequencediagram-forgotAndResetPW.puml b/10-entwurfsheft/assets/diagrams/sequencediagram-forgotAndResetPW.puml new file mode 100644 index 0000000..603130c --- /dev/null +++ b/10-entwurfsheft/assets/diagrams/sequencediagram-forgotAndResetPW.puml @@ -0,0 +1,41 @@ +@startuml + +skinparam ParticipantPadding 30 + +participant AuthenticationController << (C, #ADD1B2) @Controller >> +-> AuthenticationController: ""POST /api/2/auth/forgot.json"" \n//@RequestBody ForgotPasswordRequest forgotPasswordRequest// \n\n-> forgotPassword(//forgotPasswordRequest//) +activate AuthenticationController +participant AuthenticationService << (C, #ADD1B2) @Service >> +AuthenticationController -> AuthenticationService: forgotPassword(//forgotPasswordRequest//) +activate AuthenticationService +participant JavaMailSenderImpl << (C, #ADD1B2) >> +AuthenticationService -> JavaMailSenderImpl: create link to reset password with JWT as URL parameter \n-> send(SimpleMailMessage simpleMessage) with link +activate JavaMailSenderImpl +<<- JavaMailSenderImpl: sends email with link containing a JWT to reset password +JavaMailSenderImpl --> AuthenticationService +deactivate JavaMailSenderImpl +AuthenticationService --> AuthenticationController: int indicating status +deactivate AuthenticationService +<-- AuthenticationController: ResponseEntity indicating status \n\n-> ""HTTP status code"" +deactivate AuthenticationController +||60|| +-> AuthenticationController: ""PUT /api/2/auth/{username}/resetpassword.json"" \n//@RequestParam String jwt// \n//@RequestBody ResetPasswordRequest resetPasswordRequest// \n\n-> login user (""username"") via JWT (//jwt//) \n-> resetPassword(""username"", //resetPasswordRequest//) +activate AuthenticationController +AuthenticationController -> AuthenticationService: resetPassword(""username"", //resetPasswordRequest//) +activate AuthenticationService +participant JdbcUserDetailsManager << (C, #ADD1B2) @Repository >> +AuthenticationService -> JdbcUserDetailsManager: String oldPassword = //resetPasswordRequest//.getOldPassword() \nString newPassword = //resetPasswordRequest//.getNewPassword() \n-> changePassword(newPassword, oldPassword) +activate JdbcUserDetailsManager +database Database +JdbcUserDetailsManager -> Database: change password of logged in user +activate Database +Database --> JdbcUserDetailsManager +deactivate Database +JdbcUserDetailsManager --> AuthenticationService: int indicating status +deactivate JdbcUserDetailsManager +AuthenticationService --> AuthenticationController: int indicating status +deactivate AuthenticationService +<-- AuthenticationController: ResponseEntity indicating status \n\n-> ""HTTP status code"" +deactivate AuthenticationController + +@enduml \ No newline at end of file diff --git a/10-entwurfsheft/assets/diagrams/sequencediagram-getEpisodeActions.puml b/10-entwurfsheft/assets/diagrams/sequencediagram-getEpisodeActions.puml new file mode 100644 index 0000000..47497d5 --- /dev/null +++ b/10-entwurfsheft/assets/diagrams/sequencediagram-getEpisodeActions.puml @@ -0,0 +1,38 @@ +@startuml + +' title =**Get All Episode Actions** + +participant EpisodeActionController << (C, #ADD1B2) @Controller >> +-> EpisodeActionController: ""GET /api/2/episodes/{username}.json"" \n//@RequestParam("device") String deviceID// \n//@RequestParam("aggregated") boolean aggregated// \n\n-> getEpisodeActions(""username"", //deviceID//, //aggregated//) +note right + Die Parameter //deviceID// und //aggregated// werden ignoriert, + da nicht zwischen Geräten unterschieden und für jede + Episode sowieso nur die letzte Play-Action gespeichert + wird. Dies gilt für alle GET-Anfragen der Episode Actions API. +end note +activate EpisodeActionController +participant EpisodeActionService << (C, #ADD1B2) @Service >> +EpisodeActionController -> EpisodeActionService: getEpisodeActions(""username"") +activate EpisodeActionService +participant EpisodeActionDataAccessService << (C, #ADD1B2) @Repository >> +EpisodeActionService -> EpisodeActionDataAccessService: getEpisodeActions(""username"") +activate EpisodeActionDataAccessService +EpisodeActionDataAccessService -> EpisodeActionDataAccessService: getEpisodeActionsSince(""username"", \nLocalDateTime.MIN.toEpochSecond(ZoneOffset.UTC)) +database Database +activate EpisodeActionDataAccessService +EpisodeActionDataAccessService -> Database: get all EpisodeActions for all subscribed podcasts +activate Database +Database --> EpisodeActionDataAccessService: List selectedEpisodeActions \n-> then remove all older than LocalDateTime.MIN (none) +EpisodeActionDataAccessService -> Database: join EpisodeActions in selectedEpisodeActions with episodeURL of Episode +Database --> EpisodeActionDataAccessService +deactivate Database +EpisodeActionDataAccessService --> EpisodeActionDataAccessService: List episodeActionPosts +deactivate EpisodeActionDataAccessService +EpisodeActionDataAccessService --> EpisodeActionService: List episodeActionPosts +deactivate EpisodeActionDataAccessService +EpisodeActionService --> EpisodeActionController: List episodeActionPosts +deactivate EpisodeActionService +<-- EpisodeActionController: ResponseEntity response \n\n-> ""HTTP status code"" \n-> ""JSON"" +deactivate EpisodeActionController + +@enduml \ No newline at end of file diff --git a/10-entwurfsheft/assets/diagrams/sequencediagram-getEpisodeActionsOfPodcastSince.puml b/10-entwurfsheft/assets/diagrams/sequencediagram-getEpisodeActionsOfPodcastSince.puml new file mode 100644 index 0000000..d8797d1 --- /dev/null +++ b/10-entwurfsheft/assets/diagrams/sequencediagram-getEpisodeActionsOfPodcastSince.puml @@ -0,0 +1,32 @@ +@startuml + +' title =**Get Episode Actions of Podcast Since** + +participant EpisodeActionController << (C, #ADD1B2) @Controller >> +-> EpisodeActionController: ""GET /api/2/episodes/{username}.json"" \n//@RequestParam("podcast") String podcastURL// \n//@RequestParam("device") String deviceID// \n//@RequestParam("since") long since// \n//@RequestParam("aggregated") boolean aggregated// \n\n-> getEpisodeActionsOfPodcastSince(""username"", //podcastURL//, //deviceID//, //since//, //aggregated//) +note right + Die Parameter //deviceID// und //aggregated// werden ignoriert. + Siehe Notiz in Sequenzdiagramm **Get All Episode Actions**. +end note +activate EpisodeActionController +participant EpisodeActionService << (C, #ADD1B2) @Service >> +EpisodeActionController -> EpisodeActionService: getEpisodeActionsOfPodcastSince(""username"", //podcastURL//, //since//) +activate EpisodeActionService +participant EpisodeActionDataAccessService << (C, #ADD1B2) @Repository >> +EpisodeActionService -> EpisodeActionDataAccessService: getEpisodeActionsOfPodcastSince(""username"", //podcastURL//, //since//) +activate EpisodeActionDataAccessService +database Database +EpisodeActionDataAccessService -> Database: get all EpisodeActions the given podcast (//podcastURL//) +activate Database +Database --> EpisodeActionDataAccessService: List selectedEpisodeActions \n-> then remove all older than //since// +EpisodeActionDataAccessService -> Database: join EpisodeActions in selectedEpisodeActions with episodeURL of Episode +Database --> EpisodeActionDataAccessService +deactivate Database +EpisodeActionDataAccessService --> EpisodeActionService: List episodeActionPosts +deactivate EpisodeActionDataAccessService +EpisodeActionService --> EpisodeActionController: List episodeActionPosts +deactivate EpisodeActionService +<-- EpisodeActionController: ResponseEntity response \n\n-> ""HTTP status code"" \n-> ""JSON"" +deactivate EpisodeActionController + +@enduml \ No newline at end of file diff --git a/10-entwurfsheft/assets/diagrams/sequencediagram-getSubscriptions.puml b/10-entwurfsheft/assets/diagrams/sequencediagram-getSubscriptions.puml new file mode 100644 index 0000000..4d8ab90 --- /dev/null +++ b/10-entwurfsheft/assets/diagrams/sequencediagram-getSubscriptions.puml @@ -0,0 +1,38 @@ +@startuml + +' title =**Get All Subscriptions** + +participant SubscriptionController << (C, #ADD1B2) @Controller >> +-> SubscriptionController: ""GET /subscriptions/{username}.json"" \n"" /subscriptions/{username}/{deviceid}.json"" \n//@RequestParam("jsonp") String functionJSONP// \n\n-> getSubscriptions(""username"", ""deviceid"", //functionJSONP//) +activate SubscriptionController +note right + Die Parameter ""deviceid"" und + //functionJSONP// werden ignoriert, + da nicht zwischen Geräten unterschieden + und JSONP nicht unterstützt wird. +end note +participant SubscriptionService << (C, #ADD1B2) @Service >> +SubscriptionController -> SubscriptionService: getSubscriptions(""username"") +activate SubscriptionService +participant SubscriptionDataAccessService << (C, #ADD1B2) @Repository >> +SubscriptionService -> SubscriptionDataAccessService: getSubscriptions(""username"") +activate SubscriptionDataAccessService +SubscriptionDataAccessService -> SubscriptionDataAccessService: getSubscriptionsSince(""username"", LocalDateTime.MIN) +database Database +activate SubscriptionDataAccessService +SubscriptionDataAccessService -> Database: get all Subscriptions for ""username"" +activate Database +Database --> SubscriptionDataAccessService: List subscriptions +SubscriptionDataAccessService -> Database: get Podcasts from Subscriptions +Database --> SubscriptionDataAccessService: List subscribedPodcasts +deactivate Database +SubscriptionDataAccessService --> SubscriptionDataAccessService: List podcastURLs +deactivate SubscriptionDataAccessService +SubscriptionDataAccessService --> SubscriptionService: List podcastURLs +deactivate SubscriptionDataAccessService +SubscriptionService --> SubscriptionController: List podcastURLs +deactivate SubscriptionService +<-- SubscriptionController: ResponseEntity> podcastURLs \n \n-> ""HTTP status code"" \n-> ""JSON"" +deactivate SubscriptionController + +@enduml \ No newline at end of file diff --git a/10-entwurfsheft/assets/diagrams/sequencediagram-register.puml b/10-entwurfsheft/assets/diagrams/sequencediagram-register.puml new file mode 100644 index 0000000..b7b7aa1 --- /dev/null +++ b/10-entwurfsheft/assets/diagrams/sequencediagram-register.puml @@ -0,0 +1,26 @@ +@startuml + +' title =**Register** + +participant AuthenticationController << (C, #ADD1B2) @Controller >> +-> AuthenticationController: ""POST /api/2/auth/register.json"" \n//@RequestBody UserDetails user// \n\n-> registerUser(//user//) +activate AuthenticationController +participant AuthenticationService << (C, #ADD1B2) @Service >> +AuthenticationController -> AuthenticationService: registerUser(//user//) +activate AuthenticationService +participant JdbcUserDetailsManager << (C, #ADD1B2) @Repository >> +AuthenticationService -> JdbcUserDetailsManager: createUser(//user//) +activate JdbcUserDetailsManager +database Database +JdbcUserDetailsManager -> Database: create new User with given UserDetails (//user//) +activate Database +Database --> JdbcUserDetailsManager +deactivate Database +JdbcUserDetailsManager --> AuthenticationService: int indicating status +deactivate JdbcUserDetailsManager +AuthenticationService --> AuthenticationController: int indicating status +deactivate AuthenticationService +<-- AuthenticationController: ResponseEntity indicating status \n\n-> ""HTTP status code"" +deactivate AuthenticationController + +@enduml \ No newline at end of file diff --git a/10-entwurfsheft/assets/diagrams/sequencediagram-uploadEpisodeActions.puml b/10-entwurfsheft/assets/diagrams/sequencediagram-uploadEpisodeActions.puml new file mode 100644 index 0000000..d3dac57 --- /dev/null +++ b/10-entwurfsheft/assets/diagrams/sequencediagram-uploadEpisodeActions.puml @@ -0,0 +1,38 @@ +@startuml + +' title =**Upload Episode Actions** + +participant EpisodeActionController << (C, #ADD1B2) @Controller >> +-> EpisodeActionController: ""POST /api/2/episodes/{username}.json"" \n//@RequestBody EpisodeActionPostRequest episodeActionPostRequest// \n\n-> addEpisodeActions(""username"", //episodeActionPostRequest//) +activate EpisodeActionController +participant EpisodeActionService << (C, #ADD1B2) @Service >> +EpisodeActionController -> EpisodeActionService: addEpisodeActions(""username"", \nepisodeActionPosts = //episodeActionPostRequest//.getEpisodeActionPosts()) +activate EpisodeActionService +participant EpisodeActionDataAccessService << (C, #ADD1B2) @Repository >> +EpisodeActionService -> EpisodeActionDataAccessService: addEpisodeActions(""username"", episodeActionPosts) +database Database +activate EpisodeActionDataAccessService +loop for each EpisodeActionPost in episodeActionPosts -> episodeAction = episodeActionPost.getEpisodeAction() +opt episodeAction.getAction().equals(Action.PLAY) +EpisodeActionDataAccessService -> Database: set episodeID field of episodeAction for this ""username"" via podcastURL and episodeURL +activate Database +Database --> EpisodeActionDataAccessService +EpisodeActionDataAccessService -> Database: get last EpisodeAction with this episodeID if present +Database --> EpisodeActionDataAccessService: Optional lastEpisodeAction +opt lastEpisodeAction.isPresent() +EpisodeActionDataAccessService -> Database: replace lastEpisodeAction with episodeAction +else else +EpisodeActionDataAccessService -> Database: add episodeAction to DB as new entry +end +Database --> EpisodeActionDataAccessService +deactivate Database +end +end +EpisodeActionDataAccessService --> EpisodeActionService: long latestTimestamp +deactivate EpisodeActionDataAccessService +EpisodeActionService --> EpisodeActionController: LocalDateTime timestamp = LocalDateTime.ofEpochSecond(latestTimestamp, 0, ZoneOffset.UTC) +deactivate EpisodeActionService +<-- EpisodeActionController: ResponseEntity \n(with empty list for updateURLs) \n\n-> ""HTTP status code"" \n-> ""JSON"" +deactivate EpisodeActionController + +@enduml \ No newline at end of file diff --git a/10-entwurfsheft/assets/diagrams/sequencediagram-uploadSubscriptions.puml b/10-entwurfsheft/assets/diagrams/sequencediagram-uploadSubscriptions.puml new file mode 100644 index 0000000..1edc8cf --- /dev/null +++ b/10-entwurfsheft/assets/diagrams/sequencediagram-uploadSubscriptions.puml @@ -0,0 +1,32 @@ +@startuml + +' title =**Upload Subscriptions** + +participant SubscriptionController << (C, #ADD1B2) @Controller >> +-> SubscriptionController: ""PUT /subscriptions/{username}/{deviceid}.json"" \n//@RequestBody List subscriptions// \n\n-> uploadSubscriptions(""username"", ""deviceid"", //subscriptions//) +activate SubscriptionController +participant SubscriptionService << (C, #ADD1B2) @Service >> +SubscriptionController -> SubscriptionService: uploadSubscriptions(""username"", //subscriptions//) +activate SubscriptionService +participant SubscriptionDataAccessService << (C, #ADD1B2) @Repository >> +SubscriptionService -> SubscriptionDataAccessService: uploadSubscriptions(""username"", //subscriptions//) +activate SubscriptionDataAccessService +database Database +SubscriptionDataAccessService -> Database: delete all subsciptions of ""username"" +activate Database +Database --> SubscriptionDataAccessService +SubscriptionDataAccessService -> SubscriptionDataAccessService: addSubscriptions(""username"", //subscriptions//) +activate SubscriptionDataAccessService +SubscriptionDataAccessService -> Database: upload all subscriptions (//subscriptions//) for ""username"" +Database --> SubscriptionDataAccessService +deactivate Database +SubscriptionDataAccessService --> SubscriptionDataAccessService: int indicating status +deactivate SubscriptionDataAccessService +SubscriptionDataAccessService --> SubscriptionService: int indicating status +deactivate SubscriptionDataAccessService +SubscriptionService --> SubscriptionController: int indicating status +deactivate SubscriptionService +<-- SubscriptionController: ResponseEntity with empty String for success \n\n-> ""HTTP status code"" \n-> ""JSON"" +deactivate SubscriptionController + +@enduml \ No newline at end of file diff --git a/10-entwurfsheft/assets/episode.png b/10-entwurfsheft/assets/episode.png new file mode 100644 index 0000000..c0db4a2 Binary files /dev/null and b/10-entwurfsheft/assets/episode.png differ diff --git a/10-entwurfsheft/assets/help.png b/10-entwurfsheft/assets/help.png new file mode 100644 index 0000000..39a1b84 Binary files /dev/null and b/10-entwurfsheft/assets/help.png differ diff --git a/10-entwurfsheft/assets/lastupdate.png b/10-entwurfsheft/assets/lastupdate.png new file mode 100644 index 0000000..e9b7f5c Binary files /dev/null and b/10-entwurfsheft/assets/lastupdate.png differ diff --git a/10-entwurfsheft/assets/logo.pdf b/10-entwurfsheft/assets/logo.pdf new file mode 100644 index 0000000..91fd334 Binary files /dev/null and b/10-entwurfsheft/assets/logo.pdf differ diff --git a/10-entwurfsheft/assets/logo.svg b/10-entwurfsheft/assets/logo.svg new file mode 100644 index 0000000..1609066 --- /dev/null +++ b/10-entwurfsheft/assets/logo.svg @@ -0,0 +1,211 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Podcast Synchronisation made Efficient + + + + diff --git a/10-entwurfsheft/assets/navbar.png b/10-entwurfsheft/assets/navbar.png new file mode 100644 index 0000000..dd9f8e8 Binary files /dev/null and b/10-entwurfsheft/assets/navbar.png differ diff --git a/10-entwurfsheft/assets/password-margin.png b/10-entwurfsheft/assets/password-margin.png new file mode 100644 index 0000000..d9d4fa3 Binary files /dev/null and b/10-entwurfsheft/assets/password-margin.png differ diff --git a/10-entwurfsheft/assets/password.png b/10-entwurfsheft/assets/password.png new file mode 100644 index 0000000..68248a0 Binary files /dev/null and b/10-entwurfsheft/assets/password.png differ diff --git a/10-entwurfsheft/assets/subscription.png b/10-entwurfsheft/assets/subscription.png new file mode 100644 index 0000000..58a84f9 Binary files /dev/null and b/10-entwurfsheft/assets/subscription.png differ diff --git a/10-entwurfsheft/entwurfsheft.tex b/10-entwurfsheft/entwurfsheft.tex new file mode 100644 index 0000000..9f93eda --- /dev/null +++ b/10-entwurfsheft/entwurfsheft.tex @@ -0,0 +1,90 @@ +% \documentclass[a4paper, UTF8, 12pt]{article} +% \documentclass[a4paper, UTF8, 12pt]{scrbook} +\documentclass[parskip=half, a4paper, 12pt]{scrartcl} + +\usepackage[german]{babel} +\usepackage[dvipsnames]{xcolor} +% \usepackage{tikz} +% \usetikzlibrary{positioning} +% \usetikzlibrary{calc} +% \usetikzlibrary{arrows} +% \usetikzlibrary{intersections} +% \usepackage{tikz-uml} +% \usepackage{pgf-umlsd} +% \usepgflibrary{arrows} % for pgf-umlsd +% \tikzumlset{fill usecase=white} +\usepackage[margin=2.5cm]{geometry} +\usepackage{csquotes} +\usepackage[T1]{fontenc} +\usepackage{amsmath} +\usepackage{pdflscape} +\usepackage{graphicx} +\usepackage{caption} +\usepackage{subcaption} +\usepackage{float} +\usepackage{enumitem} +% \usepackage{textpos} +\usepackage{hyperref} +\usepackage{fancyhdr} +% \usepackage{multicol} +\usepackage{rest-api} +\usepackage{wrapfig} +\usepackage{textcomp} +\usepackage{ulem} + + +\hypersetup{ +% ‘texdoc hyperref‘ for options + pdftitle={ + PSE\textsuperscript{2} + - Podcast Synchronisation made Efficient Pflichtenheft + }, + bookmarks=true, +} +\usepackage{csquotes} +\usepackage[toc]{glossaries} +\usepackage{lastpage} + +\include{sections/glossar} + +\title{Pflichtenheft} +\author{KIT Students et al} +\date{2.12.2022} + +\pagestyle{fancy} +\setkomafont{pageheadfoot}{\footnotesize\scshape} +\fancyhead{} % clear all header fields +% \fancyhead[L]{Pflichtenheft} +\fancyhead[L]{PSE\textsuperscript{2} - Podcast Synchronisation made Efficient} +% \fancyhead[R]{2.12.2022} +\fancyfoot{} % clear all footer fields +\fancyfoot[R]{\thepage{} / \pageref{LastPage}} +\fancyfoot[L]{Praxis der Softwareentwicklung} +\fancyfoot[C]{} + +\begin{document} + +\include{titlepage} +\setcounter{page}{1} + +\tableofcontents + +\include{sections/einleitung} +\newpage + +\input{sections/aufbau} + +\include{sections/structure} + +\include{sections/backend} + +\include{sections/frontend} + +\include{sections/apidoc} + +\include{sections/changes} + +\printglossaries +% \glsaddall + +\end{document} diff --git a/10-entwurfsheft/pgf-umlsd.sty b/10-entwurfsheft/pgf-umlsd.sty new file mode 100644 index 0000000..99847db --- /dev/null +++ b/10-entwurfsheft/pgf-umlsd.sty @@ -0,0 +1,329 @@ +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% Start of pgf-umlsd.sty +% +% Some macros for UML Sequence Diagrams. +% Home page of project: http://pgf-umlsd.googlecode.com/ +% Author: Xu Yuan , Southeast University, China +% Contributor: Nobel Huang , Southeast University, China +% +% History: +% v0.7 2012/03/05 +% - unify interface of call and callself +% - non-instantaneous message +% - bugfix: conflits with tikz library backgrounds +% v0.6 2011/07/27 +% - Fix Issue 6 reported by frankmorgner@gmail.com +% - diagram without a thread +% - allows empty diagram +% - New manual +% v0.5 2009/09/30 Fix Issue 2 reported by vlado.handziski +% - Nested callself is supported +% - Rename sdloop and sdframe to sdblock +% v0.4 2008/12/08 Fix Issue 1 reported by MathStuf: +% Nested sdloop environment hides outer loop +% v0.3 2008/11/10 in Berlin, fix for the PGF cvs version: +% - the list items in \foreach are not evaluated by default now, +% the `evaluate' opinion should be used +% v0.2 2008/03/20 create project at http://pgf-umlsd.googlecode.com/ +% - use `shadows' library +% Thanks for Dr. Ludger Humbert's feedback! +% - reduce the parameter numbers, the user can write the content +% of instance (such as no colon) +% - the user can redefine the `inststyle' +% - new option: switch underlining of the instance text +% - new option: switch rounded corners +% v0.1 2008/01/25 first release at http://www.fauskes.net/pgftikzexamples/ +% + +\NeedsTeXFormat{LaTeX2e}[1999/12/01] +\ProvidesPackage{pgf-umlsd}[2011/07/27 v0.6 Some LaTeX macros for UML +Sequence Diagrams.] + +\RequirePackage{tikz} +\usetikzlibrary{arrows,shadows} + +\RequirePackage{ifthen} + +% Options +% ? the instance name under line ? +\newif\ifpgfumlsdunderline\pgfumlsdunderlinetrue +\DeclareOption{underline}{\pgfumlsdunderlinetrue} +\DeclareOption{underline=true}{\pgfumlsdunderlinetrue} +\DeclareOption{underline=false}{\pgfumlsdunderlinefalse} +% ? the instance box with rounded corners ? +\newif\ifpgfumlsdroundedcorners\pgfumlsdroundedcornersfalse +\DeclareOption{roundedcorners}{\pgfumlsdroundedcornerstrue} +\DeclareOption{roundedcorners=true}{\pgfumlsdroundedcornerstrue} +\DeclareOption{roundedcorners=false}{\pgfumlsdroundedcornersfalse} +\ProcessOptions + +% new counters +\newcounter{preinst} +\newcounter{instnum} +\newcounter{threadnum} +\newcounter{seqlevel} % level +\newcounter{callevel} +\newcounter{callselflevel} +\newcounter{blocklevel} + +% new an instance +% Example: +% \newinst[edge distance]{var}{name:class} +\newcommand{\newinst}[3][0.2]{ + \stepcounter{instnum} + \path (inst\thepreinst.east)+(#1,0) node[inststyle] (inst\theinstnum) + {\ifpgfumlsdunderline + \underline{#3} + \else + #3 + \fi}; + \path (inst\theinstnum)+(0,-0.5*\unitfactor) node (#2) {}; + \tikzstyle{instcolor#2}=[] + \stepcounter{preinst} +} + +% new an instance thread +% Example: +% \newinst[color]{var}{name}{class} +\newcommand{\newthread}[3][gray!30]{ + \newinst{#2}{#3} + \stepcounter{threadnum} + \node[below of=inst\theinstnum,node distance=0.8cm] (thread\thethreadnum) {}; + \tikzstyle{threadcolor\thethreadnum}=[fill=#1] + \tikzstyle{instcolor#2}=[fill=#1] +} + +% draw running (thick) line, should not call directly +\newcommand*{\drawthread}[2]{ + \begin{pgfonlayer}{umlsd@threadlayer} + \draw[threadstyle] (#1.west) -- (#1.east) -- (#2.east) -- (#2.west) -- cycle; + \end{pgfonlayer} +} + +% a function call +% Example: +% \begin{call}[height]{caller}{function}{callee}{return} +% \end{call} +\newenvironment{call}[5][1]{ +\ifthenelse{\equal{#2}{#4}} +{ + \begin{callself}[#1]{#2}{#3}{#5} +} +{ + \begin{callanother}[#1]{#2}{#3}{#4}{#5} +} +} +{ +\ifthenelse{\equal{\f\thecallevel}{\t\thecallevel}} +{ + \end{callself} +} +{ + \end{callanother} +} +} + +% function call to another instance +% interal use only +\newenvironment*{callanother}[5][1]{ + \stepcounter{seqlevel} + \stepcounter{callevel} % push + \path + (#2)+(0,-\theseqlevel*\unitfactor-0.7*\unitfactor) node (cf\thecallevel) {} + (#4.\threadbias)+(0,-\theseqlevel*\unitfactor-0.7*\unitfactor) node (ct\thecallevel) {}; + + \draw[->,>=triangle 60] ({cf\thecallevel}) -- (ct\thecallevel) + node[midway, above] {#3}; + \def\l\thecallevel{#1} + \def\f\thecallevel{#2} + \def\t\thecallevel{#4} + \def\returnvalue{#5} + \tikzstyle{threadstyle}+=[instcolor#2] +} +{ + \addtocounter{seqlevel}{\l\thecallevel} + \path + (\f\thecallevel)+(0,-\theseqlevel*\unitfactor-0.7*\unitfactor) node (rf\thecallevel) {} + (\t\thecallevel.\threadbias)+(0,-\theseqlevel*\unitfactor-0.7*\unitfactor) node (rt\thecallevel) {}; + \draw[dashed,->,>=angle 60] ({rt\thecallevel}) -- (rf\thecallevel) + node[midway, above]{\returnvalue}; + \drawthread{ct\thecallevel}{rt\thecallevel} + \addtocounter{callevel}{-1} % pop +} + +% a function do not need call others +% interal use only +% Example: +% \begin{callself}[height]{caller}{function}{return} +% \end{callself} +\newenvironment*{callself}[4][1]{ + \stepcounter{seqlevel} + \stepcounter{callevel} % push + \stepcounter{callselflevel} + + \path + (#2)+(\thecallselflevel*0.1-0.1,-\theseqlevel*\unitfactor-0.7*\unitfactor) node (sc\thecallevel) {} + ({sc\thecallevel}.east)+(0,-0.33*\unitfactor) node (scb\thecallevel) {}; + + \draw[->,>=triangle 60] ({sc\thecallevel}.east) -- ++(0.8,0) + node[near start, above right] {#3} -- ++(0,-0.33*\unitfactor) + -- (scb\thecallevel); + \def\l\thecallevel{#1} + \def\f\thecallevel{#2} + \def\t\thecallevel{#2} + \def\returnvalue{#4} + \tikzstyle{threadstyle}+=[instcolor#2] +}{ + \addtocounter{seqlevel}{\l\thecallevel} + \path (\f\thecallevel)+(\thecallselflevel*0.1-0.1,-\theseqlevel*\unitfactor-0.33*\unitfactor) node + (sct\thecallevel) {}; + + \draw[dashed,->,>=angle 60] ({sct\thecallevel}.east) node + (sce\thecallevel) {} -- ++(0.8,0) -- node[midway, right]{\returnvalue} ++(0,-0.33*\unitfactor) -- ++(-0.8,0); + \drawthread{scb\thecallevel}{sce\thecallevel} + \addtocounter{callevel}{-1} % pop + \addtocounter{callselflevel}{-1} +} + +% message between threads +% Example: +% \mess[delay]{sender}{message content}{receiver} +\newcommand{\mess}[4][0]{ + \stepcounter{seqlevel} + \path + (#2)+(0,-\theseqlevel*\unitfactor-0.7*\unitfactor) node (mess from) {}; + \addtocounter{seqlevel}{#1} + \path + (#4)+(0,-\theseqlevel*\unitfactor-0.7*\unitfactor) node (mess to) {}; + \draw[->,>=angle 60] (mess from) -- (mess to) node[midway, above] + {#3}; + + \node (#3 from) at (mess from) {}; + \node (#3 to) at (mess to) {}; +} + +\newenvironment{messcall}[4][1]{ + \stepcounter{seqlevel} + \stepcounter{callevel} % push + \path + (#2)+(0,-\theseqlevel*\unitfactor-0.7*\unitfactor) node (cf\thecallevel) {} + (#4.\threadbias)+(0,-\theseqlevel*\unitfactor-0.7*\unitfactor) node (ct\thecallevel) {}; + + \draw[->,>=angle 60] ({cf\thecallevel}) -- (ct\thecallevel) + node[midway, above] {#3}; + \def\l\thecallevel{#1} + \def\f\thecallevel{#2} + \def\t\thecallevel{#4} + \tikzstyle{threadstyle}+=[instcolor#2] +} +{ + \addtocounter{seqlevel}{\l\thecallevel} + \path + (\f\thecallevel)+(0,-\theseqlevel*\unitfactor-0.7*\unitfactor) node (rf\thecallevel) {} + (\t\thecallevel.\threadbias)+(0,-\theseqlevel*\unitfactor-0.3*\unitfactor) node (rt\thecallevel) {}; + \drawthread{ct\thecallevel}{rt\thecallevel} + \addtocounter{callevel}{-1} % pop +} + +% In the situation of multi-threads, some objects are called at the +% same time. Currently, we have to adjust the bias of thread line +% manually. Possible parameters are: center, west, east +\newcommand{\setthreadbias}[1]{\global\def\threadbias{#1}} + +% This function makes the call earlier. +\newcommand{\prelevel}{\addtocounter{seqlevel}{-1}} + +% This function makes the call later. +\newcommand{\postlevel}{\addtocounter{seqlevel}{+1}} + +% a block box with caption +% \begin{sdblock}[caption background color]{caption}{comments} +% \end{sdblock} +\newenvironment{sdblock}[3][white]{ + \stepcounter{seqlevel} + \stepcounter{blocklevel} % push + \coordinate (blockbeg\theblocklevel) at (0,-\theseqlevel*\unitfactor-\unitfactor); + \stepcounter{seqlevel} + \def\blockcolor\theblocklevel{#1} + \def\blockname\theblocklevel{#2} + \def\blockcomm\theblocklevel{#3} + \begin{pgfinterruptboundingbox} +}{ + \coordinate (blockend) at (0,-\theseqlevel*\unitfactor-2*\unitfactor); + \path (current bounding box.east)+(0.2,0) node (boxeast) {} + (current bounding box.west |- {blockbeg\theblocklevel}) + (-0.2,0) + node (nw) {}; + \path (boxeast |- blockend) node (se) {}; + + % % title + \node[blockstyle] (blocktitle) at (nw) {\blockname\theblocklevel}; + \path (blocktitle.south east) + (0,0.2) node (set) {} + (blocktitle.south east) + (-0.2,0) node (seb) {} + (blocktitle.north east) + (0.2,0) node (comm) {}; + \draw[fill=\blockcolor\theblocklevel] (blocktitle.north west) -- (blocktitle.north east) -- + (set.center) -- (seb.center) -- (blocktitle.south west) -- cycle; + \node[blockstyle] (blocktitle) at (nw) {\blockname\theblocklevel}; + \node[blockcommentstyle] (blockcomment) at (comm) {\blockcomm\theblocklevel}; + + \coordinate (se) at (current bounding box.south east); + \end{pgfinterruptboundingbox} + + \draw (se) rectangle (nw); + + \addtocounter{blocklevel}{-1} % pop + \stepcounter{seqlevel} +} + +% the environment of sequence diagram +\newenvironment{sequencediagram}{ + % declare layers + \pgfdeclarelayer{umlsd@background} + \pgfdeclarelayer{umlsd@threadlayer} + \pgfsetlayers{umlsd@background,umlsd@threadlayer,main} + + \begin{tikzpicture} + \setlength{\unitlength}{1cm} + \tikzstyle{sequence}=[coordinate] + \tikzstyle{inststyle}=[rectangle, draw, anchor=west, minimum + height=0.8cm, minimum width=1.6cm, fill=white, + drop shadow={opacity=1,fill=black}] + \ifpgfumlsdroundedcorners + \tikzstyle{inststyle}+=[rounded corners=3mm] + \fi + \tikzstyle{blockstyle}=[anchor=north west] + \tikzstyle{blockcommentstyle}=[anchor=north west, font=\small] + \tikzstyle{dot}=[inner sep=0pt,fill=black,circle,minimum size=0.2pt] + \global\def\unitfactor{0.6} + \global\def\threadbias{center} + % reset counters + \setcounter{preinst}{0} + \setcounter{instnum}{0} + \setcounter{threadnum}{0} + \setcounter{seqlevel}{0} + \setcounter{callevel}{0} + \setcounter{callselflevel}{0} + \setcounter{blocklevel}{0} + + % origin + \node[coordinate] (inst0) {}; +} +{ + \begin{pgfonlayer}{umlsd@background} + \ifnum\c@instnum > 0 + \foreach \t [evaluate=\t] in {1,...,\theinstnum}{ + \draw[dotted] (inst\t) -- ++(0,-\theseqlevel*\unitfactor-2.2*\unitfactor); + } + \fi + \ifnum\c@threadnum > 0 + \foreach \t [evaluate=\t] in {1,...,\thethreadnum}{ + \path (thread\t)+(0,-\theseqlevel*\unitfactor-0.1*\unitfactor) node (threadend) {}; + \tikzstyle{threadstyle}+=[threadcolor\t] + \drawthread{thread\t}{threadend} + } + \fi + \end{pgfonlayer} +\end{tikzpicture}} + + +%%% End of pgf-umlsd.sty +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \ No newline at end of file diff --git a/10-entwurfsheft/sections/apidoc.tex b/10-entwurfsheft/sections/apidoc.tex new file mode 100644 index 0000000..1eb181c --- /dev/null +++ b/10-entwurfsheft/sections/apidoc.tex @@ -0,0 +1,558 @@ +\newenvironment{urlParameter} +{ + \newcommand{\urlParamItem}[2] + { + \rowcolor{\methodLightColor} ##1 & ##2 \\ + } + \newcommand{\noUrlParameter}[1] + { + \small{\textit{##1}} + } + %\vspace{-0.61em} + + \arrayrulecolor{\methodColor} + + \begin{tabularx}{\textwidth}{X} + \rowcolor{\methodLightColor!20} + \textbf{URL-Parameter} \\ \hline + \end{tabularx} + + \tabularx{\textwidth}{l X} +} + +\newenvironment{pathParameter} +{ + \newcommand{\pathParamItem}[2] + { + \rowcolor{\methodLightColor} ##1 & ##2 \\ + } + \newcommand{\noPathParameter}[1] + { + \small{\textit{##1}} + } + %\vspace{-0.61em} + + \arrayrulecolor{\methodColor} + + \begin{tabularx}{\textwidth}{X} + \rowcolor{\methodLightColor!20} + \textbf{Pfad-Parameter} \\ \hline + \end{tabularx} + + \tabularx{\textwidth}{l X} +} + +\newenvironment{jsonKeys} +{ + \newcommand{\jsonKeyItem}[2] + { + \rowcolor{\methodLightColor} ##1 & ##2 \\ + } + \newcommand{\nojsonKeys}[1] + { + \small{\textit{##1}} + } + %\vspace{-0.61em} + + \arrayrulecolor{\methodColor} + + \begin{tabularx}{\textwidth}{X} + \rowcolor{\methodLightColor!20} + \textbf{Json-Keys} \\ \hline + \end{tabularx} + + \tabularx{\textwidth}{l X} +} + +\section{\Gls{api}} + +\subsection{Hypertext Transfer Protocol} +% könnte auch unter in die Sektion API + +Das Hypertext Transfer Protocol (http) ist ein Protokoll zum übertragen von +medialen Daten. Ein Webserver hört über den Port 80 auf Anfragen und antwortet. +Eine Anfrage besteht aus einer Methode, einem Pfad, Kopfzeilen (\texttt{Header}) +und Inhalt (\texttt{Body}). Die Methode ist eine Konvention, um zu verdeutlichen +was mit der Anfrage geschehen soll. Es können Daten abgefragt (\texttt{GET}), +erstellt (\texttt{POST}), aktualisiert (\texttt{PUT}) oder gelöscht werden +(\texttt{DELETE}). Der Pfad gibt an, für welche Daten die Methode gelten soll. +Die Kopfzeilen beinhalten dabei Metadaten, wie Authentifikationen, \Glspl{cookie}, +Datentypen (MIME-Types), Sprache und das Anwenderprogramm. + +Der Server antwortet mit einem Statuscode, Kopfzeilen und Inhalt. Der Statuscode +teilt mit, ob die Anfrage erhalten wurde ($\geq$ 100), erfolgreich bearbeitet +werden konnte ($\geq$ 200), eine Weiterleitung nötig ist ($\geq$ 300), +die Anfrage einen Fehler enthält ($\geq$ 400) oder +ein interner Serverfehler aufgetreten ist ($\geq$ 500). In den Kopfzeilen können +Berechtigungen gesetzt werden, welche festlegen, wie mit den Daten +umzugehen ist. + +\newpage +\subsection{Geräte} \label{Geräte} +Die \Gls{gpodder}, welche wir für unser Produkt verwenden und erweitern, bietet eine gerätespezifische +Synchronisation an. Diese bieten wir jedoch nicht an, denn wir synchronisieren Änderungen aller +Geräte gleichwertig mit dem Nutzeraccount. +Deshalb werden Geräte innerhalb der API ignoriert. + +\subsection{Authentication API}\label{a:auth} + +\subsubsection{Registrierung}\label{a:register} + +\begin{apiRoute}{post}{/api/2/auth/register.json} + {Registriert einen Nutzer mit einer E-Mail-Adresse und Passwort. + Versendet E-Mail mit Bestätigungslink an die angegebene E-Mail-Adresse.} + \begin{routeRequest}{application/json} + \begin{routeRequestBody} +{ + email: "jeff@example.com", + password: "MyNameIsJeff" +} + \end{routeRequestBody} + \end{routeRequest} + \begin{routeResponse}{application/json} + \begin{routeResponseItem}{200} + {OK: Nutzer wurde erfolgreich angelegt.} + \end{routeResponseItem} + + \begin{routeResponseItem}{400} + {Bad Request: Fehler beim Parsen der Anfrage} + \end{routeResponseItem} + \end{routeResponse} +\end{apiRoute} + +\newpage + +\subsubsection{Anmelden}\label{a:login} + +\begin{apiRoute}{post}{/api/2/auth/\{username\}/login.json} + {Gegebenen Nutzer des gegebenen Geräts mithilfe HTTP Basic Auth einloggen.} + \begin{pathParameter} + \pathParamItem{username}{Nutzername des einzuloggenden Nutzers} + \end{pathParameter} + \begin{routeResponse}{application/json} + \begin{routeResponseItem}{200} + {OK: Die Responseheader haben ein gesetztes Session-ID \Gls{cookie}.} + \end{routeResponseItem} + + \begin{routeResponseItem}{400} + {Bad Request: Der Client stellt einen \Gls{cookie} für einen falschen Nutzer bereit.} + \end{routeResponseItem} + + \begin{routeResponseItem}{401} + {Unauthorized: Zugriff ohne Anmeldedaten} + \end{routeResponseItem} + \end{routeResponse} +\end{apiRoute} + +\subsubsection{Abmelden}\label{a:logout} + +\begin{apiRoute}{post}{/api/2/auth/\{username\}/logout.json} + {Löscht den \Gls{cookie} beim Client.} + \begin{pathParameter} + \pathParamItem{username}{Nutzername des betreffenden Nutzers} + \end{pathParameter} + \begin{routeResponse}{application/json} + \begin{routeResponseItem}{200} + {OK: Falls der Client keinen \Gls{cookie} gesendet hat oder der Nutzer + erfolgreich ausgeloggt wurde.} + \end{routeResponseItem} + + \begin{routeResponseItem}{400} + {Bad Request: Der Client stellt einen \Gls{cookie} für den falschen Nutzer bereit.} + \end{routeResponseItem} + \end{routeResponse} +\end{apiRoute} + +\newpage + +\subsubsection{Passwort ändern}\label{a:changepassword} + +\begin{apiRoute}{put}{/api/2/auth/\{username\}/changepassword.json} + {Passwort des gegebenen Nutzer ändern, indem altes und neues Passwort + übergeben werden. } + \begin{pathParameter} + \pathParamItem{username}{Nutzername des betreffenden Nutzers} + \end{pathParameter} + \begin{routeRequest}{application/json} + \begin{routeRequestBody} +{ + password: "MyPasswordWasLeaked", + new_password: "SoIMadeANewOne" +} + \end{routeRequestBody} + \end{routeRequest} + \begin{routeResponse}{application/json} + \begin{routeResponseItem}{200} + {OK: Die Responseheader haben ein gesetztes Session-ID \Gls{cookie}.} + \end{routeResponseItem} + + \begin{routeResponseItem}{400} + {Bad Request: Der Client stellt einen \Gls{cookie} für einen falschen Nutzer bereit.} + \end{routeResponseItem} + + \begin{routeResponseItem}{401} + {Unauthorized: Zugriff ohne Anmeldedaten} + \end{routeResponseItem} + \end{routeResponse} +\end{apiRoute} + +\subsubsection{Passwort vergessen}\label{a:forgot} + +\begin{apiRoute}{post}{/api/2/auth/forgot.json} + {Sende eine E-Mail zum Zurücksetzen des Passworts. } + \begin{routeRequest}{application/json} + \begin{routeRequestBody} +{ + email: "jeff@example.com" +} + \end{routeRequestBody} + \end{routeRequest} + \begin{routeResponse}{application/json} + \begin{routeResponseItem}{200} + {OK: E-Mail wurde erfolgreich versendet.} + \end{routeResponseItem} + + \begin{routeResponseItem}{400} + {Bad Request: Fehler beim Parsen der Anfrage} + \end{routeResponseItem} + \end{routeResponse} +\end{apiRoute} + +\newpage +\subsubsection{Passwort zurücksetzen}\label{a:resetpassword} + +\begin{apiRoute}{put}{/api/2/auth/\{username\}/resetpassword.json} + {Passwort des gegebenen Nutzers ändern nachdem dieser sein Passwort + vergessen hat. } + \begin{pathParameter} + \pathParamItem{username}{Nutzername des betreffenden Nutzers} + \end{pathParameter} + \begin{urlParameter} + \urlParamItem{token}{JSON-Web-Token} + \end{urlParameter} + \begin{routeRequest}{application/json} + \begin{routeRequestBody} +{ + password: "APasswordIWontForgetAgain" +} + \end{routeRequestBody} + \end{routeRequest} + \begin{routeResponse}{application/json} + \begin{routeResponseItem}{200} + {OK: Das Passwort wurde erfolgreich geändert.} + \end{routeResponseItem} + + \begin{routeResponseItem}{400} + {Bad Request: Fehler beim Parsen der Anfragen. } + \end{routeResponseItem} + + \begin{routeResponseItem}{401} + {Unauthorized: JWT ist ungültig. } + \end{routeResponseItem} + \end{routeResponse} +\end{apiRoute} + +\newpage +\subsubsection{Account löschen}\label{a:delete} + +\begin{apiRoute}{delete}{/api/2/auth/\{username\}/delete.json} + {Der Account des gegebenen Nutzers wird gelöscht.} + \begin{pathParameter} + \pathParamItem{username}{Nutzername des zu löschenden Nutzers.} + \end{pathParameter} + \begin{routeRequest}{application/json} + \begin{routeRequestBody} +{ + password: "APasswordIWontHaveToRememberAnymore" +} + \end{routeRequestBody} + \end{routeRequest} + \begin{routeResponse}{application/json} + \begin{routeResponseItem}{200} + {OK: Der Account des gegebenen Nutzers wurde erfolgreich gelöscht.} + \end{routeResponseItem} + + \begin{routeResponseItem}{400} + {Bad Request: Der Client stellt einen \Gls{cookie} für einen falschen Nutzer bereit.} + \end{routeResponseItem} + + \begin{routeResponseItem}{401} + {Unauthorized: Zugriff ohne Anmeldedaten} + \end{routeResponseItem} + \end{routeResponse} +\end{apiRoute} + +\newpage +\subsection{Subscriptions API}\label{a:subs} +\subsubsection{Abrufen aller \Glspl{abo}}\label{a:getSubs} + +\begin{apiRoute}{get}{/subscriptions/\{username\}.json}{/subscriptions/\{username\}/\{deviceid\}.json} + \begin{pathParameter} + \pathParamItem{username}{Nutzername des betreffenden Nutzers} + \pathParamItem{deviceid}{siehe~\nameref{Geräte}} + \end{pathParameter} + + \begin{urlParameter} + \urlParamItem{jsonp}{\Gls{JSONP} wird nicht unterstützt und der Parameter entsprechend ignoriert.} + \end{urlParameter} + + \begin{routeResponse}{application/json} + + \begin{routeResponseItem}{200} + {OK: \Glspl{abo} werden im \Gls{json}-Format zurückgegeben.} + \begin{routeResponseItemBody} +[ + "http://example.org/feed.rss", + "http://example.org/podcast.php", + "http://example.org/foo" +] + \end{routeResponseItemBody} + \end{routeResponseItem} + + \begin{routeResponseItem}{400}{Bad Request: falsches Format} + \end{routeResponseItem} + + \begin{routeResponseItem}{401} + {Unauthorized: falscher Nutzer oder nicht eingeloggt} + \end{routeResponseItem} + + \end{routeResponse} +\end{apiRoute} + +\newpage +\subsubsection{Abrufen der Informationen aller \Glspl{abo}}\label{a:getTitles} + +\begin{apiRoute}{get}{/subscriptions/titles/\{username\}.json}{/subscriptions/\{username\}/\{deviceid\}.json} + + \begin{pathParameter} + \pathParamItem{username}{Nutzername des betreffenden Nutzers} + \end{pathParameter} + \begin{routeResponse}{application/json} + + \begin{routeResponseItem}{200} + {OK: \Glspl{abo} werden im angefragten Format zurückgegeben. Dabei werden für ein \Gls{abo} nur die aktuellsten 20 \Glspl{episode} zurückgegeben.} + \begin{routeResponseItemBody} +[ + { + "url": "http://example.com/podcast.php", + "title": "This is a cool podcast" + "timestamp": "2009-12-12T09:00:00", + "episodes": [ + { + "podcast": "http://example.org/podcast.php", + "episode": "http://ftp.example.org/foo.ogg", + "title": "Episode 1: My journy", + "timestamp": "2009-12-12T09:00:00", + "guid": "AU-20190814-1342-5100-A", + "action": "play", + "started": 15, + "position": 120, + "total": 500 + } + ] + } +] + \end{routeResponseItemBody} + \end{routeResponseItem} + + \begin{routeResponseItem}{400}{Bad Request: falsches Format} + \end{routeResponseItem} + + \end{routeResponse} + +\end{apiRoute} +\newpage + +\subsubsection{\Glspl{abo} hochladen}\label{a:uploadSubs} + +\begin{apiRoute}{put}{/subscriptions/\{username\}/\{deviceid\}.json} + {Falls bereits \Glspl{abo} im Nutzeraccount vorhanden, werden diese + zusammengeführt.} + + \begin{pathParameter} + \pathParamItem{username}{Nutzername des betreffenden Nutzers} + \pathParamItem{deviceid}{siehe~\nameref{Geräte}} + \end{pathParameter} + + \begin{routeResponse}{application/json} + \begin{routeResponseItem}{200} + {OK: \Glspl{abo} wurden aktualisiert und leerer String wird zurückgegeben.} + \end{routeResponseItem} + + \begin{routeResponseItem}{400}{Bad Request: falsches Format} + \end{routeResponseItem} + + \begin{routeResponseItem}{401}{Unauthorized: falscher Nutzer} + \end{routeResponseItem} + \end{routeResponse} +\end{apiRoute} + +\subsubsection{Abrufen von \Gls{abo}-Änderungen}\label{a:getSubDelta} + +\begin{apiRoute}{get}{/api/2/subscriptions/\{username\}/\{deviceid\}.json} + {} + + \begin{pathParameter} + \pathParamItem{username}{Nutzername des betreffenden Nutzers} + \pathParamItem{deviceid}{siehe~\nameref{Geräte}} + \end{pathParameter} + + \begin{urlParameter} + \urlParamItem{since}{Timestamp-Wert der letzten Antwort} + \end{urlParameter} + + \begin{routeResponse}{application/json} + + \begin{routeResponseItemBody} +{ + "add": [ + "http://example.com/feed.rss", + "http://example.org/podcast.php" + ], + "remove": ["http://example.net/foo.xml"], + "timestamp": 12347 +} + \end{routeResponseItemBody} + \end{routeResponse} + +\end{apiRoute} + +\newpage +\subsubsection{Änderungen der \Glspl{abo} hochladen}\label{a:applySubDelta} + +\begin{apiRoute}{post}{/api/2/subscriptions/\{username\}/\{deviceid\}.json} + {} + + \begin{pathParameter} + \pathParamItem{username}{Nutzername des betreffenden Nutzers} + \pathParamItem{deviceid}{siehe~\nameref{Geräte}} + \end{pathParameter} + + \begin{routeRequest}{application/json} + \begin{routeRequestBody} +{ + "add": [ + "http://example.com/feed.rss", + "http://example.org/podcast.php" + ], + "remove": ["http://example.net/foo.xml"] +} + \end{routeRequestBody} + \end{routeRequest} + + \begin{routeResponse}{application/json} + \begin{routeResponseItem}{200}{OK: \Glspl{abo} wurden aktualisiert.} + \begin{routeResponseItemBody} +{ + "timestamp": 1337, + "update_urls": [] +} + \end{routeResponseItemBody} + ``update\textunderscore urls'' wird nicht bereitgestellt, deshalb + wird eine leere Liste zurückgegeben. + \end{routeResponseItem} + \begin{routeResponseItem}{400}{Bad Request: falsches Format} + \end{routeResponseItem} + \end{routeResponse} +\end{apiRoute} + +\newpage +\subsection{Episode Action API}\label{a:episodeActions} + +\subsubsection{Episode Actions hochladen}\label{a:uploadEpisodeActions} + +\begin{apiRoute}{post}{/api/2/episodes/\{username\}.json} + {} + + \begin{pathParameter} + \pathParamItem{username}{Nutzername des betreffenden Nutzers} + \end{pathParameter} + + \begin{routeRequest} + + \begin{routeRequestBody} +POST /api/2/episodes/some-user.json +[ + { + "podcast": "http://example.org/podcast.php", + "episode": "http://ftp.example.org/foo.ogg", + "title": "Episode 1: My journey", + "timestamp": "2009-12-12T09:00:00" + "guid": "AU-20190814-1342-5100-A", + "action": "play", + "started": 15, + "position": 120, + "total": 500 + } +] + + \end{routeRequestBody} + + \end{routeRequest} + + \begin{jsonKeys} + \jsonKeyItem{podcast}{Feed-URL des \Glspl{podcast} zu welchem die \Gls{episode} gehört (erforderlich)} + \jsonKeyItem{episode}{Medien-URL der \Gls{episode} (erforderlich)} + \jsonKeyItem{device}{Geräte-ID auf welcher die Aktion stattfand.} + \jsonKeyItem{action}{Eins von: download, play, delete, new (erforderlich)} + \jsonKeyItem{timestamp}{UTC-Timestamp im ISO 8601 Format, wann die Aktion stattfand} + \jsonKeyItem{started}{Position in Sekunden, an welcher das Anhören der \Gls{episode} gestartet wurde (nur bei Aktion: play)} + \jsonKeyItem{position}{Position in Sekunden, an welcher das Anhören der \Gls{episode} gestoppt wurde (nur bei Aktion: play)} + \jsonKeyItem{total}{Länge der \Gls{episode} (nur bei Aktion: play)} + + \end{jsonKeys} + + \begin{routeResponse}{application/json} + \begin{routeResponseItem}{200}{OK} + \begin{routeResponseItemBody} +{ + "timestamp": 1337, + "update_urls": [] +} + \end{routeResponseItemBody} + ``update\textunderscore urls'' wird nicht bereitgestellt, deshalb + wird eine leere Liste zurückgegeben. + \end{routeResponseItem} + \end{routeResponse} +\end{apiRoute} + +\subsubsection{Abrufen von Episode Actions}\label{a:getEpisodeActions} + +\begin{apiRoute}{get}{/api/2/episodes/\{username\}.json} + {} + + \begin{pathParameter} + \pathParamItem{username}{Nutzername des betreffenden Nutzers} + \end{pathParameter} + + \begin{urlParameter} + \urlParamItem{podcast (string)}{URL des \Glspl{podcast}; falls gesetzt, werden nur Aktionen der \Glspl{episode} des gegebenen \Glspl{podcast} zurückgegeben.} + \urlParamItem{device (string)}{Device-ID; siehe~\nameref{Geräte}} + \urlParamItem{since (int)}{Nur \Glspl{episode}-Aktionen ab dem gegebenen Timestamp werden zurückgegeben.} + \urlParamItem{aggregated (bool)}{Wird ignoriert. } + \end{urlParameter} + + \begin{routeResponse}{application/json} + \begin{routeResponseItem}{200}{OK} + \begin{routeResponseItemBody} +{ + "actions": [ + { + "podcast": "http://example.org/podcast.php", + "episode": "http://ftp.example.org/foo.ogg", + "title": "Episode 1: My journey", + "timestamp": "2009-12-12T09:00:00" + "guid": "AU-20190814-1342-5100-A", + "action": "play", + "started": 15, + "position": 120, + "total": 500 + } + ], + "timestamp": 12345 +} + \end{routeResponseItemBody} + \end{routeResponseItem} + \end{routeResponse} +\end{apiRoute} + diff --git a/10-entwurfsheft/sections/aufbau.tex b/10-entwurfsheft/sections/aufbau.tex new file mode 100644 index 0000000..c0747a9 --- /dev/null +++ b/10-entwurfsheft/sections/aufbau.tex @@ -0,0 +1,89 @@ +\section{Aufbau} + +\subsection{Architektur} + +\subsubsection{Aufteilung in Pakete} + +Das Backend wird in Pakete aufgeteilt, die den einzelnen implementierten +\Glspl{api} der gpodder.net \Gls{api} entsprechen. +Dies entspricht hier den \Glspl{api}: Subscriptions, Episode Actions und Authentication. +Außerdem gibt es ein Model-Paket, welches die in den Logikschichten verwendeten Objekte modelliert. + +Für den Webserver selbst werden die \Glspl{api} um zusätzliche Funktionen erweitert. Denn die vorhandenen \Glspl{api} bieten nicht alle Funktionalitäten, die der Webserver benötigt, um die im Pflichtenheft beschriebenen Kriterien zu erfüllen. +% (TODO: WebAPI bestimmen) + +\subsubsection{Die Architektur in den Paketen} + +In den Paketen selber herrscht eine 3-schichtige Architektur aus den Schichten: Controller, Service und Data-Access (siehe \ref{DAO_Pattern}). + +Die Controller-Schicht nimmt die Anfragen des Clients entgegen, lässt diese in den untergeordneten Schichten verarbeiten und gibt den Status der abgeschlossenen Handlung an den Client zurück. + +Die Service-Schicht ist die \Gls{business} des Programmes und überprüft bspw. Eingaben oder verschickt E-Mails. Sollte ein \Gls{db}aufruf nötig sein, so ruft der Service die Data-Access-Schicht auf und übergibt ihr die nötigen Informationen, was gespeichert, gelöscht, aktualisiert oder gelesen werden soll. + +Die Data-Access-Schicht ist für den Datenzugriff auf \Glspl{db} verantwortlich. Alle verantwortlichen Klassen für einen solchen Zugriff folgen dem \Gls{crud}-Prinzip. +\Gls{crud} steht für die Funktionen Create (erschaffen), Read (lesen), Update (aktualisieren) und Delete (löschen). Entsprechend können Einträge in einer \Gls{db} auch nur erschaffen, gelesen, aktualisiert und gelöscht werden. + +\subsubsection{Das Model-Paket}\label{p:model} + +In einem globalen Model-Paket befinden sich alle Klassen, die für die Modellierungen von Objekten zuständig sind. +Diese Objekte werden später in \Gls{db}abfragen gelesen und mittels objektrelationaler Abbildung (siehe \ref{t:orm}) gespeichert. +In einer Antwort an den Client werden die Objekte, falls benötigt, als Antwort-Klassen gewrappt im \Gls{json}-Format zurückgeschickt. +Auch kann der Client in einer Anfrage ein Objekt im \Gls{json}-Format übergeben, welches dann mithilfe von Spring als \Gls{java}-Objekt der korrespondierenden Anfrage-Klasse interpretiert wird. +Aus diesem wird dann in Controller- und Service-Schicht das Objekt der entsprechenden Model-Klasse extrahiert. + +\subsubsection{Die Datenhaltungsschicht} + +Mit den Paketen ist bereits eine 3-schichtige Architektur aufgebaut. +Damit Daten aber auch gelesen und gespeichert werden können ist eine vierte globale Schicht nötig - die Datenhaltungsschicht. + +Die Datenhaltungsschicht ist für die persistente Speicherung aller Daten zuständig. +Diese werden meist (wie in diesem Fall) in einer \Gls{db} gespeichert. +Der Zugriff auf die Datenhaltungsschicht erfolgt gemäß der intransparenten Schichtenarchitektur nur über die Data-Access-Schicht der Pakete. + +Der Vorteil dieser mehrschichtigen Architektur ist die klare Strukturierung des Programmes, womit der Code leserlicher und einfacher zu warten ist. + +\begin{landscape} + +\subsection{Klassendiagramm Backend} +Das Klassendiagramm zeigt alle für den Entwurf relevanten Klassen des Backends mit ihren öffentlichen Methoden. +Weiter zeigt das Diagramm die Aufteilung der Klassen in Pakete sowie schemenhaft dargestellte Verbindungen zu \Gls{db} und Webserver. + +% \input{assets/diagrams/classdiagram.latex} +\includegraphics[width=\linewidth]{assets/diagrams/classdiagram} +\end{landscape} + +\subsection{Sequenzdiagramme} + +\subsubsection{Authentication \Gls{api}} +\subsubsection*{Registrierung \scriptsize{(\ref{a:register})}} +\includegraphics[width=\textwidth]{assets/diagrams/sequencediagram-register} +\subsubsection*{Passwort vergessen und zurücksetzen \scriptsize{(\ref{a:forgot}, \ref{a:resetpassword})}} +\includegraphics[width=\textwidth]{assets/diagrams/sequencediagram-forgotAndResetPW} + +\subsubsection{Subscriptions \Gls{api}} +\subsubsection*{Abonnements hochladen \scriptsize{(\ref{a:uploadSubs})}} +\includegraphics[width=\textwidth]{assets/diagrams/sequencediagram-uploadSubscriptions} + +\subsubsection*{Abrufen aller Abonnements \scriptsize{(\ref{a:getSubs})}} +\includegraphics[width=\textwidth]{assets/diagrams/sequencediagram-getSubscriptions} + +\subsubsection{Episode Actions \Gls{api}} +\subsubsection*{Episode Actions hochladen \scriptsize{(\ref{a:uploadEpisodeActions})}} +\includegraphics[width=\textwidth]{assets/diagrams/sequencediagram-uploadEpisodeActions} + +\subsubsection*{Abrufen aller Episode Actions seit einem Zeitpunkt \scriptsize{(\ref{a:getEpisodeActions})}} +\includegraphics[width=\textwidth]{assets/diagrams/sequencediagram-getEpisodeActionsOfPodcastSince} + +\subsubsection*{Abrufen aller Episode Actions \scriptsize{(\ref{a:getEpisodeActions})}} +\includegraphics[width=\textwidth]{assets/diagrams/sequencediagram-getEpisodeActions} + +\subsection{Komponentendiagramm Backend} +\includegraphics[width=\textwidth]{assets/diagrams/backendComponentDiagram} + +\subsection{Verteilungsdiagram} +\includegraphics[width=\textwidth]{assets/diagrams/deployment} + +\subsection{\Gls{db}-Modell} + +\includegraphics[width=\textwidth]{assets/diagrams/db} + diff --git a/10-entwurfsheft/sections/backend.tex b/10-entwurfsheft/sections/backend.tex new file mode 100644 index 0000000..19efd65 --- /dev/null +++ b/10-entwurfsheft/sections/backend.tex @@ -0,0 +1,28 @@ +\section{Backend} + +Für das Backend wird das \Gls{java}-Framework \Gls{spring} verwendet. Neben seiner Modularität, +bietet es viele Funktionen, die unter +anderem die Entwicklung von Backend-Anwendungen vereinfachen. +Darunter fällt zum Beispiel die Unterstützung von Dependency Injection, dessen +Vorteile bereits beschrieben wurden. +Außerdem unterstützt \Gls{java} \Gls{spring} intern eine Verwaltung von \Glspl{db}, sodass +sich nicht mit dem \Gls{SQL}-Code an sich befasst werden muss. +Auch bezüglich Authentifizierung und Sicherheit bietet \Gls{spring} eigene Funktionalitäten. + +Zusätzlich dazu wird als Build-System für das \Gls{java}-\Gls{spring}-Backend Maven verwendet. +Maven hilft dabei alle Abhängigkeiten des Projekts zu verwalten und automatisiert +den Build Prozess. +Durch die zusätzliche Unterstützung von Versionskontrollsystemen und der Kompatibilität +zu vielen Continuous Integration-Tools wird außerdem die Entwicklung in einem Team erleichtert +und optimiert. Denn dadurch können Build- und Deployment-Prozesse automatisiert werden. +Außerdem bietet Maven Bibliotheken zum Testen sowie zur Generierung von Dokumentationen an. + +Vom Backend benötigte Abhängigkeiten: +\begin{itemize} + \item \Gls{spring} Web + \item \Gls{spring} Security + \item \Gls{spring} Mail Sender + \item \Gls{spring} Data JPA + \item Lombok + \item Rome (\Gls{rss} parsing/fetching) +\end{itemize} \ No newline at end of file diff --git a/10-entwurfsheft/sections/changes.tex b/10-entwurfsheft/sections/changes.tex new file mode 100644 index 0000000..d8ac2ec --- /dev/null +++ b/10-entwurfsheft/sections/changes.tex @@ -0,0 +1,41 @@ + +\section{Änderungen zum Pflichtenheft} + +Im Folgenden werden die Änderungen zum Pflichtenheft aufgelistet. +Hierbei handelt es sich um neue Kriterien, Kriterien die wegfallen und Kriterien die modifiziert wurden. + +\subsection{Neue Kriterien} + +\subsubsection{Cooldown Fetch-Vorgang} +\begin{description} + \item[\textcolor{Green}{\textlangle RS11\textrangle}] \textcolor{Green}{Wenn ein Nuter ein \Gls{abo} oder eine \Gls{episode} eines \Gls{abo} hinzugefügt hat, welche noch nicht + in der \Gls{db} vorhanden ist, werden die Daten vom entsprechenden Server gefetched. Bis eine weitere fetch-Anfrage + zum selben \Gls{abo} gestartet wird, wird ein Cooldown von einer Stunde abgewartet. + Das hilft dabei DoS Angriffe zu vermeiden, da der Server damit nicht durch zu viele Fetch-Anfragen überlastet + werden kann.} +\end{description} + +\subsubsection{Sprache von Browserinformationen übernehmen} +\begin{description} + \item[\textcolor{Green}{\textlangle RC13\textrangle}] \textcolor{Green}{Betritt ein Nutzer zum ersten Mal die Webseite, ruft ein Skript die Browserdaten zur eingestellten Sprache ab. + Die Sprache wird im localStorage gespeichert und ruft entsprechend die Webseite in der jeweiligen Sprache auf. + Ändert der Nutzer die Sprache, so wird dies durch eine Änderung der Sprachvariable im localStorage festgehalten. } +\end{description} + +\subsection{Abgeänderte Kriterien} + + \subsubsection{Sessionspeicherung mit JWT} + + \begin{description} + \item[\textlangle RS10\textrangle] Im Webfrontend angemeldete Benutzer bleiben dort angemeldet. + Hierfür wird ein \Gls{session-token} in einem \Gls{cookie} gespeichert. + \textcolor{blue}{Als \Gls{session-token} wird ein \Gls{json} Web Token verwendet. + Dieser Token wird für die Authentifikation genutzt.} + \end{description} + +\subsection{Entfernte Kriterien} + + \begin{description} + \item[\textlangle RC5\textrangle] + \textcolor{red}{\sout{Die Weboberfläche ist kompatibel mit beliebigen \Glspl{gpodder}.}} + \end{description} diff --git a/10-entwurfsheft/sections/einleitung.tex b/10-entwurfsheft/sections/einleitung.tex new file mode 100644 index 0000000..0fd6114 --- /dev/null +++ b/10-entwurfsheft/sections/einleitung.tex @@ -0,0 +1,22 @@ +\section{Einleitung} + +Im vorangegangenen Pflichtenheft wurde ein Synchronisationsserver für \Glspl{podcast} beschrieben. +In diesem Entwurfsheft wird die Implementierung dieses Synchronisationsservers +mithilfe eines Entwurfes geplant. +Wie im Pflichtenheft beschrieben, wird das finale Produkt +aus einem Backend und einem Frontend bestehen. + +Das Webfrontend dient dazu, dem Nutzer eine Oberfläche zur Accountverwaltung +und Einsicht seiner Synchronisationsstände und Daten zu ermöglichen. +Dieses wird mithilfe des Javascript Frameworks Vue.js erstellt. + +Das Backend dient dazu, HTTP-Anfragen des Frontends und der \Gls{podcatcher} entgegenzunehmen, +zu verarbeiten sowie zu beantworten. +Damit Daten persistent gespeichert werden können, wird eine \Gls{db} eingebunden. +Zur Erstellung des Backends wird \Gls{java} \Gls{spring} verwendet und für die \Gls{db} MariaDB. + +%Neben den Schichten gibt es drei große Zuordnungsbereiche in unserem Produkt. +%Nämlich die Abonnements die ein Nutzer haben kann, deren Episoden +%und die Accountverwaltung. +%Deshalb unterteilen wir die einzelnen Projektklassen +%schichtenübergreifend gemäß dieser Kategorien nochmals in einzelne Pakete. diff --git a/10-entwurfsheft/sections/frontend.tex b/10-entwurfsheft/sections/frontend.tex new file mode 100644 index 0000000..69a3c61 --- /dev/null +++ b/10-entwurfsheft/sections/frontend.tex @@ -0,0 +1,150 @@ +\section{Weboberfläche} + +Die Weboberfläche wird mit dem Frontend-Web-Framework Vue.js erstellt. Mit Vue +werden wiederverwendbare, auf Datenänderungen reagierende Komponenten erstellt. +Die Komponenten nutzen ein fertiges Aussehen von dem Frontend-CSS-Framework +Bootstrap. Außerdem werden Icons der freien Schriftart fontawesome +verwendet. + +Das Projekt wird durch den \Gls{bundler} vite aufgesetzt, gebaut und stellt einen +Entwicklungswebserver mit Echtzeitvorschau bereit. Durch den vue-router wird bei +der \Gls{spa} ein Seitenwechsel durch Manipulation der +Browser-Chronik (History Manipulation) simuliert. Dadurch hat der Nutzer eine +bessere Erfahrung, weil die Seite nicht neu geladen werden muss, wenn zu einem +anderen Menüpunkt gewechselt wird. + +Wenn der Nutzer sich anmeldet wird die E-Mail-Adresse, der \Gls{session-token} und +Spracheinstellungen in dem globallen Zustandsspeicher gespeichert, welcher durch +die Pinia-Abhängigkeit bereitgestellt wird. Dadurch haben alle +\Glspl{uiComponent} einfachen Zugriff auf die Daten und diese müssen nicht über +Props in tiefliegende \Glspl{uiComponent} durchgereicht werden. + +Die Seiten werden als einzelne Kompontenten erstellt. Andere HTML-Strukturen +werden als eigene Komponenten ausgelagert, wenn sie in mehreren Seiten verwendet +werden und zur Reduzierung von Komplexität und Duplikation beitragen und selbst +Logik beinhalten. + +Vom Frontend benötigte Abhängigkeiten: +\begin{itemize} +\item vite +\item vue +\item vue-router +\item Pinia (globaler Zustandsspeicher) +\item bootstrap +\item fontawesome +\item vue-i18n (Support für mehrere Sprachen) +\end{itemize} + +\subsection{Komponentendiagramm Web-Frontend} +\includegraphics[width=\textwidth]{assets/diagrams/componentdiagram} + +\subsection{Komponentenbeschreibung} + +\begin{minipage}{.7\linewidth} + +\subsubsection*{SubscriptionComponent} + +\begin{description} +\item[Tag] \texttt{} +\item[Props] \mbox{} \\ + \emph{subscription} \mbox{} Subscription-Objekt, welches Attribute zu einem \Gls{podcast} und + dessen \Glspl{episode} enthält. +\item[Beschreibung] Nimmt ein Subscription-Objekt, zeigt Titel und LastUpdate und +\Glspl{episode} der Subscription an. +\end{description} +\end{minipage} +\begin{minipage}{.3\linewidth} + \begin{figure}[H] + \includegraphics[width=\textwidth]{assets/subscription.png} + \end{figure} +\end{minipage} + + +\vspace{.5cm} +\begin{minipage}{.7\linewidth} +\subsubsection*{EpisodeComponent} + +\begin{description} +\item[Tag] \texttt{} +\item[Props] \mbox{} \\ + \emph{episode} \mbox{} EpisodeAction-Objekt, welches Titel, \Gls{podcast}, + Timestamp, Dauer und Hörfortschritt der \Gls{episode} enthält. +\item[Beschreibung] Nimmt ein EpisodeAction-Object, zeigt Titel, \Gls{podcast}, Dauer, + Hörfortschritt und LastUpdate an. +\end{description} +\end{minipage} +\begin{minipage}{.3\linewidth} + \begin{figure}[H] + \includegraphics[width=\textwidth]{assets/episode.png} + \end{figure} +\end{minipage} + + +\vspace{.5cm} +\begin{minipage}{.7\linewidth} +\subsubsection*{LastUpdateComponent} + +\begin{description} +\item[Tag] \texttt{} +\item[Props] \mbox{} \\ + \emph{timestamp} \mbox{} Date-Objekt, welches die Zeit der letzten Änderung enthält +\item[Beschreibung] Nimmt ein Timestamp und gibt die Zeit seit dem Timestamp in + einem menschenlesbaren Format aus. +\end{description} +\end{minipage} +\begin{minipage}{.3\linewidth} + \begin{figure}[H] + \includegraphics[width=\textwidth]{assets/lastupdate.png} + \end{figure} +\end{minipage} + + +\vspace{.5cm} +\begin{minipage}{.7\linewidth} +\subsubsection*{HelpComponent} + +\begin{description} +\item[Tag] \texttt{} +\item[Beschreibung] Zeigt Hilfestellungen in einem Fenster an. Dieses kann über + die Navigationsleiste aufgerufen werden. +\end{description} +\end{minipage} +\begin{minipage}{.3\linewidth} + \begin{figure}[H] + \includegraphics[width=\textwidth]{assets/help.png} + \end{figure} +\end{minipage} + + +\vspace{.5cm} +\begin{minipage}{.7\linewidth} +\subsubsection*{NavbarComponent} + +\begin{description} +\item[Tag] \texttt{} +\item[Beschreibung] Enthält route-links zu im vue-router definierten Pfaden. +\end{description} +\end{minipage} +\begin{minipage}{.3\linewidth} + \begin{figure}[H] + \fbox{\includegraphics[width=\textwidth]{assets/navbar.png}} + \end{figure} +\end{minipage} + + +\vspace{.5cm} +\begin{minipage}{.7\linewidth} +\subsubsection*{PasswordValidatorComponent} + +\begin{description} +\item[Tag] \texttt{} +\item[Beschreibung] Überprüft ob der eingegebene Text die Bedingungen für ein + sicheres Passwort erfüllt. +\end{description} +\end{minipage} +\begin{minipage}{.3\linewidth} + \begin{figure}[H] + \includegraphics[width=\textwidth]{assets/password-margin.png} + \end{figure} +\end{minipage} + diff --git a/10-entwurfsheft/sections/glossar.tex b/10-entwurfsheft/sections/glossar.tex new file mode 100644 index 0000000..9caad87 --- /dev/null +++ b/10-entwurfsheft/sections/glossar.tex @@ -0,0 +1,371 @@ +\makeglossaries + +\newglossaryentry{spa} +{ + name=Single-Page-Application, + description={ + ist ein Webseiten-Modell, bei welchem dem Nutzer nur ein Webdokument + bereitgestellt wird. Mit einem Skript wird der Inhalt der Seite + dynamisch mit Daten einer API befüllt. Außerdem verwaltet die Seite + (nicht der Server), welcher Inhalt bei welchem Pfad angezeigt wird. Dies + erzeugt geringere Serverlast und eine bessere Nutzererfahrung, da die + Seitenstruktur beim Laden von neuen Inhalten erhalten bleibt} +} + +\newglossaryentry{packagemanager} +{ + name=Paketmanager, + description={ + ist ein Programm, welches Pakete und dessen Abhängigkeiten verwaltet, + installiert, entfernt und aktualisiert. Pakete können andere Programme, + Plugins oder Software-Bibliotheken sein} +} + +\newglossaryentry{bundler} +{ + name=Bundler, + description={ + ist ein Programm, welches genutzte Teile von Abhängigkeiten eines + Software-Projekts in passender Reihenfolge zusammensucht und daraus + Dateien erstellt, die für den Nutzer bereitgestellt werden können. Dabei + kann der Bundler mit zusätzlichen Modulen Dateien erzeugen, die + rückwärtskompatibel oder für den Nutzer schwerer einsehbar sind} +} + +\newglossaryentry{java} +{ + name=Java, + description={ + ist eine objekt-orientierte interpretierte kompilierte + Programmiersprache, welche plattformunabhängig auf einer virtuellen + Maschine ausgeführt wird} +} + +\newglossaryentry{db} +{ + name=Datenbank, + plural=Datenbanken, + description={ + ist ein System um Daten persistent zu speichern und effizient zu + verwalten. Am meisten verbreitet sind relationale Datenbanken, welche + Daten in Tabellen mit Referenzen zu Einträgen anderer Tabellen + speichern. Programme können dann über eine Anfragesprache (Structured + Query Language - \Gls{SQL}) komplexe Operationen auf den Daten ausführen} +} + +\newglossaryentry{docker} +{ + name=Docker, + description={ + ist ein Programm, das virtualisierte Container ausführt. Ein Programm in + so einem Container läuft in seiner eigenen virtuellen Umgebung, wodurch + das Host-System sicher bleibt. Zudem lassen sich die Container leicht + auf andere Systeme verteilen} +} + +% RESTfull-API, JSON, RSS-Feed, Salting and Hasing, OAuth, Cookie, Garbage +% Collection, DSGVO, Podcast, Podcatcher, Episode, Gpodder, + +\newglossaryentry{podcatcher} +{ + name=Podcatcher, + plural=Podcatchern, + description={ + ist ein Programm, über welches man Podcasts entdecken, abonnieren und + Episoden von Podcasts hören kann. Mit einem Account auf einer Plattform, + welche eine Gpodder-API zur Verfügung stellt, können Ereignisse, die von + einem Nutzer ausgehen, auf anderen Podcatchern des Nutzers + synchronisiert werden} +} + +\newglossaryentry{podcast} +{ + name=Podcast, + description={ + ist ein RSS-Feed, dessen Einträge die Episoden darstellen} +} + +\newglossaryentry{episode} +{ + name=Episode, + plural=Episoden, + description={ + ist ein Eintrag in einem Podcast. Eine URL in dem Eintrag zeigt auf eine + Medien-Datei, welche vom Podcatcher abgespielt werden kann} +} + +\newglossaryentry{rest-api} +{ + name=RESTful-API, + description={ + ist ein Schnittstellenentwurf über das Hypertext Transfer Protocol + (HTTP), bei dem die Schnittstellen strukturiert als Pfad an einem + Endpunkt erreichbar sind. Mittels verschiedener HTTP-Methoden können an + der Schnittstelle Daten abgefragt (GET), gesendet (PUT), gelöscht + (DELETE) oder geändert (POST) werden. Die Daten, die über die + Schnittstelle gesendet werden liegen meist im JSON-Format vor} +} + + +\newglossaryentry{gpodder} +{ + name=Gpodder-API, + description={ + wird von gpodder.net benutzt und entwickelt. Die API wird als + Schnittstelle zwischen Podcatchern und Podcast Synchronisationsservern + verwendet. Weitere Details sind unter + "https://gpoddernet.readthedocs.io/en/latest/api/" zu finden} +} + +\newglossaryentry{json} +{ + name=JSON, + description={ + (JavaScript Object Notation) ist ein Datenformat und wird zur + Übertragung von Strukturen und Daten eingesetzt. JSON besteht dabei aus + grundlegenden Datentypen sowie Objekten mit Schlüssel-Wert Paaren und + Listen} +} + +\newglossaryentry{oauth} +{ + name=OAuth, + description={ + (Open Authorization) ist ein offenes Protokoll, welches es Nutzern + ermöglicht, sich mit bereits bestehenden Accounts bei anderen Diensten + zu registrieren. Dabei werden benötigte Daten für die Registrierung über + die bereitgestellte Schnittstelle zur Verfügung gestellt} +} + +\newglossaryentry{garbage-collection} +{ + name=Garbage Collection, + description={ + ist eine automatische Speicherbereinigung, welche nicht mehr benötigten + Speicherplatz wieder freigibt. Die Bereinigung kann dabei in determinierten + Zeitintervallen erfolgen oder durch bestimmte Ereignisse ausgelöst + werden} +} + +\newglossaryentry{salt-hash} +{ + name=Salting und Hashing, + description={ + ist eine Methode um Passwörter so zu kodieren, dass sie nicht als + Klartext gespeichert werden und auch sicher vor Hash-Wörterbüchern sind. + Dafür wird dem Passwort ein bekanntes Wort, der Salt, angefügt, bevor + aus dem kompletten Wort eine Prüfsumme, ein Hash, generiert wird. Beim + Anmelden wird die Prüfsumme der Anmeldung mit der bekannten + Prüfsumme des Passworts verglichen} +} + +\newglossaryentry{rss} +{ + name=RSS, + description={ + (Really Simple Syndication) zeigt strukturiert Listen von Nachrichten + an. Die Änderungen werden im XML-Format in sogenannte RSS-Dateien + geschrieben, welche über einen Link abgerufen werden können} +} + +\newglossaryentry{dsgvo} +{ + name=Datenschutz-Grundverordnung, + description={ + (DSGVO) ist eine im europäischen Wirtschaftsraum + geltende Verordnung. Sie sorgt für eine Reglementierung bei der + Verarbeitung personenbezogener Daten. Unter anderem muss einsehbar sein, + welche Daten von Nutzern erhoben werden. Außerdem muss für einen Nutzer + die Möglichkeit bestehen, seine erhobenen Daten abrufen zu können} +} + +\newglossaryentry{push-pull} +{ + name=Push und Pull, + description={ + sind Methoden, um Daten auszutauschen. Bei der Pull-Methode + stellt Akteur A einem Akteur B eine Anfrage auf Daten und erhält diese + als Antwort. Damit Akteur A und B immer auf dem selben Stand sind, muss + Akteur A chronisch Anfragen an Akteur B stellen. Im Gegensatz dazu steht + die Push-Methode, bei der Akteur B den Akteuren mitteilt, dass er neue + Änderungen hat. Dafür muss Akteur B allerdings wissen mit welchen + anderen Akteuren er in Verbindung steht und diese Verbindung aufrecht + erhalten} +} + +\newglossaryentry{ui-lib} +{ + name=UI-Bibliothek, + plural=UI-Bibliotheken, + description={ + kümmert sich um das Layout einer Webseite. Dabei unterscheidet man + zwischen Design-Bibliotheken (wie Bootstrap), welche fertige + UI-Komponenten bereitstellen, und Layout-Bibliotheken (wie Vue oder + React.js), welche die Komponenten basierend auf Daten dynamisch + anzeigen} +} + +\newglossaryentry{responsive} +{ + name=Responsive, + description={ + Design ist ein Design-Prinzip für Webseiten, bei dem die selbe Webseite ihre + Komponenten dynamisch der Bildschirmbreite anpasst} +} + +\newglossaryentry{pseudoprotocol} +{ + name=Pseudoprotokoll, + description={ + ist ein URL-Schema, auf das Webseiten hören können, wenn sie sich das + URL-Schema im Browser anmelden. Bekannt Pseudoprotokolle sind: + ,,mailto:'', ,,tel:'' oder ,,irc:''} +} + +\newglossaryentry{dashboard} +{ + name=Dashboard, + description={ + ist die erste Seite auf der man landet, wenn man angemeldet ist} +} + +\newglossaryentry{abo} +{ + name=Abonnement, + description={ + ist ein abonnierter Podcast} +} + +\newglossaryentry{discovery} +{ + name=Discovery, + description={ + ist ein Feature der Gpodder-API, welches dem Nutzer eine Reihe von + Podcasts zum abonnieren anbietet} +} + +\newglossaryentry{session-token} +{ + name=Session-Token, + description={ + ist ein Wort, dass vom Client gespeichert wird solange der Nutzer + eingeloggt ist und bei jeder Anfrage an den Server mitgeschickt wird. + Der Server kann den Session-Token einem Nutzer zuordnen und so mit + nutzerspezifischen Daten antworten} +} + +\newglossaryentry{cookie} +{ + name=Cookie, + description={ + ist ein kleiner webseitenspezifischer Speicher im Browser, welcher vom + Server und von der Webseite gesetzt werden kann und bei jeder weiteren + Anfrage an den Server mitgesendet wird. Cookies bleiben entweder + temporär im Browserspeicher, bis der Browser geschlossen wird oder + permanent, bis ein optionales Verfallsdatum erreicht ist} +} + +\newglossaryentry{uiComponent} +{ + name=UI-Komponente, + plural=UI-Komponenten, + description={ + In Vue.js werden die grafischen Elemente einer Webseite in einzelne + Komponenten zerteilt. + Diese reagieren automatisch auf Änderungen und können ohne Neuladen + der Seite ihr Aussehen verändern und somit Änderungen direkt anzeigen} +} + +\newglossaryentry{spring} +{ + name=Spring, + description={ + Ein Java-Framework, welches die Entwicklung von Web-Applikationen erleichtert. + Dazu wird eine Reihe von Werkzeugsets zur Verfügung gestellt. + Unter anderem sind das Spring Web für das Erstellen von Webanwendungen, + Spring Security für die Verwaltung von Benutzerauthentifizierungen und + Spring Data JPA für die Arbeit mit relationalen Datenbanken + } +} + +\newglossaryentry{api} +{ + name=API, + plural=APIs, + description={ + Eine Schnittstelle, welche es ermöglicht auf Funktionalitäten einer Anwendung + zuzugreifen. APIs für Webanwendungen heißen WebAPIs. + Ein Beispiel für eine WebAPI ist die REST-API + } +} + +\newglossaryentry{business} +{ + name=Geschäftslogik, + description={ + Eine Schicht in der Anwendungsentwicklung, in der die Art und Weise, wie das + Programm auf Eingaben reagiert, wie Daten verarbeitet und wie sie gespeichert + werden sollen, festgelegt ist + } +} + +\newglossaryentry{solid} +{ + name=SOLID, + description={ + Eine Sammlung an Prinzipien, welche zu gutem objektorientierten Design führen soll. + Jedes Prinzip steht für einen Buchstaben in SOLID: + \textbf{S}ingle-Responsibility Prinzip, + \textbf{O}pen-Closed Prinzip, + \textbf{L}iskovsches Substitutionsprinzip, + \textbf{I}nterface Segregation Prinzip und + \textbf{D}ependency Inversion Prinzip + } +} + +\newglossaryentry{crud} +{ + name=CRUD, + description={ + CRUD steht für \textbf{C}reate, \textbf{R}ead, \textbf{U}pdate und \textbf{D}elete. + Hierbei handelt es sich um die grundlegenden Funktionen einer Anwendung, + die mit einer Datenbank arbeitet. + Hierbei können Daten angelegt, abgerufen, aktualisiert und gelöscht werden. + Auch in Web-Applikationen ist CRUD mit HTTP über die Anfragen POST, GET, PUT und DELETE + vertreten + } +} + +\newglossaryentry{SQL} +{ + name=SQL, + description={ + SQL (Structured Query Language) ist eine Sprache, die einen strukturierten Zugriff auf Datenbanken ermöglicht. + Daten können hierbei hinzugefügt, abgefragt, geändert und gelöscht werden. + Das besondere hierbei ist der strukturierte Zugriff auf Daten, indem explizit Daten mit bestimmten Kriterien und + Relationen ausgewählt und bearbeitet werden können. + SQL wird fast von allen verbreiteten Datenbanksystemen unterstützt + } +} + +\newglossaryentry{Base64} +{ + name=Base64, + description={ + Mithilfe von Base64 können 8-Bit-Binärdaten in eine ASCII-Zeichenkette + kodiert werden. So werden zum Beispiel E-Mail-Anhänge versendet + } +} + +\newglossaryentry{JSONP} +{ + name=JSONP, + description={ + JSONP ermöglicht die Übertragung von JSON-Daten zwischen verschiedenen Domains. + Dies wäre durch die Same-Origin-Policy nicht möglich. + JSONP nutzt allerdings die Tatsache aus, + dass sich Skripte domainübergreifend übertragen lassen. + Dazu werden die JSON-Daten als Argument einer übergebenen Funktion über + ein Skript-Element eingebunden + } +} diff --git a/10-entwurfsheft/sections/structure.tex b/10-entwurfsheft/sections/structure.tex new file mode 100644 index 0000000..2a6ecb3 --- /dev/null +++ b/10-entwurfsheft/sections/structure.tex @@ -0,0 +1,104 @@ +\section{Entwurfsmuster und Techniken} + +\subsection{Entwurfsmuster} + +\subsubsection{Dependency Injection} + +Die Dependency Injection (dt. Abhängigkeitsinjektion) ist ein Entwurfsmuster, welches die Abhängigkeiten von Objekten bestimmt und an einem zentralen Ort speichert sowie verwaltet. +Sollte ein Objekt also von einem anderen Objekt abhängig sein, so wird an diesem zentralen Ort nach der Abhängigkeit gesucht. +Ist die Abhängigkeit vorhanden, so wird dieses Objekt dann an dem benötigten Ort eingesetzt (injiziert). +Dies geschieht während der Laufzeit. +Der zentrale Ort, an dem die Abhängigkeiten gespeichert werden, wird meist von einem Framework verwaltet. + +Im Falle dieses Projekts ist \Gls{spring} das Framework und der \Gls{spring} Container der zentrale Ort, an dem die Abhängigkeiten gespeichert werden. +Der Vorteil dieses Entwurfsmusters ist, dass Objekte von anderen Objekten abgekoppelt werden, sprich: Das Objekt mit der Abhängigkeit muss nicht mehr von der expliziten Klasse Kenntnis haben und es kann nur mit Interfaces gearbeitet werden, was in den \Gls{solid}-Kriterien das D für Dependency Inversion erfüllt. +Ein weiterer Vorteil ist, dass die Abhängigkeiten innerhalb einer Konfigurationsdatei definiert werden können. +Sprich: Man kann mehrere Implementierungen besitzen, die alle das gleiche Interface implementieren und kann in der Konfigurationsdatei angeben, welche Implementierung gewählt werden soll. + +\subsubsection{Data Access Object (DAO)} +\label{DAO_Pattern} + +Das Data Access Object (kurz: DAO, dt: Datenzugriffsobjekt) ist ein Entwurfsmuster, das eingesetzt wird um den Zugriff auf \Gls{db}en zu vereinfachen und die \Gls{business} von der Datenzugriffslogik zu trennen. +Dazu gibt es zwei Komponenten: das DAO-Interface und die DAO-Implementierung. + +Das DAO-Interface wird von allen DAO-Implementierungen implementiert und bietet alle Datenzugriffsfunktionen an, auf die die \Gls{business} zugreift. +Die DAO-Implementierung ist eine Klasse, die das DAO-Interface implementiert und den tatsächlichen Zugriff auf die \Gls{db} ausführt. + +Der Vorteil dieses Entwurfsmusters ist es mehrere Implementierungen desselben DAO-Interfaces zu besitzen. +In Kombination mit der Dependency Injection ist es einfach zwischen den Implementierungen für verschiedene \Gls{db}en (bspw. MariaDB und My\Gls{SQL}) zu wechseln. +Damit wird der Datenzugriff flexibler. +Im Falle dieses Projekts wird eine DAO-Implementierung für MariaDB verwendet. + +Ein weiterer Vorteil ist die zuvor angesprochene Trennung der Geschäfts- und Datenzugriffslogik. +Da sich die \Gls{business} und Datenzugriffslogik mithilfe dieses Musters in verschiedenen Komponenten befinden, sind diese voneinander getrennt und es wird einfacher die jeweiligen Implementierungen zu testen. +Gleichzeitig verbessert sich damit die Wiederverwendbarkeit des Codes, da die DAO-Implementierungen in anderen Programmen, die mit demselben DAO-Interface arbeiten, eingesetzt werden können. + +Damit erfüllt das DAO-Muster die Kriterien S und O der \Gls{solid}-Kriterien. +Das Single-Responsibility Prinzip wird erfüllt, da der Zugriff auf die \Gls{db} von der \Gls{business} getrennt wird und damit die DAO-Implementierung alleine für den Zugriff auf die \Gls{db} verantwortlich ist. +Das Open/Closed Prinzip wird erfüllt, da die DAO-Implementierung erweitert werden kann, ohne dass der Rest vom Projekt betroffen wird und außerhalb der Klasse nur mit dem DAO-Interface gearbeitet werden kann. + +\subsection{Techniken} + +\subsubsection{JSON Web Token} + +\Gls{json} Web Token (JWT) ist ein offener Standard der in RFC 7519 definiert wird. +Mit einem JWT ist es möglich Informationen sicher in einem kodierten \Gls{json} Objekt zu übertragen. +Die Sicherheit der Daten wird dabei durch eine digitale Signatur gewährleistet. + +Ein JWT besteht aus drei durch Punkte ('.') voneinander getrennten Teilen: +\texttt{Header}, \texttt{Payload} und \texttt{Signatur}. +Der \texttt{Header} besteht dabei typischerweise aus der Information um welchen Typ von Token es sich handelt, also einen JWT, +und der Information welcher Signierungs-Algorithmus verwendet wird. +Diese Informationen werden \Gls{Base64} kodiert und bilden den ersten Teil des JWT. + +Im \texttt{Payload} Teil werden die eigentlichen Informationen \Gls{Base64} kodiert. + +Die \texttt{Signatur} ergibt sich durch die mit einem Punkt voneinander getrennten Kodierungen des \texttt{Headers} und +des \texttt{Payload}-Teils. Diese Zeichenkette wird dann mit einem geheimen Schlüssel +durch den im \texttt{Header} angegebenen Signierungs-Algorithmus signiert. +JWT werden einmalig vom Server erzeugt und beim Client gehalten. Daher ist es +nicht notwendig wie z.B. bei \gls{cookie} basierten Sessions, eine Liste mit gültigen Sessions auf dem Server zu verwalten, was bei mehreren Servern schwierig ist. + +In diesem Projekt werden JWT zur Verifikation der E-Mail-Adresse eines Benutzers und zur Überprüfung der Autorisation bei Anfragen an den Server verwendet. + +Zur Bestätigung der E-Mail-Adresse speichert der Server die zur Verifikation des +Benutzers benötigten Daten in einem JWT. Dieser wird in der URL des Verifikations-Links kodiert. Wenn der Benutzer den Verifikations-Link anklickt, +wird der JWT an den Server weitergeleitet. Dieser überprüft die Signatur des JWT ihn mit seinem geheimen Schlüssel +und kann so die Verifikation der E-Mail-Adresse abschließen. + +Bei dem Login-Vorgang sendet der Client zuerst seine Anmeldedaten (Benutername und Passwort), um sich zu authentifizieren. +Der Server überprüft die Angaben, generiert einen JWT und gibt diesen zurück, falls die Daten korrekt sind. +Bei späteren Anfragen an den Server übermittelt der Client diesen JWT. Der Server überprüft die Validität des JWT und trifft +basierend darauf die Entscheidung, ob die Anfrage bearbeitet oder abgelehnt wird. + +\subsubsection{Objektrelationale Abbildung (Object-relational mapping)}\label{t:orm} + +Objektrelationale Abbildung - kurz ORM von der englischen Bezeichnung \enquote{Object-relational mapping} - ist eine Technik der Softwareentwicklung. +Sie widmet sich dem, mit der persistenten Speicherung von Laufzeit-Objekten zusammenhängenden \enquote{Impedance mismatch} Problem. +Dieses beschreibt die Diskrepanz zwischen den in der Pogrammier- beziehungsweise \Gls{db}welt vorherrschenden Konzepten - nämlich der objektorientierten Programmierung und relationalen \Gls{db}en. +So speichern objektorientierte Programmiersprachen Daten und Verhalten in Objekten mit eindeutiger Identität, wobei Zustand und Verhalten hinter einer Schnittstelle verborgen werden. +Relationale \Gls{db}en hingegen speichern Daten in Tabellen und basieren auf dem mathematischen Konzept der Relationenalgebra. + +Objektrelationale Abbildung bietet eine Möglichkeit diese Diskrepanz zu vermindern, indem sie ein Mapping zwischen Objekten und Datenstrukturen relationaler \Gls{db}en herstellt. +Einem, in einer objektorientierten Programmiersprache geschriebenen, Anwendungsprogramm erscheint dann die verbundene relationale \Gls{db} als objektorientierte \Gls{db}. +Durch ORM wird also sowohl das Ablegen von Objekten mit Attributen und Methoden in relationale \Gls{db}en, als auch das Erzeugen von solchen Objekten aus entsprechenden Datensätzen ermöglicht. +Vorteilhaft ist daran außerdem, dass die objektorientierte Programmiersprache nicht erweitert werden muss. +Des Weiteren existiert für jede Umgebung ausgereifte Software für die Verwendung relationaler \Gls{db}en. +Allerdings ist der Schritt in Richtung objektorientiertem Ansatz immanent ein Schritt weg von den eigentlichen Stärken relationaler \Gls{db}en. + +Der grundlegende Ansatz ist die Abbildung von Klassen auf Tabellen, wobei die Spalten den Attributen und die Zeilen den Objekten der Klasse zugeordnet sind. +Dabei entspricht der Primärschlüssel der Tabelle der Objektidentität und Objektreferenzen werden mithilfe von zusätzlichen Fremdschlüsseln repräsentiert. +%Um Vererbung abzubilden gibt es drei grundlegende Möglichkeiten. +%Erst einmal kann für eine Vererbungshierarchie auch genau eine gemeinsame Tabelle mit allen Attributen verwendet werden, in der ein Diskriminator bestimmt, zu welcher Klasse ein Objekt gehört. +%Als zweite Option kann pro Unterklasse eine zusätzliche verknüpfte Tabelle eingeführt werden. +%Letztlich kann auch pro konkreter Klasse eine Tabelle verwendet werden, wobei die Tabelle für die abstrakte Basisklasse entfällt. + +Die von diesem Mapping betroffenen Klassen aus dem Model-Paket (\ref{p:model}) des Backends sind User, SubscriptionAction, Subscription, EpisodeAction und \Gls{episode}. + +Konkret für dieses Projekt findet ORM als Technik durch die Implementierung der Jakarta Persistence \Gls{api} (JPA) Anwendung. +Dafür wird das von \Gls{spring} zur Implementierung von JPA-basierten Datenzugriffsschichten bereitgestellte Modul \Gls{spring} Data JPA genutzt. +Als JPA-Implementation wiederum wird das Open-Source-Persistenz- und ORM-Framework Hibernate für \Gls{java} verwendet. +Dabei erfolgen Abfragen der persistierten Objekte über die Abfragesprache Jakarta Persistence Query Language (JPQL), welche dann mittels JDBC in den entsprechen \Gls{SQL}-Dialekt für MariaDB übersetzt. +%Hier sei angemerkt, dass JPQL eine Untermenge der Hibernate Query Language (HQL) ist. + +\newpage diff --git a/10-entwurfsheft/tikz-uml.sty b/10-entwurfsheft/tikz-uml.sty new file mode 100644 index 0000000..c6e8e0d --- /dev/null +++ b/10-entwurfsheft/tikz-uml.sty @@ -0,0 +1,5377 @@ +%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% Start of tikz-uml.sty +% +% Some macros for UML Diagrams. +% Home page of project : +% Author: Nicolas Kielbasiewicz +% Style from : +% Fixed by Nicolas Kielbasiewicz (nicolas.kielbasiewicz@ensta-paristech.fr) in march 2016 to compile with pgf 3.00 +%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +\NeedsTeXFormat{LaTeX2e}[1995/12/01]% +\ProvidesPackage{tikz-uml}[2011/01/26]% +% +\RequirePackage{etoolbox}% +\RequirePackage{ifthen}% +\RequirePackage{tikz}% +\RequirePackage{xstring}% +\RequirePackage{calc}% +\RequirePackage{pgfopts}% +\usetikzlibrary{backgrounds,arrows,shapes,fit,shadows,decorations.markings}% +% +\def\tikzumlPackageLayersNum{3}% +\pgfkeys{/tikzuml/options/.cd, packageLayers/.initial=3}% +\pgfkeys{/tikzuml/options/.cd, packageLayers/.store in=\tikzumlPackageLayersNum}% +\def\tikzumlStateLayersNum{3}% +\pgfkeys{/tikzuml/options/.cd, stateLayers/.initial=3}% +\pgfkeys{/tikzuml/options/.cd, stateLayers/.store in=\tikzumlStateLayersNum}% +\def\tikzumlFragmentLayersNum{3}% +\pgfkeys{/tikzuml/options/.cd, fragmentLayers/.initial=3}% +\pgfkeys{/tikzuml/options/.cd, fragmentLayers/.store in=\tikzumlFragmentLayersNum}% +\def\tikzumlComponentLayersNum{3}% +\pgfkeys{/tikzuml/options/.cd, componentLayers/.initial=3}% +\pgfkeys{/tikzuml/options/.cd, componentLayers/.store in=\tikzumlComponentLayersNum}% +% +\ProcessPgfOptions{/tikzuml/options}% +% +\def\pgfsetlayersArg{background}% +\pgfdeclarelayer{background}% +\newcounter{tikzumlPackageLayers}% +\loop \pgfdeclarelayer{package\thetikzumlPackageLayers}% + \xdef\pgfsetlayersArg{\pgfsetlayersArg,package\thetikzumlPackageLayers}% + \ifnum\tikzumlPackageLayersNum>\thetikzumlPackageLayers% + \stepcounter{tikzumlPackageLayers}% +\repeat% +% +\newcounter{tikzumlFragmentLayers}% +\loop \pgfdeclarelayer{fragment\thetikzumlFragmentLayers}% + \xdef\pgfsetlayersArg{\pgfsetlayersArg,fragment\thetikzumlFragmentLayers}% + \ifnum\tikzumlFragmentLayersNum>\thetikzumlFragmentLayers% + \stepcounter{tikzumlFragmentLayers}% +\repeat% +% +\newcounter{tikzumlStateLayers}% +\loop \pgfdeclarelayer{state\thetikzumlStateLayers}% + \xdef\pgfsetlayersArg{\pgfsetlayersArg,state\thetikzumlStateLayers}% + \ifnum\tikzumlStateLayersNum>\thetikzumlStateLayers% + \stepcounter{tikzumlStateLayers}% +\repeat% +% +\newcounter{tikzumlComponentLayers}% +\loop \pgfdeclarelayer{component\thetikzumlComponentLayers}% + \xdef\pgfsetlayersArg{\pgfsetlayersArg,component\thetikzumlComponentLayers}% + \ifnum\tikzumlComponentLayersNum>\thetikzumlComponentLayers% + \stepcounter{tikzumlComponentLayers}% +\repeat% +% +\pgfdeclarelayer{lifelines}% +\pgfdeclarelayer{activity}% +\pgfdeclarelayer{connections}% +\xdef\pgfsetlayersArg{\pgfsetlayersArg,lifelines,activity,connections,main}% +\pgfsetlayers{\pgfsetlayersArg}% +% +\pgfkeys{/tikzuml/.cd, text/.initial=black, draw/.initial=black, font/.initial=\small,% + x/.initial=0, y/.initial=0,% + package type/.initial=tikzumlEmpty, fill package/.initial=blue!20,% + class width/.initial=10ex, simple interface width/.initial=4ex, class type/.initial=class, fill class/.initial=yellow!20, fill template/.initial=yellow!2,% + narynode width/.initial=6ex,% + relation geometry/.initial=--, relation angle1/.initial=-30, relation angle2/.initial=30, relation loopsize/.initial=3em, relation weight/.initial=0.5, relation pos1/.initial=0.2, relation pos2/.initial=0.8, relation pos stereo/.initial=0.5,% + note width/.initial=3cm, fill note/.initial=green!20,% + fill system/.initial=white,% + fill usecase/.initial=blue!20,% + actor below/.initial=0.5cm,% + state join width/.initial=3ex,% + state decision width/.initial=3ex,% + state initial width/.initial=5ex,% + state final width/.initial=5.5ex,% + state enter width/.initial=5ex,% + state exit width/.initial=5ex,% + state end width/.initial=5ex,% + state history width/.initial=5ex,% + state deep history width/.initial=5ex,% + state width/.initial=8ex, fill state/.initial=yellow!20,% + object stereo/.initial=object, fill object/.initial=yellow!20,% + call dt/.initial=tikzumlEmpty, call padding/.initial=2, call type/.initial=synchron, fill call/.initial=white,% + fragment type/.initial=opt, fragment inner xsep/.initial=1, fragment inner ysep/.initial=1, fill fragment/.initial= none,% + create call dt/.initial=4,% + component width/.initial=8ex, fill component/.initial= yellow!20,% + required interface distance/.initial=2.5cm, required interface width/.initial=1em, required interface padding/.initial=1cm,% + provided interface distance/.initial=3cm, provided interface width/.initial=1em, provided interface padding/.initial=1cm,% + port width/.initial=1ex, fill port/.initial= yellow!20,% + fill assembly connector/.initial= white,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \errmessage{TIKZUML ERROR : in tikzuml global, invalid option \keyname}% + }% +}% +% +\pgfkeys{/tikzuml/.cd, text/.get=\tikzumlDefaultTextColor, draw/.get=\tikzumlDefaultDrawColor, font/.get=\tikzumlDefaultFont,% + x/.get=\tikzumlDefaultX, y/.get=\tikzumlDefaultY,% + package type/.get=\tikzumlPackageDefaultType, fill package/.get=\tikzumlPackageDefaultFillColor,% + class width/.get=\tikzumlClassDefaultWidth, simple interface width/.get=\tikzumlSimpleInterfaceDefaultWidth, class type/.get=\tikzumlClassDefaultType, fill class/.get=\tikzumlClassDefaultFillColor, fill template/.get=\tikzumlClassTemplateFillColorDefaultFillColor,% + narynode width/.get=\tikzumlNaryNodeDefaultWidth,% + relation geometry/.get=\tikzumlRelationDefaultGeometry, relation angle1/.get=\tikzumlRelationDefaultAngleO, relation angle2/.get=\tikzumlRelationDefaultAngleT, relation loopsize/.get=\tikzumlRelationDefaultLoopSize, relation weight/.get=\tikzumlRelationDefaultWeight, relation pos1/.get=\tikzumlRelationDefaultPosO, relation pos2/.get=\tikzumlRelationDefaultPosT, relation pos stereo/.get=\tikzumlRelationDefaultPosStereo,% + note width/.get=\tikzumlNoteDefaultWidth, fill note/.get=\tikzumlNoteDefaultFillColor,% + fill system/.get=\tikzumlSystemDefaultFillColor,% + fill usecase/.get=\tikzumlUseCaseDefaultFillColor,% + actor below/.get=\tikzumlActorDefaultBelow,% + state join width/.get=\tikzumlStateJoinDefaultWidth,% + state decision width/.get=\tikzumlStateDecisionDefaultWidth,% + state initial width/.get=\tikzumlStateInitialDefaultWidth,% + state final width/.get=\tikzumlStateFinalDefaultWidth,% + state enter width/.get=\tikzumlStateEnterDefaultWidth,% + state exit width/.get=\tikzumlStateExitDefaultWidth,% + state end width/.get=\tikzumlStateEndDefaultWidth,% + state history width/.get=\tikzumlStateHistoryDefaultWidth,% + state deep history width/.get=\tikzumlStateDeepHistoryDefaultWidth,% + state width/.get=\tikzumlStateDefaultWidth, fill state/.get=\tikzumlStateDefaultFillColor,% + object stereo/.get=\tikzumlObjectDefaultStereo, fill object/.get=\tikzumlObjectDefaultFillColor,% + call dt/.get=\tikzumlCallDefaultDT, call padding/.get=\tikzumlCallDefaultPadding, call type/.get=\tikzumlCallDefaultType, fill call/.get=\tikzumlCallDefaultFillColor,% + fragment type/.get=\tikzumlFragmentDefaultType, fragment inner xsep/.get=\tikzumlFragmentDefaultXSep, fragment inner ysep/.get=\tikzumlFragmentDefaultYSep, fill fragment/.get=\tikzumlFragmentDefaultFillColor,% + create call dt/.get=\tikzumlCreateCallDefaultDT,% + component width/.get=\tikzumlComponentDefaultWidth, fill component/.get=\tikzumlComponentDefaultFillColor,% + required interface distance/.get=\tikzumlRequiredInterfaceDefaultDistance, required interface width/.get=\tikzumlRequiredInterfaceDefaultWidth, required interface padding/.get=\tikzumlRequiredInterfaceDefaultPadding,% + provided interface distance/.get=\tikzumlProvidedInterfaceDefaultDistance, provided interface width/.get=\tikzumlProvidedInterfaceDefaultWidth, provided interface padding/.get=\tikzumlProvidedInterfaceDefaultPadding,% + port width/.get=\tikzumlPortDefaultWidth, fill port/.get=\tikzumlPortDefaultFillColor,% + fill assembly connector/.get=\tikzumlAssemblyConnectorDefaultFillColor% +}% +% +% utility : change default colors +\newcommand{\tikzumlset}[1]{% + \pgfkeys{/tikzuml/.cd,#1}% + \pgfkeys{/tikzuml/.cd, text/.get=\tikzumlDefaultTextColor, draw/.get=\tikzumlDefaultDrawColor, font/.get=\tikzumlDefaultFont,% + x/.get=\tikzumlDefaultX, y/.get=\tikzumlDefaultY,% + package type/.get=\tikzumlPackageDefaultType, fill package/.get=\tikzumlPackageDefaultFillColor,% + class width/.get=\tikzumlClassDefaultWidth, simple interface width/.get=\tikzumlSimpleInterfaceDefaultWidth, class type/.get=\tikzumlClassDefaultType, fill class/.get=\tikzumlClassDefaultFillColor, fill template/.get=\tikzumlClassTemplateFillColorDefaultFillColor,% + narynode width/.get=\tikzumlNaryNodeWidth,% + relation geometry/.get=\tikzumlRelationDefaultGeometry, relation angle1/.get=\tikzumlRelationDefaultAngleO, relation angle2/.get=\tikzumlRelationDefaultAngleT, relation loopsize/.get=\tikzumlRelationDefaultLoopSize, relation weight/.get=\tikzumlRelationDefaultWeight, relation pos1/.get=\tikzumlRelationDefaultPosO, relation pos2/.get=\tikzumlRelationDefaultPosT, relation pos stereo/.get=\tikzumlRelationDefaultPosStereo,% + note width/.get=\tikzumlNoteDefaultWidth, fill note/.get=\tikzumlNoteDefaultFillColor,% + fill system/.get=\tikzumlSystemDefaultFillColor,% + fill usecase/.get=\tikzumlUseCaseDefaultFillColor,% + actor below/.get=\tikzumlActorDefaultBelow,% + state join width/.get=\tikzumlStateJoinDefaultWidth,% + state decision width/.get=\tikzumlStateDecisionDefaultWidth,% + state initial width/.get=\tikzumlStateInitialDefaultWidth,% + state final width/.get=\tikzumlStateFinalDefaultWidth,% + state enter width/.get=\tikzumlStateEnterDefaultWidth,% + state exit width/.get=\tikzumlStateExitDefaultWidth,% + state end width/.get=\tikzumlStateEndDefaultWidth,% + state history width/.get=\tikzumlStateHistoryDefaultWidth,% + state deep history width/.get=\tikzumlStateDeepHistoryDefaultWidth,% + state width/.get=\tikzumlStateDefaultWidth, fill state/.get=\tikzumlStateDefaultFillColor,% + object stereo/.get=\tikzumlObjectDefaultStereo, fill object/.get=\tikzumlObjectDefaultFillColor,% + call dt/.get=\tikzumlCallDefaultDT, call padding/.get=\tikzumlCallDefaultPadding, call type/.get=\tikzumlCallDefaultType, fill call/.get=\tikzumlCallDefaultFillColor,% + fragment type/.get=\tikzumlFragmentDefaultType, fragment inner xsep/.get=\tikzumlFragmentDefaultXSep, fragment inner ysep/.get=\tikzumlFragmentDefaultYSep, fill fragment/.get=\tikzumlFragmentDefaultFillColor,% + create call dt/.get=\tikzumlCreateCallDT,% + component width/.get=\tikzumlComponentDefaultWidth, fill component/.get=\tikzumlComponentDefaultFillColor,% + required interface distance/.get=\tikzumlRequiredInterfaceDefaultDistance, required interface width/.get=\tikzumlRequiredInterfaceDefaultWidth, required interface padding/.get=\tikzumlRequiredInterfaceDefaultPadding,% + provided interface distance/.get=\tikzumlProvidedInterfaceDefaultDistance, provided interface width/.get=\tikzumlProvidedInterfaceDefaultWidth, provided interface padding/.get=\tikzumlProvidedInterfaceDefaultPadding,% + port width/.get=\tikzumlPortDefaultWidth, fill port/.get=\tikzumlPortDefaultFillColor,% + fill assembly connector/.get=\tikzumlAssemblyConnectorDefaultFillColor% + }% +}% +% +% define a point +% arg : node/coordinates of the point +\newcommand{\umlpoint}[1]{% + \begin{pgfonlayer}{connections}% + \node[tikzuml control nodes style] at (#1) {};% + \end{pgfonlayer}% +}% +% +\newcommand{\tikzumlskipescape}[3][_]{% +\begingroup% + \def\_{#1}\edef\x{\endgroup% + \def\noexpand\csname #3\endcsname{#2}}\x% +}% +% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% class diagrams % +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +\pgfkeys{/tikzuml/relation/.cd, attr1/.style args={#1|#2}{arg1=#1, mult1=#2},% + attr2/.style args={#1|#2}{arg2=#1, mult2=#2},% + attr/.style args={#1|#2}{arg=#1, mult=#2},% + recursive/.style args={#1|#2|#3}{angle1=#1, angle2=#2, loopsize=#3},% + anchors/.style args={#1 and #2}{anchor1=#1, anchor2=#2},% + recursive direction/.style args={#1 to #2}{recursive direction start=#1, recursive direction end=#2}% +}% +% +\pgfkeys{/tikzuml/note/.cd, anchors/.style args={#1 and #2}{anchor1=#1, anchor2=#2}}% +% +\tikzstyle{tikzuml simpleclass style}=[rectangle, minimum height=2em, node distance=2em]% +\tikzstyle{tikzuml simpleinterface style}=[circle, minimum height=1em, node distance=1em]% +\tikzstyle{tikzuml class style}=[rectangle split, rectangle split parts=3, rectangle split part align={center, left, left}, minimum height=2em, node distance=2em]% +\tikzstyle{tikzuml narynode style}=[diamond]% +\tikzstyle{tikzuml template style}=[dashed, inner ysep=0.5em, inner xsep=1ex]% +\tikzstyle{tikzuml control nodes style}=[fill=black, inner sep=1.5pt, circle]% +% +\tikzstyle{tikzuml association style}=[color=\tikzumlDefaultDrawColor, -]% +\tikzstyle{tikzuml bidirectional association style}=[color=\tikzumlDefaultDrawColor, angle45-angle45]% +\tikzstyle{tikzuml unidirectional association style}=[color=\tikzumlDefaultDrawColor, -angle 45]% +\tikzstyle{tikzuml aggregation style}=[color=\tikzumlDefaultDrawColor, open diamond-]% +\tikzstyle{tikzuml unidirectional aggregation style}=[color=\tikzumlDefaultDrawColor, open diamond-angle 45]% +\tikzstyle{tikzuml composition style}=[color=\tikzumlDefaultDrawColor, diamond-]% +\tikzstyle{tikzuml unidirectional composition style}=[color=\tikzumlDefaultDrawColor, diamond-angle 45]% +\tikzstyle{tikzuml nesting style}=[color=\tikzumlDefaultDrawColor]% +\tikzstyle{tikzuml dependency style}=[color=\tikzumlDefaultDrawColor, -angle 45, dashed]% +\tikzstyle{tikzuml import style}=[color=\tikzumlDefaultDrawColor, -angle 45, dashed]% +\tikzstyle{tikzuml inherit style}=[color=\tikzumlDefaultDrawColor, -open triangle 45]% +\tikzstyle{tikzuml implements style}=[color=\tikzumlDefaultDrawColor, -open triangle 45, dashed]% +% +\pgfkeys{/tikzuml/assemblyconnectorrelation/.cd, anchors/.style args={#1 and #2}{anchor1=#1, anchor2=#2}}% +% +\newcounter{tikzumlPackageClassNum}% +\newcounter{tikzumlPackageSubPackageNum}% +\newcounter{tikzumlRelationNum}% +\setcounter{tikzumlRelationNum}{1}% +\newcounter{tikzumlNoteNum}% +\setcounter{tikzumlNoteNum}{1}% +% +\newcounter{pos}% +\newcounter{posT}% +\newcounter{posStereo}% +% +\newcounter{tikzumlPackageLevel}% +\setcounter{tikzumlPackageLevel}{0}% +% +\newif\iftikzumlpackageSimpleStyle% +\newif\iftikzumlclassSimpleStyle% +\newif\iftikzumlclassCircleShape% +\newif\iftikzumlpackageWithoutCoords% +\newif\iftikzumlclassWithoutCoords% +\newif\iftikzumlassocclassWithoutCoords% +\newif\iftikzumlnoteWithoutCoords% +% +% define a uml package +% arg : package name +% optional : x, y: coordinates of the package +% type: stereotype of the package +% name: name of the package node +% draw, fill, text: colors +% style: to manage every default TikZ option +% no coords: to tell that the package position is defined relatively +% to another node (automatically used with TikZ options above, below, left, right, below left, ...) +\newenvironment{umlpackage}[2][]{% + \pgfkeys{/tikzuml/package/.cd, x/.initial=\tikzumlDefaultX, y/.initial=\tikzumlDefaultX, style/.style={},% + name/.initial=tikzumlEmpty, type/.initial=\tikzumlPackageDefaultType, draw/.initial=\tikzumlDefaultDrawColor,% + fill/.initial=\tikzumlPackageDefaultFillColor, text/.initial=\tikzumlDefaultTextColor,% + no coords/.is if=tikzumlpackageWithoutCoords,% + no coords=false,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{above}\OR% + \equal{\keyname}{left}\OR% + \equal{\keyname}{below}\OR% + \equal{\keyname}{right}\OR% + \equal{\keyname}{above left}\OR% + \equal{\keyname}{above right}\OR% + \equal{\keyname}{below left}\OR% + \equal{\keyname}{below right}}{% + \IfSubStr{\keyvalue}{ of }{% + \pgfkeys{/tikzuml/package/.cd, no coords}% + }{}% + }{}% + \ifx\keyvalue\pgfkeysnovalue% + \pgfkeys{/tikzuml/package/.cd, style/.append style/.expand once={\keyname}}% + \else% + \pgfkeys{/tikzuml/package/.cd, style/.append style/.expand twice={\expandafter\keyname\expandafter=\keyvalue}}% + \fi% + %\errmessage{TIKZUML ERROR : in umlpackage, invalid option \keyname}% + }% + }% + \pgfkeys{/tikzuml/package/.cd, #1}% + \pgfkeys{/tikzuml/package/.cd, x/.get=\tikzumlPackageXShift, y/.get=\tikzumlPackageYShift, name/.get=\tikzumlPackageName, type/.get=\tikzumlPackageTypeTmp,% + draw/.get=\tikzumlPackageDrawColor, fill/.get=\tikzumlPackageFillColor,% + text/.get=\tikzumlPackageTextColor% + }% + % + + % + \ifthenelse{\equal{\tikzumlPackageTypeTmp}{tikzumlEmpty}}{% + \def\tikzumlPackageType{}% + }{% + \expandafter\def\expandafter\tikzumlPackageType\expandafter{$\ll$\tikzumlPackageTypeTmp$\gg$ \\}% + }% + % + \ifnum\thetikzumlPackageLevel>0% + \let\tikzumlPackage@nameold\tikzumlPackage@fitname% + \def\tikzumlPackage@name{#2}% + \begingroup% + \def\_{@}\edef\x{\endgroup% + \def\noexpand\tikzumlPackage@fitname{\tikzumlPackage@name}}\x% + \let\tikzumlPackage@parentold\tikzumlPackage@parent% + \edef\tikzumlPackage@parent{\tikzumlPackage@parentold @@\tikzumlPackage@nameold}% + \else% + \def\tikzumlPackage@parent{}% + \def\tikzumlPackage@name{#2}% + \begingroup% + \def\_{@}\edef\x{\endgroup% + \def\noexpand\tikzumlPackage@fitname{\tikzumlPackage@name}}\x% + \fi% + % + \let\tikzumlPackage@nodeNameold\tikzumlPackage@nodeName% + % + \begingroup% + \def\_{_}\edef\x{\endgroup% + \def\noexpand\tikzumlPackage@nodeName{\tikzumlPackage@name}}\x% + % + \ifthenelse{\equal{\tikzumlPackageName}{tikzumlEmpty}}{}{% + \def\tikzumlPackage@nodeName{\tikzumlPackageName}% + }% + % + \StrSubstitute{\tikzumlPackage@nodeName}{.}{@POINT@}{\tikzumlPackage@nodeName}% + % + \expandafter\gdef\csname tikzumlPackageFit\tikzumlPackage@parent @@\tikzumlPackage@fitname\endcsname{}% + % + \setcounter{tikzumlPackageClassNum}{0}% + \setcounter{tikzumlPackageSubPackageNum}{0}% + \stepcounter{tikzumlPackageLevel}% + % + \begin{scope}[xshift=\tikzumlPackageXShift cm, yshift=\tikzumlPackageYShift cm]% +}{% + \addtocounter{tikzumlPackageLevel}{-1}% + \begin{pgfonlayer}{package\thetikzumlPackageLevel}% + % + % if contains no class, and not simple, one define a fictive node to enable the fit option + \ifnum\c@tikzumlPackageClassNum=0% + \ifnum\c@tikzumlPackageSubPackageNum=0% + \iftikzumlpackageWithoutCoords% + \node[inner sep=1.5ex, /tikzuml/package/style] (\tikzumlPackage@nodeName-root) {\phantom{\tikzumlPackage@nodeName}};% + \else% + \node[inner sep=1.5ex, /tikzuml/package/style] (\tikzumlPackage@nodeName-root) at (0,0) {\phantom{\tikzumlPackage@nodeName}};% + \fi% + \expandafter\xdef\csname tikzumlPackageFit\tikzumlPackage@parent @@\tikzumlPackage@fitname\endcsname{(\tikzumlPackage@nodeName-root)}% + \fi% + \fi% + % + \ifnum\c@tikzumlPackageLevel>0% + \def\tikzumlPackageFitTmp{\csname tikzumlPackageFit\tikzumlPackage@parent\endcsname}% + \expandafter\xdef\csname tikzumlPackageFit\tikzumlPackage@parent\endcsname{\tikzumlPackageFitTmp (\tikzumlPackage@nodeName) (\tikzumlPackage@nodeName-caption)}% + \stepcounter{tikzumlPackageSubPackageNum}% + \fi% + % + \node[draw=\tikzumlPackageDrawColor, fill=\tikzumlPackageFillColor, text=\tikzumlPackageTextColor, font=\tikzumlDefaultFont, inner sep=1.5ex, /tikzuml/package/style, fit = \csname tikzumlPackageFit\tikzumlPackage@parent @@\tikzumlPackage@fitname\endcsname] (\tikzumlPackage@nodeName) {};% + \node[draw=\tikzumlPackageDrawColor, fill=\tikzumlPackageFillColor, text=\tikzumlPackageTextColor, font=\tikzumlDefaultFont, minimum height=1.5em, outer ysep=-0.3, anchor=south west] (\tikzumlPackage@nodeName-caption) at (\tikzumlPackage@nodeName.north west) {\begin{tabular}{c} \tikzumlPackageType \textbf{\tikzumlPackage@name}\end{tabular}};% + \end{pgfonlayer}% + \end{scope}% +}% +% +% shortcut to define an empty package +\newcommand{\umlemptypackage}[2][]{\begin{umlpackage}[#1]{#2} \end{umlpackage}}% +% +% define a uml class +% args : name of the class +% attributes of the class +% operations of the class +% optional : x, y: coordinates of the class +% width: of the class node +% type: type of class (class, interface, typedef, enum) +% tags: tagged values of class +% template: template parameters +% simple: if used, class is empty and drawn with a rectangle +% circle: if used with simple, class is empty and drawn with a circle +% draw, fill, fill template, and text: colors +% style: to manage every default TikZ option +% no coords: to tell that the class position is defined relatively +% to another node (automatically used with TikZ options above, below, left, right, below left, ...) +\newcommand{\umlclass}[4][]{% + \pgfkeys{/tikzuml/class/.cd, x/.initial=\tikzumlDefaultX, y/.initial=\tikzumlDefaultX, width/.initial=\tikzumlClassDefaultWidth, type/.initial=\tikzumlClassDefaultType,% + tags/.initial={}, style/.style={},% + template/.initial={}, name/.initial=tikzumlEmpty,% + draw/.initial=\tikzumlDefaultDrawColor,% + fill template/.initial=\tikzumlClassTemplateFillColorDefaultFillColor,% + fill/.initial=\tikzumlClassDefaultFillColor,% + text/.initial=\tikzumlDefaultTextColor,% + simple/.is if=tikzumlclassSimpleStyle, circle/.is if=tikzumlclassCircleShape, no coords/.is if=tikzumlclassWithoutCoords,% + simple=false, circle=false, no coords=false,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{above}\OR% + \equal{\keyname}{left}\OR% + \equal{\keyname}{below}\OR% + \equal{\keyname}{right}\OR% + \equal{\keyname}{above left}\OR% + \equal{\keyname}{above right}\OR% + \equal{\keyname}{below left}\OR% + \equal{\keyname}{below right}}{% + \IfSubStr{\keyvalue}{ of }{% + \pgfkeys{/tikzuml/class/.cd, no coords}% + }{}% + }{}% + \ifx\keyvalue\pgfkeysnovalue% + \pgfkeys{/tikzuml/class/.cd, style/.append style/.expand once={\keyname}}% + \else% + \pgfkeys{/tikzuml/class/.cd, style/.append style/.expand twice={\expandafter\keyname\expandafter=\keyvalue}}% + \fi% + %\errmessage{TIKZUML ERROR : in umlclass, invalid option \keyname}% + }% + }% + \pgfkeys{/tikzuml/class/.cd,#1}% + % + \iftikzumlclassSimpleStyle% + \iftikzumlclassCircleShape% + \pgfkeys{/tikzuml/class/.cd, width/.initial=\tikzumlSimpleInterfaceDefaultWidth}% + \fi% + \fi% + % + \pgfkeys{/tikzuml/class/.cd, x/.get=\tikzumlClassX, y/.get=\tikzumlClassY, width/.get=\tikzumlClassMinimumWidth,% + type/.get=\tikzumlClassTypeTmp, tags/.get=\tikzumlClassTagsTmp, template/.get=\tikzumlClassTemplateFillColorParam,% + name/.get=\tikzumlClassName,% + draw/.get=\tikzumlClassDrawColor, fill/.get=\tikzumlClassFillColor,% + text/.get=\tikzumlClassTextColor, fill template/.get=\tikzumlClassTemplateFillColor% + }% + % + \ifthenelse{\equal{\tikzumlClassTypeTmp}{class}\OR\equal{\tikzumlClassTypeTmp}{abstract}}{% + \def\tikzumlClassType{}% + }{% + \expandafter\def\expandafter\tikzumlClassType\expandafter{$\ll$\tikzumlClassTypeTmp$\gg$ \\}% + }% + % + \ifthenelse{\equal{\tikzumlClassTagsTmp}{}}{% + \def\tikzumlClassTags{}% + }{% + \def\tikzumlClassTags{\\ \{\tikzumlClassTagsTmp\}}% + }% + % + \ifthenelse{\equal{\tikzumlClassTemplateFillColorParam}{}}{% + \def\tikzumlClassVPadding{}% + \def\tikzumlClassHPadding{}% + }{% + \def\tikzumlClassVPadding{\vspace{0.1em} \\}% + \def\tikzumlClassHPadding{\hspace{0.5ex} $ $}% + }% + % + \def\tikzumlClassName{#2}% + % + \begingroup% + \def\_{_}\edef\x{\endgroup% + \def\noexpand\tikzumlClassNodeName{\tikzumlClassName}}\x% + % + \ifthenelse{\equal{\tikzumlClassName}{tikzumlEmpty}}{}{% + \def\tikzumlClassNodeName{\tikzumlClassName}% + }% + % + \StrSubstitute{\tikzumlClassNodeName}{:}{@COLON@}[\tikzumlClassNodeName]% + \StrSubstitute{\tikzumlClassNodeName}{\_}{@UNDERSCORE@}[\tikzumlClassNodeName]% + % + \ifthenelse{\equal{\tikzumlClassTypeTmp}{abstract}}{% + \let\tikzumlClassNameOld\tikzumlClassName% + \def\tikzumlClassName{{\it \tikzumlClassNameOld}}% + }{}% + % + \def\tikzumlClassPos{\tikzumlClassX,\tikzumlClassY}% + \def\tikzumlClassAttributes{#3}% + \def\tikzumlClassOperations{#4}% + % + \iftikzumlclassSimpleStyle% + \iftikzumlclassWithoutCoords% + \iftikzumlclassCircleShape% + \node[tikzuml simpleinterface style, draw=\tikzumlClassDrawColor, fill=\tikzumlClassFillColor, text=\tikzumlClassTextColor, font=\tikzumlDefaultFont, minimum width=\tikzumlClassMinimumWidth, /tikzuml/class/style] (\tikzumlClassNodeName) {};% + \node[anchor=south] (\tikzumlClassNodeName-label) at (\tikzumlClassNodeName.north) {\begin{tabular}{c}\tikzumlClassVPadding \tikzumlClassType \tikzumlClassHPadding \textbf{\tikzumlClassName} \tikzumlClassHPadding \tikzumlClassTags \end{tabular}% + };% + \else% + \node[tikzuml simpleclass style, draw=\tikzumlClassDrawColor, fill=\tikzumlClassFillColor, text=\tikzumlClassTextColor, font=\tikzumlDefaultFont, minimum width=\tikzumlClassMinimumWidth, /tikzuml/class/style] (\tikzumlClassNodeName) {\begin{tabular}{c}\tikzumlClassVPadding \tikzumlClassType \tikzumlClassHPadding \textbf{\tikzumlClassName} \tikzumlClassHPadding \tikzumlClassTags \end{tabular}% + };% + \fi% + \else% + \iftikzumlclassCircleShape% + \node[tikzuml simpleinterface style, draw=\tikzumlClassDrawColor, fill=\tikzumlClassFillColor, text=\tikzumlClassTextColor, font=\tikzumlDefaultFont, minimum width=\tikzumlClassMinimumWidth, /tikzuml/class/style] (\tikzumlClassNodeName) at (\tikzumlClassPos) {}; + \node[anchor=south] (\tikzumlClassNodeName-label) at (\tikzumlClassNodeName.north){\begin{tabular}{c}\tikzumlClassVPadding \tikzumlClassType \tikzumlClassHPadding \textbf{\tikzumlClassName} \tikzumlClassHPadding \tikzumlClassTags \end{tabular}% + };% + \else% + \node[tikzuml simpleclass style, draw=\tikzumlClassDrawColor, fill=\tikzumlClassFillColor, text=\tikzumlClassTextColor, font=\tikzumlDefaultFont, minimum width=\tikzumlClassMinimumWidth, /tikzuml/class/style] (\tikzumlClassNodeName) at (\tikzumlClassPos) {\begin{tabular}{c}\tikzumlClassVPadding \tikzumlClassType \tikzumlClassHPadding \textbf{\tikzumlClassName} \tikzumlClassHPadding \tikzumlClassTags \end{tabular}% + };% + \fi% + \fi% + \else% + \iftikzumlclassWithoutCoords% + \node[tikzuml class style, draw=\tikzumlClassDrawColor, fill=\tikzumlClassFillColor, text=\tikzumlClassTextColor, font=\tikzumlDefaultFont, minimum width=\tikzumlClassMinimumWidth, /tikzuml/class/style] (\tikzumlClassNodeName) {\begin{tabular}{c}\tikzumlClassVPadding \tikzumlClassType \tikzumlClassHPadding \textbf{\tikzumlClassName} \tikzumlClassHPadding \tikzumlClassTags \end{tabular}% + \nodepart{second}% + \begin{tabular}{l}% + \tikzumlClassAttributes% + \end{tabular}% + \nodepart{third}% + \begin{tabular}{l}% + \tikzumlClassOperations% + \end{tabular}% + };% + \else% + \node[tikzuml class style, draw=\tikzumlClassDrawColor, fill=\tikzumlClassFillColor, text=\tikzumlClassTextColor, font=\tikzumlDefaultFont, minimum width=\tikzumlClassMinimumWidth, /tikzuml/class/style] (\tikzumlClassNodeName) at (\tikzumlClassPos) {\begin{tabular}{c}\tikzumlClassVPadding \tikzumlClassType \tikzumlClassHPadding \textbf{\tikzumlClassName} \tikzumlClassHPadding \tikzumlClassTags \end{tabular}% + \nodepart{second}% + \begin{tabular}{l}% + \tikzumlClassAttributes% + \end{tabular}% + \nodepart{third}% + \begin{tabular}{l}% + \tikzumlClassOperations% + \end{tabular}% + };% + \fi% + \fi% + % + \ifthenelse{\equal{\tikzumlClassTemplateFillColorParam}{}}{}{% + \draw (\tikzumlClassNodeName.north east) node[tikzuml template style, name=\tikzumlClassNodeName-template, draw=\tikzumlClassDrawColor, fill=\tikzumlClassTemplateFillColor, text=\tikzumlClassTextColor, font=\tikzumlDefaultFont] {\tikzumlClassTemplateFillColorParam};% + }% + % + % add to fit + \ifnum\c@tikzumlPackageLevel>0% + \edef\tikzumlPackageFitOld{\csname tikzumlPackageFit\tikzumlPackage@parent @@\tikzumlPackage@fitname\endcsname}% + \ifthenelse{\equal{\tikzumlClassTemplateFillColorParam}{}}{% + \expandafter\xdef\csname tikzumlPackageFit\tikzumlPackage@parent @@\tikzumlPackage@fitname\endcsname{\tikzumlPackageFitOld (\tikzumlClassNodeName)}% + }{% + \expandafter\xdef\csname tikzumlPackageFit\tikzumlPackage@parent @@\tikzumlPackage@fitname\endcsname{\tikzumlPackageFitOld (\tikzumlClassNodeName) (\tikzumlClassNodeName-template)}% + }% + \stepcounter{tikzumlPackageClassNum}% + \fi% + \ifnum\c@tikzumlComponentLevel>0% + \def\tikzumlComponentFitTmp{\csname tikzumlComponentFit\tikzumlComponent@parent @@\tikzumlComponent@fitname\endcsname}% + \ifthenelse{\equal{\tikzumlClassTemplateFillColorParam}{}}{% + \expandafter\xdef\csname tikzumlComponentFit\tikzumlComponent@parent @@\tikzumlComponent@fitname\endcsname{\tikzumlComponentFitTmp (\tikzumlClassNodeName)}% + }{% + \expandafter\xdef\csname tikzumlComponentFit\tikzumlComponent@parent @@\tikzumlComponent@fitname\endcsname{\tikzumlComponentFitTmp (\tikzumlClassNodeName) (\tikzumlClassNodeName-template)}% + }% + \stepcounter{tikzumlComponentSubComponentNum}% + \fi% +}% +% +% shortcuts for interface, enum and typedef environments +\newcommand{\umlabstract}[4][]{% + \pgfkeys{/tikzuml/class/.cd, .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{type}}{% + \errmessage{TIKZUML ERROR : in umlabstract, forbidden option type}% + }{}% + }% + }% + \pgfkeys{/tikzuml/class/.cd, #1}% + \umlclass[type=abstract,#1]{#2}{#3}{#4}% +}% +\newcommand{\umlinterface}[4][]{% + \pgfkeys{/tikzuml/class/.cd, .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{type}}{% + \errmessage{TIKZUML ERROR : in umlinterface, forbidden option type}% + }{}% + }% + }% + \pgfkeys{/tikzuml/class/.cd, #1}% + \umlclass[type=interface,#1]{#2}{#3}{#4}% +}% +\newcommand{\umltypedef}[4][]{% + \pgfkeys{/tikzuml/class/.cd, .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{type}}{% + \errmessage{TIKZUML ERROR : in umltypedef, forbidden option type}% + }{}% + }% + }% + \pgfkeys{/tikzuml/class/.cd, #1}% + \umlclass[type=typedef,#1]{#2}{#3}{#4}% +}% +\newcommand{\umlenum}[4][]{% + \pgfkeys{/tikzuml/class/.cd, .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{type}}{% + \errmessage{TIKZUML ERROR : in umlenum, forbidden option type}% + }{}% + }% + }% + \pgfkeys{/tikzuml/class/.cd, #1}% + \umlclass[type=enum,#1]{#2}{#3}{#4} +}% +% +% shortcut to define an empty class +\newcommand{\umlemptyclass}[2][]{\umlclass[#1]{#2}{}{}}% +\newcommand{\umlsimpleclass}[2][]{% + \pgfkeys{/tikzuml/class/.cd, .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{simple}}{% + \errmessage{TIKZUML ERROR : in umlsimpleclass, forbidden option simple}% + }{}% + }% + }% + \pgfkeys{/tikzuml/class/.cd, #1}% + \umlemptyclass[simple, #1]{#2}% +}% +% +\newcommand{\umlsimpleinterface}[2][]{% + \pgfkeys{/tikzuml/class/.cd, .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{simple}}{% + \errmessage{TIKZUML ERROR : in umlsimpleinterface, forbidden option simple}% + }{% + \ifthenelse{\equal{\keyname}{circle}}{% + \errmessage{TIKZUML ERROR : in umlsimpleinterface, forbidden option circle}% + }{}% + }% + }% + }% + \pgfkeys{/tikzuml/class/.cd, #1}% + \umlsimpleclass[circle, #1]{#2}% +}% +% underline the text for static arg +\newcommand{\umlstatic}[1]{\underline{#1}}% +\newcommand{\umlvirt}[1]{\textit{#1}}% +% +% define node for n-ary association +\newcommand{\umlNarynode}[2][]{% + \def\tikzumlNaryNodeAnchor{.north} + \def\tikzumlNaryNodeLabelPos{above} + \pgfkeys{/tikzuml/narynode/.cd, x/.initial=\tikzumlDefaultX, y/.initial=\tikzumlDefaultX, width/.initial=\tikzumlNaryNodeDefaultWidth, name/.initial=tikzumlEmpty,% + draw/.initial=\tikzumlDefaultDrawColor, fill/.initial=\tikzumlClassDefaultFillColor,% + text/.initial=\tikzumlDefaultTextColor, style/.style={},% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{above}}{% + \def\tikzumlNaryNodeAnchor{.north}% + \def\tikzumlNaryNodeLabelPos{above}% + }{% + \ifthenelse{\equal{\keyname}{above left}}{% + \def\tikzumlNaryNodeAnchor{.north west}% + \def\tikzumlNaryNodeLabelPos{above left}% + }{% + \ifthenelse{\equal{\keyname}{left}}{% + \def\tikzumlNaryNodeAnchor{.west}% + \def\tikzumlNaryNodeLabelPos{left}% + }{% + \ifthenelse{\equal{\keyname}{below left}}{% + \def\tikzumlNaryNodeAnchor{.south west}% + \def\tikzumlNaryNodeLabelPos{below left}% + }{% + \ifthenelse{\equal{\keyname}{below}}{% + \def\tikzumlNaryNodeAnchor{.south}% + \def\tikzumlNaryNodeLabelPos{below}% + }{% + \ifthenelse{\equal{\keyname}{below right}}{% + \def\tikzumlNaryNodeAnchor{.south east}% + \def\tikzumlNaryNodeLabelPos{below right}% + }{% + \ifthenelse{\equal{\keyname}{right}}{% + \def\tikzumlNaryNodeAnchor{.east}% + \def\tikzumlNaryNodeLabelPos{right}% + }{% + \ifthenelse{\equal{\keyname}{above right}}{% + \def\tikzumlNaryNodeAnchor{.north east}% + \def\tikzumlNaryNodeLabelPos{above right}% + }{% + \ifx\keyvalue\pgfkeysnovalue% + \pgfkeys{/tikzuml/narynode/.cd, style/.append style/.expand once={\keyname}}% + \else% + \pgfkeys{/tikzuml/narynode/.cd, style/.append style/.expand twice={\expandafter\keyname\expandafter=\keyvalue}}% + \fi% + %\errmessage{TIKZUML ERROR : in umlNarynode, invalid option \keyname}% + }% + }% + }% + }% + }% + }% + }% + }% + }% + }% + \pgfkeys{/tikzuml/narynode/.cd,#1}% + \pgfkeys{/tikzuml/narynode/.cd, x/.get=\tikzumlNaryNodeX, y/.get=\tikzumlNaryNodeY, width/.get=\tikzumlNaryNodeMinimumWidth,% + name/.get=\tikzumlNaryNodeName,% + draw/.get=\tikzumlNaryNodeDrawColor, fill/.get=\tikzumlNaryNodeFillColor,% + text/.get=\tikzumlNaryNodeTextColor% + }% + % + \def\tikzumlNaryName{#2}% + % + \ifthenelse{\equal{\tikzumlNaryNodeName}{tikzumlEmpty}}{% + \edef\tikzumlNaryNodeName{\tikzumlNaryName}% + }{% + \edef\tikzumlNaryNodeName{\tikzumlNaryNodeName}% + }% + % + \begingroup% + \def\_{_}\edef\x{\endgroup% + \def\noexpand\tikzumlNaryNode@nodeName{\tikzumlNaryNodeName}}\x% + % + \StrSubstitute{\tikzumlNaryNode@nodeName}{:}{@COLON@}[\tikzumlNaryNode@nodeName]% + \StrSubstitute{\tikzumlNaryNode@nodeName}{\_}{@UNDERSCORE@}[\tikzumlNaryNode@nodeName]% + % + \def\tikzumlNarynodePos{\tikzumlNaryNodeX,\tikzumlNaryNodeY}% + % + \node[tikzuml narynode style, draw=\tikzumlNaryNodeDrawColor, fill=\tikzumlNaryNodeFillColor, text=\tikzumlNaryNodeTextColor, font=\tikzumlDefaultFont, minimum width=\tikzumlNaryNodeMinimumWidth, minimum height=\tikzumlNaryNodeMinimumWidth, /tikzuml/narynode/style] (\tikzumlNaryNode@nodeName) at (\tikzumlNarynodePos) {};% + \draw (\tikzumlNaryNode@nodeName\tikzumlNaryNodeAnchor) node[\tikzumlNaryNodeLabelPos] {\tikzumlNaryName};% + % + % add to fit + \ifnum\c@tikzumlPackageLevel>0% + \edef\tikzumlPackageFitOld{\csname tikzumlPackageFit\tikzumlPackage@parent @@\tikzumlPackage@fitname\endcsname}% + \expandafter\xdef\csname tikzumlPackageFit\tikzumlPackage@parent @@\tikzumlPackage@fitname\endcsname{\tikzumlPackageFitOld (\tikzumlNaryNode@nodeName)}% + \stepcounter{tikzumlPackageClassNum}% + \fi% +}% +% +\newdimen\tikzumlNestingSymbolSize% +% +% main command to define a relation between two classes +% args : src class +% dest class +% optional : geometry: geometry of the line +% weight: barycentric weight of the middle part when geometry is a 3-line +% arm1, arm2: lengths of first or last part when geometry id a 3-line +% arg1, arg2, arg: name of the src/dest/dest side class type attribute defined by the relation +% mult1, mult2, mult: multiplicity of the src/dest/dest side class type attribute defined by the relation +% pos1, pos2, pos: position of the src/dest/dest side class type attribute defined by the relation +% align1, align2, align: text justification of the src/dest/dest side class type attribute defined by the relation +% anchor1, anchor2: src/dest anchors on linked classes +% angle1, angle2, loopsize: start angle, end angle and size of the relation (only if recursive) +% stereo: stereotype of the relation +% pos stereo: position of the stereotype on the relation +% style: style of the relation (association, aggregation, composition, inherit, ...) +% name: rootname used for naming nodes of the relation +% recursive mode: type of recursive arrow (transition for state diagrams, or default) +% recursive direction start/end: when transition relation, start/end directions of the relation arrow +\newcommand{\umlrelation}[3][]{% + \pgfkeys{/tikzuml/relation/.cd, geometry/.initial=\tikzumlRelationDefaultGeometry, weight/.initial=\tikzumlRelationDefaultWeight,% + arm1/.initial=auto, arm2/.initial=auto,% + arg1/.initial={}, arg2/.initial={}, arg/.initial={},% + mult1/.initial={}, mult2/.initial={}, mult/.initial={},% + pos1/.initial=\tikzumlRelationDefaultPosO, pos2/.initial=\tikzumlRelationDefaultPosT, pos/.initial=tikzumlEmpty,% + align1/.initial={}, align2/.initial={}, align/.initial={},% + anchor1/.initial=tikzumlEmpty, anchor2/.initial=tikzumlEmpty,% + angle1/.initial=\tikzumlRelationDefaultAngleO, angle2/.initial=\tikzumlRelationDefaultAngleT, loopsize/.initial=\tikzumlRelationDefaultLoopSize,% + stereo/.initial={}, pos stereo/.initial=\tikzumlRelationDefaultPosStereo,% + style/.initial=->, style2/.style={}, name/.initial=relation-\thetikzumlRelationNum,% + recursive mode/.initial=default, recursive direction start/.initial=right,% + recursive direction end/.initial=bottom,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{with port}% + \OR\equal{\keyname}{interface}% + \OR\equal{\keyname}{padding}% + \OR\equal{\keyname}{width}% + \OR\equal{\keyname}{first arm}% + \OR\equal{\keyname}{second arm}% + \OR\equal{\keyname}{middle arm}% + \OR\equal{\keyname}{last arm}% + \OR\equal{\keyname}{distance}}{}{% + \ifx\keyvalue\pgfkeysnovalue% + \pgfkeys{/tikzuml/relation/.cd, style2/.append style/.expand once={\keyname}}% + \else% + \pgfkeys{/tikzuml/relation/.cd, style2/.append style/.expand twice={\expandafter\keyname\expandafter=\keyvalue}}% + \fi% + %\errmessage{TIKZUML ERROR : in umlrelation, invalid option \keyname}% + }% + }% + }% + \pgfkeys{/tikzuml/relation/.cd,#1}% + \pgfkeys{/tikzuml/relation/.cd, geometry/.get=\tikzumlRelationGeometry, weight/.get=\tikzumlRelationWeight,% + arm1/.get=\tikzumlRelationArmO, arm2/.get=\tikzumlRelationArmT,% + arg1/.get=\tikzumlRelationAttrName, arg2/.get=\tikzumlRelationAttrNameTO, arg/.get=\tikzumlRelationAttrNameTT,% + mult1/.get=\tikzumlRelationMultiplicity, mult2/.get=\tikzumlRelationMultiplicityTO, mult/.get=\tikzumlRelationMultiplicityTT,% + pos1/.get=\tikzumlRelationPosition, pos2/.get=\tikzumlRelationPositionTO, pos/.get=\tikzumlRelationPositionTT,% + align1/.get=\tikzumlRelationAlign, align2/.get=\tikzumlRelationAlignTO, align/.get=\tikzumlRelationAlignTT,% + anchor1/.get=\tikzumlRelationSrcAnchor, anchor2/.get=\tikzumlRelationDestAnchor,% + angle1/.get=\tikzumlRelationStartAngle, angle2/.get=\tikzumlRelationEndAngle, loopsize/.get=\tikzumlRelationLoopSize,% + stereo/.get=\tikzumlRelationStereoType, pos stereo/.get=\tikzumlRelationPositionStereotype,% + style/.get=\tikzumlRelationStyle, name/.get=\tikzumlRelationName,% + recursive mode/.get=\tikzumlRelationRecursiveMode,% + recursive direction start/.get=\tikzumlRelationRecursiveDirectionStart,% + recursive direction end/.get=\tikzumlRelationRecursiveDirectionEnd% + }% + % + \def\tikzumlSrcClassName{#2}% + % + % managing \_ in class names for node names + \begingroup% + \def\_{_}\edef\x{\endgroup% + \def\noexpand\tikzumlSrcClassNodeName{\tikzumlSrcClassName}}\x% + % + \StrSubstitute{\tikzumlSrcClassNodeName}{:}{@COLON@}[\tikzumlSrcClassNodeName]% + \StrSubstitute{\tikzumlSrcClassNodeName}{\_}{@UNDERSCORE@}[\tikzumlSrcClassNodeName]% + % + \def\tikzumlDestClassName{#3}% + \begingroup% + \def\_{_}\edef\x{\endgroup% + \def\noexpand\tikzumlDestClassNodeName{\tikzumlDestClassName}}\x% + % + \StrSubstitute{\tikzumlDestClassNodeName}{:}{@COLON@}[\tikzumlDestClassNodeName]% + \StrSubstitute{\tikzumlDestClassNodeName}{\_}{@UNDERSCORE@}[\tikzumlDestClassNodeName]% + % + % managing alias keys + \def\tikzumlRelationAttrNameT{\tikzumlRelationAttrNameTO\tikzumlRelationAttrNameTT}% + \def\tikzumlRelationMultiplicityT{\tikzumlRelationMultiplicityTO\tikzumlRelationMultiplicityTT}% + \def\tikzumlRelationAlignT{\tikzumlRelationAlignTO\tikzumlRelationAlignTT}% + \def\posAttrName{}% + \def\posMultiplicity{}% + \def\posAttrNameT{}% + \def\posMultiplicityT{}% + % + \ifthenelse{\equal{\tikzumlRelationPositionTT}{tikzumlEmpty}}{% + \def\tikzumlRelationPositionT{\tikzumlRelationPositionTO}% + }{% + \def\tikzumlRelationPositionT{\tikzumlRelationPositionTT}% + }% + % + \def\attrAlign{}% + \def\multAlign{}% + \def\attrAlignT{}% + \def\multAlignT{}% + % + \ifthenelse{\equal{\tikzumlRelationAlign}{left}}{% + \def\attrAlign{above right}% + \def\multAlign{below right}% + }{% + \ifthenelse{\equal{\tikzumlRelationAlign}{right}}{% + \def\attrAlign{above left}% + \def\multAlign{below left}% + }{}% + }% + % + \ifthenelse{\equal{\tikzumlRelationAlignT}{left}}{% + \def\attrAlignT{above right}% + \def\multAlignT{below right}% + }{% + \ifthenelse{\equal{\tikzumlRelationAlignT}{right}}{% + \def\attrAlignT{above left}% + \def\multAlignT{below left}% + }{}% + }% + % + % def stereotype + \ifthenelse{\equal{\tikzumlRelationStereoType}{}}{% + \def\stereotype{}% + }{% + \def\stereotype{$\ll$\tikzumlRelationStereoType$\gg$}% + }% + + % def anchors macros + \ifthenelse{\equal{\tikzumlRelationSrcAnchor}{tikzumlEmpty}}{% + \def\tikzumlRelationSrcAnchor{}% + }{% + \let\tikzumlRelationSrcAnchorold\tikzumlRelationSrcAnchor% + \def\tikzumlRelationSrcAnchor{.\tikzumlRelationSrcAnchorold}% + }% + % + \ifthenelse{\equal{\tikzumlRelationDestAnchor}{tikzumlEmpty}}{% + \def\tikzumlRelationDestAnchor{}% + }{% + \let\tikzumlRelationDestAnchorold\tikzumlRelationDestAnchor% + \def\tikzumlRelationDestAnchor{.\tikzumlRelationDestAnchorold}% + }% + % + \setcounter{pos}{100*\real{\tikzumlRelationPosition}}% + \setcounter{posT}{100*\real{\tikzumlRelationPositionT}}% + \setcounter{posStereo}{100*\real{\tikzumlRelationPositionStereotype}}% + % + \pgfmathsetmacro{\tikzumlRelationWeightT}{1.0-\tikzumlRelationWeight}% + % + %\newcounter{tikzumlControlNodesNum}% + %\setcounter{tikzumlControlNodesNum}{0}% + % + \node[inner sep=0] (\tikzumlRelationName-middle) at (barycentric cs:\tikzumlSrcClassNodeName=\tikzumlRelationWeightT,\tikzumlDestClassNodeName=\tikzumlRelationWeight) {};% + % + % straight line + \ifthenelse{\equal{\tikzumlRelationGeometry}{--}}% + {% + \ifthenelse{\equal{\tikzumlSrcClassNodeName}{\tikzumlDestClassNodeName}}{% + \def\arcNum{1}% + \def\arcNumT{1}% + % + \ifthenelse{\equal{\tikzumlRelationRecursiveMode}{default}}{% + \xdef\tikzumlLastArc{node[pos=\tikzumlRelationPosition, \posAttrName, \attrAlign] {\tikzumlRelationAttrName}% + node[pos=\tikzumlRelationPosition, swap, \posMultiplicity, \multAlign] {\tikzumlRelationMultiplicity}% + node[pos=\tikzumlRelationPositionT, \posAttrNameT, \attrAlignT] {\tikzumlRelationAttrNameT}% + node[pos=\tikzumlRelationPositionT, swap, \posMultiplicityT, \multAlignT] {\tikzumlRelationMultiplicityT}% + node[pos=\tikzumlRelationPositionStereotype, anchor=center] {\stereotype} }% + \xdef\tikzumlPath{(\tikzumlSrcClassNodeName) edge[in=\tikzumlRelationEndAngle, out=\tikzumlRelationStartAngle, distance=\tikzumlRelationLoopSize] \tikzumlLastArc% + node[midway, inner sep=0, name=\tikzumlRelationName-1, anchor=center] {} (\tikzumlDestClassNodeName) }% + }{% + \ifthenelse{\equal{\tikzumlRelationRecursiveMode}{transition}}{% + \xdef\tikzumlFirstArc{node[midway, inner sep=0, name=\tikzumlRelationName-1, anchor=center] {}}% + \xdef\tikzumlMidOneArc{node[midway, inner sep=0, name=\tikzumlRelationName-3, anchor=center] {}}% + % + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionStart}{\tikzumlRelationRecursiveDirectionEnd}}{% + \def\numArcs{3}% + \xdef\tikzumlLastArc{node[midway, inner sep=0, name=\tikzumlRelationName-5, anchor=center] {}}% + % + \begin{pgfonlayer}{connections}% + \draw (\tikzumlSrcClassNodeName) edge[in=\tikzumlRelationEndAngle, out=\tikzumlRelationStartAngle, distance=\tikzumlRelationLoopSize, draw=none] % + node[midway, inner sep=0, name=\tikzumlRelationName-tmp, anchor=center] {} (\tikzumlDestClassNodeName);% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionStart}{right}\OR\equal{\tikzumlRelationRecursiveDirectionStart}{left}}{% + \node[inner sep=0, name=\tikzumlRelationName-2] at (\tikzumlSrcClassNodeName.\tikzumlRelationStartAngle -| \tikzumlRelationName-tmp) {};% + \node[inner sep=0, name=\tikzumlRelationName-4] at (\tikzumlDestClassNodeName.\tikzumlRelationEndAngle -| \tikzumlRelationName-tmp) {};% + }{% + \node[inner sep=0, name=\tikzumlRelationName-2] at (\tikzumlSrcClassNodeName.\tikzumlRelationStartAngle |- \tikzumlRelationName-tmp) {};% + \node[inner sep=0, name=\tikzumlRelationName-4] at (\tikzumlDestClassNodeName.\tikzumlRelationEndAngle |- \tikzumlRelationName-tmp) {};% + }% + \end{pgfonlayer}% + }{% + \def\numArcs{4}% + \xdef\tikzumlMidTwoArc{node[midway, inner sep=0, name=\tikzumlRelationName-5, anchor=center] {}}% + \xdef\tikzumlLastArc{node[midway, inner sep=0, name=\tikzumlRelationName-7, anchor=center] {}}% + % + \begin{pgfonlayer}{connections}% + \draw (\tikzumlSrcClassNodeName) edge[in=\tikzumlRelationEndAngle, out=\tikzumlRelationStartAngle, distance=\tikzumlRelationLoopSize, draw=none] % + node[midway, name=\tikzumlRelationName-4, anchor=center] {} (\tikzumlDestClassNodeName);% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionStart}{right}\OR\equal{\tikzumlRelationRecursiveDirectionStart}{left}}{% + \node[inner sep=0, name=\tikzumlRelationName-2] at (\tikzumlSrcClassNodeName.\tikzumlRelationStartAngle -| \tikzumlRelationName-4) {};% + \node[inner sep=0, name=\tikzumlRelationName-6] at (\tikzumlDestClassNodeName.\tikzumlRelationEndAngle |- \tikzumlRelationName-4) {};% + }{% + \node[inner sep=0, name=\tikzumlRelationName-2] at (\tikzumlSrcClassNodeName.\tikzumlRelationStartAngle |- \tikzumlRelationName-4) {};% + \node[inner sep=0, name=\tikzumlRelationName-6] at (\tikzumlDestClassNodeName.\tikzumlRelationEndAngle -| \tikzumlRelationName-4) {};% + }% + \end{pgfonlayer}% + }% + % + \ifnum\numArcs=4% + \ifnum\theposStereo>300% + \pgfmathsetmacro{\tikzumlRelationPositionStereotype}{(\theposStereo-300)/100}% + \xdef\tikzumlLastArc{\tikzumlLastArc node[pos=\tikzumlRelationPositionStereotype, anchor=center] {\stereotype}}% + \else% + \ifnum\theposStereo<100% + \xdef\tikzumlFirstArc{\tikzumlFirstArc node[pos=\tikzumlRelationPositionStereotype, anchor=center] {\stereotype}}% + \else% + \ifnum\theposStereo>200% + \pgfmathsetmacro{\tikzumlRelationPositionStereotype}{(\theposStereo-200)/100}% + \xdef\tikzumlMidTwoArc{\tikzumlMidTwoArc node[pos=\tikzumlRelationPositionStereotype, anchor=center] {\stereotype}}% + \else% + \pgfmathsetmacro{\tikzumlRelationPositionStereotype}{(\theposStereo-100)/100}% + \xdef\tikzumlMidOneArc{\tikzumlMidOneArc node[pos=\tikzumlRelationPositionStereotype, anchor=center] {\stereotype}}% + \fi% + \fi% + \fi% + % + \ifthenelse{\thepos=300\OR\thepos=100}{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionStart}{right}}{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionEnd}{bottom}}{% + \def\posAttrName{above right}% + \def\posMultiplicity{below left}% + }{% + \def\posAttrName{above left}% + \def\posMultiplicity{below right}% + }% + }{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionStart}{left}}{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionEnd}{bottom}}{% + \def\posAttrName{above left}% + \def\posMultiplicity{below right}% + }{% + \def\posAttrName{above right}% + \def\posMultiplicity{below left}% + }% + }{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionStart}{top}}{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionEnd}{left}}{% + \def\posAttrName{above right}% + \def\posMultiplicity{below left}% + }{% + \def\posAttrName{above left}% + \def\posMultiplicity{below right}% + }% + }{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionEnd}{left}}{% + \def\posAttrName{above left}% + \def\posMultiplicity{below right}% + }{% + \def\posAttrName{above right}% + \def\posMultiplicity{below left}% + }% + }% + }% + }% + }{}% + % + \ifthenelse{\thepos=200}{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionStart}{right}}{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionEnd}{bottom}}{% + \def\posAttrName{above left}% + \def\posMultiplicity{below right}% + }{% + \def\posAttrName{above right}% + \def\posMultiplicity{below left}% + }% + }{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionStart}{left}}{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionEnd}{bottom}}{% + \def\posAttrName{above right}% + \def\posMultiplicity{below left}% + }{% + \def\posAttrName{above left}% + \def\posMultiplicity{below right}% + }% + }{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionStart}{top}}{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionEnd}{left}}{% + \def\posAttrName{above left}% + \def\posMultiplicity{below right}% + }{% + \def\posAttrName{above right}% + \def\posMultiplicity{below left}% + }% + }{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionEnd}{left}}{% + \def\posAttrName{above right}% + \def\posMultiplicity{below left}% + }{% + \def\posAttrName{above left}% + \def\posMultiplicity{below right}% + }% + }% + }% + }% + }{}% + % + \ifnum\thepos>300% + \pgfmathsetmacro{\tikzumlRelationPosition}{(\thepos-300)/100}% + \xdef\tikzumlLastArc{\tikzumlLastArc node[pos=\tikzumlRelationPosition, \posAttrName, \attrAlign] {\tikzumlRelationAttrName}% + node[pos=\tikzumlRelationPosition, swap, \posMultiplicity, \multAlign] {\tikzumlRelationMultiplicity}% + }% + \else% + \ifnum\thepos<100% + \xdef\tikzumlFirstArc{\tikzumlFirstArc node[pos=\tikzumlRelationPosition, \posAttrName, \attrAlign] {\tikzumlRelationAttrName}% + node[pos=\tikzumlRelationPosition, swap, \posMultiplicity, \multAlign] {\tikzumlRelationMultiplicity}% + }% + \else% + \ifnum\thepos>200% + \pgfmathsetmacro{\tikzumlRelationPosition}{(\thepos-200)/100}% + \xdef\tikzumlMidTwoArc{\tikzumlMidTwoArc node[pos=\tikzumlRelationPosition, \posAttrName, \attrAlign] {\tikzumlRelationAttrName}% + node[pos=\tikzumlRelationPosition, swap, \posMultiplicity, \multAlign] {\tikzumlRelationMultiplicity}% + }% + \else% + \pgfmathsetmacro{\tikzumlRelationPosition}{(\thepos-100)/100}% + \xdef\tikzumlMidOneArc{\tikzumlMidOneArc node[pos=\tikzumlRelationPosition, \posAttrName, \attrAlign] {\tikzumlRelationAttrName}% + node[pos=\tikzumlRelationPosition, swap, \posMultiplicity, \multAlign] {\tikzumlRelationMultiplicity}% + }% + \fi% + \fi% + \fi% + % + \ifthenelse{\theposT=300\OR\theposT=100}{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionStart}{right}}{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionEnd}{bottom}}{% + \def\posAttrNameT{above right}% + \def\posMultiplicityT{below left}% + }{% + \def\posAttrNameT{above left}% + \def\posMultiplicityT{below right}% + }% + }{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionStart}{left}}{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionEnd}{bottom}}{% + \def\posAttrNameT{above left}% + \def\posMultiplicityT{below right}% + }{% + \def\posAttrNameT{above right}% + \def\posMultiplicityT{below left}% + }% + }{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionStart}{top}}{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionEnd}{left}}{% + \def\posAttrNameT{above right}% + \def\posMultiplicityT{below left}% + }{% + \def\posAttrNameT{above left}% + \def\posMultiplicityT{below right}% + }% + }{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionEnd}{left}}{% + \def\posAttrNameT{above left}% + \def\posMultiplicityT{below right}% + }{% + \def\posAttrNameT{above right}% + \def\posMultiplicityT{below left}% + }% + }% + }% + }% + }{}% + \ifthenelse{\theposT=200}{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionStart}{right}}{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionEnd}{bottom}}{% + \def\posAttrNameT{above left}% + \def\posMultiplicity{below right}% + }{% + \def\posAttrNameT{above right}% + \def\posMultiplicityT{below left}% + }% + }{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionStart}{left}}{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionEnd}{bottom}}{% + \def\posAttrNameT{above right}% + \def\posMultiplicityT{below left}% + }{% + \def\posAttrNameT{above left}% + \def\posMultiplicityT{below right}% + }% + }{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionStart}{top}}{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionEnd}{left}}{% + \def\posAttrNameT{above left}% + \def\posMultiplicityT{below right}% + }{% + \def\posAttrNameT{above right}% + \def\posMultiplicityT{below left}% + }% + }{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionEnd}{left}}{% + \def\posAttrNameT{above right}% + \def\posMultiplicityT{below left}% + }{% + \def\posAttrNameT{above left}% + \def\posMultiplicityT{below right}% + }% + }% + }% + }% + }{}% + % + \ifnum\theposT>300% + \pgfmathsetmacro{\tikzumlRelationPositionT}{(\theposT-300)/100}% + \xdef\tikzumlLastArc{\tikzumlLastArc node[pos=\tikzumlRelationPositionT, \posAttrNameT, \attrAlignT] {\tikzumlRelationAttrNameT}% + node[pos=\tikzumlRelationPositionT, swap, \posMultiplicityT, \multAlignT] {\tikzumlRelationMultiplicityT}% + }% + \else% + \ifnum\theposT<100% + \xdef\tikzumlFirstArc{\tikzumlFirstArc node[pos=\tikzumlRelationPositionT, \posAttrNameT, \attrAlignT] {\tikzumlRelationAttrNameT}% + node[pos=\tikzumlRelationPositionT, swap, \posMultiplicityT, \multAlignT] {\tikzumlRelationMultiplicityT}% + }% + \else% + \ifnum\theposT>200% + \pgfmathsetmacro{\tikzumlRelationPositionT}{(\theposT-200)/100}% + \xdef\tikzumlMidTwoArc{\tikzumlMidTwoArc node[pos=\tikzumlRelationPositionT, \posAttrNameT, \attrAlignT] {\tikzumlRelationAttrNameT}% + node[pos=\tikzumlRelationPositionT, swap, \posMultiplicityT, \multAlignT] {\tikzumlRelationMultiplicityT}% + }% + \else% + \pgfmathsetmacro{\tikzumlRelationPositionT}{(\theposT-100)/100}% + \xdef\tikzumlMidOneArc{\tikzumlMidOneArc node[pos=\tikzumlRelationPositionT, \posAttrNameT, \attrAlignT] {\tikzumlRelationAttrNameT}% + node[pos=\tikzumlRelationPositionT, swap, \posMultiplicityT, \multAlignT] {\tikzumlRelationMultiplicityT}% + }% + \fi% + \fi% + \fi% + \else% + \ifnum\theposStereo>200% + \pgfmathsetmacro{\tikzumlRelationPositionStereotype}{(\theposStereo-200)/100}% + \xdef\tikzumlLastArc{\tikzumlLastArc node[pos=\tikzumlRelationPositionStereotype, anchor=center] {\stereotype} }% + \else% + \ifnum\theposStereo<100% + \xdef\tikzumlFirstArc{\tikzumlFirstArc node[pos=\tikzumlRelationPositionStereotype, anchor=center] {\stereotype} }% + \else% + \pgfmathsetmacro{\tikzumlRelationPositionStereotype}{(\theposStereo-100)/100}% + \xdef\tikzumlMidOneArc{\tikzumlMidOneArc node[pos=\tikzumlRelationPositionStereotype, anchor=center] {\stereotype} }% + \fi% + \fi% + % + \ifthenelse{\thepos=100}{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionStart}{right}}{% + \ifthenelse{\tikzumlRelationEndAngle<\tikzumlRelationStartAngle}{% + \def\posAttrName{above right}% + \def\posMultiplicity{below left}% + }{% + \def\posAttrName{above left}% + \def\posMultiplicity{below right}% + }% + }{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionStart}{left}}{% + \ifthenelse{\tikzumlRelationEndAngle<\tikzumlRelationStartAngle}{% + \def\posAttrName{above left}% + \def\posMultiplicity{below right}% + }{% + \def\posAttrName{above right}% + \def\posMultiplicity{below left}% + }% + }{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionStart}{top}}{% + \ifthenelse{\tikzumlRelationEndAngle<\tikzumlRelationStartAngle}{% + \def\posAttrName{above right}% + \def\posMultiplicity{below left}% + }{% + \def\posAttrName{above left}% + \def\posMultiplicity{below right}% + }% + }{% + \ifthenelse{\tikzumlRelationEndAngle<\tikzumlRelationStartAngle}{% + \def\posAttrName{above left}% + \def\posMultiplicity{below right}% + }{% + \def\posAttrName{above right}% + \def\posMultiplicity{below left}% + }% + }% + }% + }% + }{}% + % + \ifthenelse{\thepos=200}{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionStart}{right}}{% + \ifthenelse{\tikzumlRelationEndAngle<\tikzumlRelationStartAngle}{% + \def\posAttrName{above left}% + \def\posMultiplicity{below right}% + }{% + \def\posAttrName{above right}% + \def\posMultiplicity{below left}% + }% + }{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionStart}{left}}{% + \ifthenelse{\tikzumlRelationEndAngle<\tikzumlRelationStartAngle}{% + \def\posAttrName{above right}% + \def\posMultiplicity{below left}% + }{% + \def\posAttrName{above left}% + \def\posMultiplicity{below right}% + }% + }{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionStart}{top}}{% + \ifthenelse{\tikzumlRelationEndAngle<\tikzumlRelationStartAngle}{% + \def\posAttrName{above left}% + \def\posMultiplicity{below right}% + }{% + \def\posAttrName{above right}% + \def\posMultiplicity{below left}% + }% + }{% + \ifthenelse{\tikzumlRelationEndAngle<\tikzumlRelationStartAngle}{% + \def\posAttrName{above right}% + \def\posMultiplicity{below left}% + }{% + \def\posAttrName{above left}% + \def\posMultiplicity{below right}% + }% + }% + }% + }% + }{}% + % + \ifnum\thepos>200% + \pgfmathsetmacro{\tikzumlRelationPosition}{(\thepos-200)/100}% + \xdef\tikzumlLastArc{\tikzumlLastArc node[pos=\tikzumlRelationPosition, \posAttrName, \attrAlign] {\tikzumlRelationAttrName}% + node[pos=\tikzumlRelationPosition, swap, \posMultiplicity, \multAlign] {\tikzumlRelationMultiplicity}% + }% + \else% + \ifnum\thepos<100% + \xdef\tikzumlFirstArc{\tikzumlFirstArc node[pos=\tikzumlRelationPosition, \posAttrName, \attrAlign] {\tikzumlRelationAttrName}% + node[pos=\tikzumlRelationPosition, swap, \posMultiplicity, \multAlign] {\tikzumlRelationMultiplicity}% + }% + \else% + \pgfmathsetmacro{\tikzumlRelationPosition}{(\thepos-100)/100}% + \xdef\tikzumlMidOneArc{\tikzumlMidOneArc node[pos=\tikzumlRelationPosition, \posAttrName, \attrAlign] {\tikzumlRelationAttrName}% + node[pos=\tikzumlRelationPosition, swap, \posMultiplicity, \multAlign] {\tikzumlRelationMultiplicity}% + }% + \fi% + \fi% + % + \ifthenelse{\theposT=100}{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionStart}{right}}{% + \ifthenelse{\tikzumlRelationEndAngle<\tikzumlRelationStartAngle}{% + \def\posAttrNameT{above right}% + \def\posMultiplicityT{below left}% + }{% + \def\posAttrNameT{above left}% + \def\posMultiplicityT{below right}% + }% + }{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionStart}{left}}{% + \ifthenelse{\tikzumlRelationEndAngle<\tikzumlRelationStartAngle}{% + \def\posAttrNameT{above left}% + \def\posMultiplicityT{below right}% + }{% + \def\posAttrNameT{above right}% + \def\posMultiplicityT{below left}% + }% + }{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionStart}{top}}{% + \ifthenelse{\tikzumlRelationEndAngle<\tikzumlRelationStartAngle}{% + \def\posAttrNameT{above right}% + \def\posMultiplicityT{below left}% + }{% + \def\posAttrNameT{above left}% + \def\posMultiplicityT{below right}% + }% + }{% + \ifthenelse{\tikzumlRelationEndAngle<\tikzumlRelationStartAngle}{% + \def\posAttrNameT{above left}% + \def\posMultiplicityT{below right}% + }{% + \def\posAttrNameT{above right}% + \def\posMultiplicityT{below left}% + }% + }% + }% + }% + }{}% + % + \ifthenelse{\theposT=200}{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionStart}{right}}{% + \ifthenelse{\tikzumlRelationEndAngle<\tikzumlRelationStartAngle}{% + \def\posAttrNameT{above left}% + \def\posMultiplicityT{below right}% + }{% + \def\posAttrNameT{above right}% + \def\posMultiplicityT{below left}% + }% + }{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionStart}{left}}{% + \ifthenelse{\tikzumlRelationEndAngle<\tikzumlRelationStartAngle}{% + \def\posAttrNameT{above right}% + \def\posMultiplicityT{below left}% + }{% + \def\posAttrNameT{above left}% + \def\posMultiplicityT{below right}% + }% + }{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionStart}{top}}{% + \ifthenelse{\tikzumlRelationEndAngle<\tikzumlRelationStartAngle}{% + \def\posAttrNameT{above left}% + \def\posMultiplicityT{below right}% + }{% + \def\posAttrNameT{above right}% + \def\posMultiplicityT{below left}% + }% + }{% + \ifthenelse{\tikzumlRelationEndAngle<\tikzumlRelationStartAngle}{% + \def\posAttrNameT{above right}% + \def\posMultiplicityT{below left}% + }{% + \def\posAttrNameT{above left}% + \def\posMultiplicityT{below right}% + }% + }% + }% + }% + }{}% + % + \ifnum\theposT>200% + \pgfmathsetmacro{\tikzumlRelationPositionT}{(\theposT-200)/100}% + \xdef\tikzumlLastArc{\tikzumlLastArc node[pos=\tikzumlRelationPositionT, \posAttrNameT, \attrAlignT] {\tikzumlRelationAttrNameT}% + node[pos=\tikzumlRelationPositionT, swap, \posMultiplicityT, \multAlignT] {\tikzumlRelationMultiplicityT}% + }% + \else% + \ifnum\theposT<100% + \xdef\tikzumlFirstArc{\tikzumlFirstArc node[pos=\tikzumlRelationPositionT, \posAttrNameT, \attrAlignT] {\tikzumlRelationAttrNameT}% + node[pos=\tikzumlRelationPositionT, swap, \posMultiplicityT, \multAlignT] {\tikzumlRelationMultiplicityT}% + }% + \else% + \pgfmathsetmacro{\tikzumlRelationPositionT}{(\theposT-100)/100}% + \xdef\tikzumlMidOneArc{\tikzumlMidOneArc node[pos=\tikzumlRelationPositionT, \posAttrNameT, \attrAlignT] {\tikzumlRelationAttrNameT}% + node[pos=\tikzumlRelationPositionT, swap, \posMultiplicityT, \multAlignT] {\tikzumlRelationMultiplicityT}% + }% + \fi% + \fi% + \fi% + % + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionStart}{\tikzumlRelationRecursiveDirectionEnd}}{% + \xdef\tikzumlPath{(\tikzumlSrcClassNodeName.\tikzumlRelationStartAngle) -- \tikzumlFirstArc (\tikzumlRelationName-2.center) -- \tikzumlMidOneArc (\tikzumlRelationName-4.center) -- \tikzumlLastArc (\tikzumlDestClassNodeName.\tikzumlRelationEndAngle) }% + \ifnum\thetikzumlStateLevel>0% + \def\tikzumlStateFitTmp{\csname tikzumlStateFit\tikzumlState@parent @@\tikzumlState@fitname\endcsname}% + \expandafter\xdef\csname tikzumlStateFit\tikzumlState@parent @@\tikzumlState@fitname\endcsname{\tikzumlStateFitTmp (\tikzumlRelationName-1) (\tikzumlRelationName-2) (\tikzumlRelationName-3) (\tikzumlRelationName-4) (\tikzumlRelationName-5)}% + \fi% + }{% + \xdef\tikzumlPath{(\tikzumlSrcClassNodeName.\tikzumlRelationStartAngle) -- \tikzumlFirstArc (\tikzumlRelationName-2.center) -- \tikzumlMidOneArc (\tikzumlRelationName-4.center) -- \tikzumlMidTwoArc (\tikzumlRelationName-6.center) -- \tikzumlLastArc (\tikzumlDestClassNodeName.\tikzumlRelationEndAngle) }% + \ifnum\thetikzumlStateLevel>0% + \def\tikzumlStateFitTmp{\csname tikzumlStateFit\tikzumlState@parent @@\tikzumlState@fitname\endcsname}% + \expandafter\xdef\csname tikzumlStateFit\tikzumlState@parent @@\tikzumlState@fitname\endcsname{\tikzumlStateFitTmp (\tikzumlRelationName-1) (\tikzumlRelationName-2) (\tikzumlRelationName-3) (\tikzumlRelationName-4) (\tikzumlRelationName-5) (\tikzumlRelationName-6) (\tikzumlRelationName-7)}% + \fi% + }% + }{}% + }% + }{% + \def\arcNum{1}% + \def\arcNumT{1}% + % + \node[inner sep=0] (\tikzumlRelationName-1) at (\tikzumlRelationName-middle) {};% + \xdef\tikzumlLastArc{node[pos=\tikzumlRelationPosition, \posAttrName, \attrAlign] {\tikzumlRelationAttrName}% + node[pos=\tikzumlRelationPosition, swap, \posMultiplicity, \multAlign] {\tikzumlRelationMultiplicity}% + node[pos=\tikzumlRelationPositionT, \posAttrNameT, \attrAlignT] {\tikzumlRelationAttrNameT}% + node[pos=\tikzumlRelationPositionT, swap, \posMultiplicityT, \multAlignT] {\tikzumlRelationMultiplicityT}% + node[pos=\tikzumlRelationPositionStereotype, anchor=center] {\stereotype} }% + \xdef\tikzumlPath{(\tikzumlSrcClassNodeName\tikzumlRelationSrcAnchor) -- \tikzumlLastArc (\tikzumlDestClassNodeName\tikzumlRelationDestAnchor) }% + \ifnum\thetikzumlStateLevel>0% + \def\tikzumlStateFitTmp{\csname tikzumlStateFit\tikzumlState@parent @@\tikzumlState@fitname\endcsname}% + \expandafter\xdef\csname tikzumlStateFit\tikzumlState@parent @@\tikzumlState@fitname\endcsname{\tikzumlStateFitTmp (\tikzumlRelationName-1) }% + \fi% + }% + }{% + % first vertical then horizontal line + \ifthenelse{\equal{\tikzumlRelationGeometry}{|-}}% + {% + %\setcounter{tikzumlControlNodesNum}{1}% + % + \def\tikzumlFirstArc{node[midway, inner sep=0, name=\tikzumlRelationName-1, anchor=center] {} }% + \def\tikzumlLastArc{node[midway, inner sep=0, name=\tikzumlRelationName-3, anchor=center]{} }% + % + \begin{pgfonlayer}{connections}% + \node[inner sep=0] (\tikzumlRelationName-2) at (\tikzumlSrcClassNodeName\tikzumlRelationSrcAnchor |- \tikzumlDestClassNodeName\tikzumlRelationDestAnchor) {};% + \end{pgfonlayer}% + % + \ifnum\theposStereo>100% + \pgfmathsetmacro{\tikzumlRelationPositionStereotype}{(\theposStereo-100)/100}% + \xdef\tikzumlLastArc{\tikzumlLastArc node[pos=\tikzumlRelationPositionStereotype, anchor=center] {\stereotype} }% + \else% + \xdef\tikzumlFirstArc{\tikzumlFirstArc node[pos=\tikzumlRelationPositionStereotype, anchor=center] {\stereotype} }% + \fi% + % + \ifnum\thepos>100% + \pgfmathsetmacro{\tikzumlRelationPosition}{(\thepos-100)/100}% + \def\arcNum{2}% + \else% + \def\arcNum{1}% + \ifnum\thepos=100% + \def\posAttrName{above left}% + \def\posMultiplicity{below right}% + \fi% + \fi% + % + \ifnum\arcNum=1% + \xdef\tikzumlFirstArc{\tikzumlFirstArc node[pos=\tikzumlRelationPosition, \posAttrName, \attrAlign] {\tikzumlRelationAttrName}% + node[pos=\tikzumlRelationPosition, swap, \posMultiplicity, \multAlign] {\tikzumlRelationMultiplicity} }% + \fi% + \ifnum\arcNum=2% + \xdef\tikzumlLastArc{\tikzumlLastArc node[pos=\tikzumlRelationPosition, \posAttrName, \attrAlign] {\tikzumlRelationAttrName}% + node[pos=\tikzumlRelationPosition, swap, \posMultiplicity, \multAlign] {\tikzumlRelationMultiplicity} }% + \fi% + % + \ifnum\theposT>100% + \pgfmathsetmacro{\tikzumlRelationPositionT}{(\theposT-100)/100}% + \def\arcNumT{2}% + \else% + \def\arcNumT{1}% + \ifnum\theposT=100% + \def\posAttrNameT{above left}% + \def\posMultiplicityT{below right}% + \fi% + \fi% + % + \ifnum\arcNumT=1% + \xdef\tikzumlFirstArc{\tikzumlFirstArc node[pos=\tikzumlRelationPositionT, \posAttrNameT, \attrAlignT] {\tikzumlRelationAttrNameT}% + node[pos=\tikzumlRelationPositionT, swap, \posMultiplicityT, \multAlignT] {\tikzumlRelationMultiplicityT} }% + \fi% + \ifnum\arcNumT=2% + \xdef\tikzumlLastArc{\tikzumlLastArc node[pos=\tikzumlRelationPositionT, \posAttrNameT, \attrAlignT] {\tikzumlRelationAttrNameT}% + node[pos=\tikzumlRelationPositionT, swap, \posMultiplicityT, \multAlignT] {\tikzumlRelationMultiplicityT} }% + \fi% + % + \xdef\tikzumlPath{(\tikzumlSrcClassNodeName\tikzumlRelationSrcAnchor) -- \tikzumlFirstArc (\tikzumlRelationName-2.base) -- \tikzumlLastArc (\tikzumlDestClassNodeName\tikzumlRelationDestAnchor) }% + \ifnum\thetikzumlStateLevel>0% + \def\tikzumlStateFitTmp{\csname tikzumlStateFit\tikzumlState@parent @@\tikzumlState@fitname\endcsname}% + \expandafter\xdef\csname tikzumlStateFit\tikzumlState@parent @@\tikzumlState@fitname\endcsname{\tikzumlStateFitTmp (\tikzumlRelationName-1) (\tikzumlRelationName-2) (\tikzumlRelationName-3) }% + \fi% + }{% + % first horizontal then vertical line + \ifthenelse{\equal{\tikzumlRelationGeometry}{-|}}% + {% + %\setcounter{tikzumlControlNodesNum}{1}% + % + \def\tikzumlFirstArc{node[midway, inner sep=0, name=\tikzumlRelationName-1, anchor=center]{} }% + \def\tikzumlLastArc{node[midway, inner sep=0, name=\tikzumlRelationName-3, anchor=center] {} }% + % + \begin{pgfonlayer}{connections}% + \node[inner sep=0] (\tikzumlRelationName-2) at (\tikzumlSrcClassNodeName\tikzumlRelationSrcAnchor -| \tikzumlDestClassNodeName\tikzumlRelationDestAnchor) {};% + \end{pgfonlayer}% + % + \ifnum\theposStereo>100% + \pgfmathsetmacro{\tikzumlRelationPositionStereotype}{(\theposStereo-100)/100}% + \xdef\tikzumlLastArc{\tikzumlLastArc node[pos=\tikzumlRelationPositionStereotype, anchor=center] {\stereotype} }% + \else% + \xdef\tikzumlFirstArc{\tikzumlFirstArc node[pos=\tikzumlRelationPositionStereotype, anchor=center] {\stereotype} }% + \fi% + % + \ifnum\thepos>100% + \pgfmathsetmacro{\tikzumlRelationPosition}{(\thepos-100)/100}% + \def\arcNum{2}% + \else% + \def\arcNum{1}% + \ifnum\thepos=100% + \def\posAttrName{above left}% + \def\posMultiplicity{below right}% + \fi% + \fi% + % + \ifnum\arcNum=1% + \xdef\tikzumlFirstArc{\tikzumlFirstArc node[pos=\tikzumlRelationPosition, \posAttrName, \attrAlign] {\tikzumlRelationAttrName}% + node[pos=\tikzumlRelationPosition, swap, \posMultiplicity, \multAlign] {\tikzumlRelationMultiplicity} }% + \fi% + \ifnum\arcNum=2% + \xdef\tikzumlLastArc{\tikzumlLastArc node[pos=\tikzumlRelationPosition, \posAttrName, \attrAlign] {\tikzumlRelationAttrName}% + node[pos=\tikzumlRelationPosition, swap, \posMultiplicity, \multAlign] {\tikzumlRelationMultiplicity} }% + \fi% + % + \ifnum\theposT>100% + \pgfmathsetmacro{\tikzumlRelationPositionT}{(\theposT-100)/100}% + \def\arcNumT{2}% + \else% + \def\arcNumT{1}% + \ifnum\theposT=100% + \def\posAttrNameT{above left}% + \def\posMultiplicityT{below right}% + \fi% + \fi% + % + \ifnum\arcNumT=1% + \xdef\tikzumlFirstArc{\tikzumlFirstArc node[pos=\tikzumlRelationPositionT, \posAttrNameT, \attrAlignT] {\tikzumlRelationAttrNameT}% + node[pos=\tikzumlRelationPositionT, swap, \posMultiplicityT, \multAlignT] {\tikzumlRelationMultiplicityT} }% + \fi% + \ifnum\arcNumT=2% + \xdef\tikzumlLastArc{\tikzumlLastArc node[pos=\tikzumlRelationPositionT, \posAttrNameT, \attrAlignT] {\tikzumlRelationAttrNameT}% + node[pos=\tikzumlRelationPositionT, swap, \posMultiplicityT, \multAlignT] {\tikzumlRelationMultiplicityT} }% + \fi% + % + \xdef\tikzumlPath{(\tikzumlSrcClassNodeName\tikzumlRelationSrcAnchor) -- \tikzumlFirstArc (\tikzumlRelationName-2.base) -- \tikzumlLastArc (\tikzumlDestClassNodeName\tikzumlRelationDestAnchor) }% + \ifnum\thetikzumlStateLevel>0% + \def\tikzumlStateFitTmp{\csname tikzumlStateFit\tikzumlState@parent @@\tikzumlState@fitname\endcsname}% + \expandafter\xdef\csname tikzumlStateFit\tikzumlState@parent @@\tikzumlState@fitname\endcsname{\tikzumlStateFitTmp (\tikzumlRelationName-1) (\tikzumlRelationName-2) (\tikzumlRelationName-3) }% + \fi% + }{% + % first vertical, then horizontal, finally vertical line + \ifthenelse{\equal{\tikzumlRelationGeometry}{|-|}}% + {% + %\setcounter{tikzumlControlNodesNum}{2}% + % + \def\tikzumlFirstArc{node[midway, inner sep=0, name=\tikzumlRelationName-1, anchor=center] {} }% + \def\tikzumlLastArc{node[midway, inner sep=0, name=\tikzumlRelationName-5, anchor=center] {} }% + \def\tikzumlMidOneArc{ }% + % + \begin{pgfonlayer}{connections}% + % + \ifthenelse{\equal{\tikzumlRelationArmO}{auto}}{% + \ifthenelse{\equal{\tikzumlRelationArmT}{auto}}{% + \node[inner sep=0] (\tikzumlRelationName-3) at (\tikzumlRelationName-middle) {};% + \node[inner sep=0] (\tikzumlRelationName-2) at (\tikzumlSrcClassNodeName\tikzumlRelationSrcAnchor |- \tikzumlRelationName-3) {};% + \node[inner sep=0] (\tikzumlRelationName-4) at (\tikzumlRelationName-3 -| \tikzumlDestClassNodeName\tikzumlRelationDestAnchor) {};% + }{% + \draw (\tikzumlDestClassNodeName\tikzumlRelationDestAnchor)+(0,\tikzumlRelationArmT) node[inner sep=0, name=\tikzumlRelationName-4] {};% + \node[inner sep=0] (\tikzumlRelationName-2) at (\tikzumlRelationName-4 -| \tikzumlSrcClassNodeName\tikzumlRelationSrcAnchor) {};% + \node[inner sep=0] (\tikzumlRelationName-3) at (barycentric cs:\tikzumlRelationName-2=0.5,\tikzumlRelationName-4=0.5) {};% + }% + }{% + \draw (\tikzumlSrcClassNodeName\tikzumlRelationSrcAnchor)+(0,\tikzumlRelationArmO) node[inner sep=0, name=\tikzumlRelationName-2] {};% + \node[inner sep=0] (\tikzumlRelationName-4) at (\tikzumlRelationName-2 -| \tikzumlDestClassNodeName\tikzumlRelationDestAnchor) {};% + \node[inner sep=0] (\tikzumlRelationName-3) at (barycentric cs:\tikzumlRelationName-2=0.5,\tikzumlRelationName-4=0.5) {};% + }% + \end{pgfonlayer}% + % + \ifnum\theposStereo>200% + \pgfmathsetmacro{\tikzumlRelationPositionStereotype}{(\theposStereo-200)/100}% + \xdef\tikzumlLastArc{\tikzumlLastArc node[pos=\tikzumlRelationPositionStereotype, anchor=center] {\stereotype} }% + \else% + \ifnum\theposStereo<100% + \xdef\tikzumlFirstArc{\tikzumlFirstArc node[pos=\tikzumlRelationPositionStereotype, anchor=center] {\stereotype} }% + \else% + \pgfmathsetmacro{\tikzumlRelationPositionStereotype}{(\theposStereo-100)/100}% + \xdef\tikzumlMidOneArc{\tikzumlMidOneArc node[pos=\tikzumlRelationPositionStereotype, anchor=center] {\stereotype} }% + \fi% + \fi% + % + \ifthenelse{\thepos=200\OR\thepos=100}{% + \def\posAttrName{above left}% + \def\posMultiplicity{below right}% + }{}% + % + \ifthenelse{\thepos>200}{% + \pgfmathsetmacro{\tikzumlRelationPosition}{(\thepos-200)/100}% + \def\arcNum{3}% + }{% + \ifthenelse{\thepos<100}{% + \def\arcNum{1}% + }{% + \pgfmathsetmacro{\tikzumlRelationPosition}{(\thepos-100)/100}% + \def\arcNum{2}% + }% + }% + % + \ifnum\arcNum=1% + \xdef\tikzumlFirstArc{\tikzumlFirstArc node[pos=\tikzumlRelationPosition, \posAttrName, \attrAlign] {\tikzumlRelationAttrName}% + node[pos=\tikzumlRelationPosition, swap, \posMultiplicity, \multAlign] {\tikzumlRelationMultiplicity} }% + \fi% + \ifnum\arcNum=2% + \xdef\tikzumlMidOneArc{\tikzumlMidOneArc node[pos=\tikzumlRelationPosition, \posAttrName, \attrAlign] {\tikzumlRelationAttrName}% + node[pos=\tikzumlRelationPosition, swap, \posMultiplicity, \multAlign] {\tikzumlRelationMultiplicity} }% + \fi% + \ifnum\arcNum=3% + \xdef\tikzumlLastArc{\tikzumlLastArc node[pos=\tikzumlRelationPosition, \posAttrName, \attrAlign] {\tikzumlRelationAttrName}% + node[pos=\tikzumlRelationPosition, swap, \posMultiplicity, \multAlign] {\tikzumlRelationMultiplicity} }% + \fi% + % + \ifthenelse{\theposT=200\OR\theposT=100}{% + \def\posAttrNameT{above left}% + \def\posMultiplicityT{below right}% + }{}% + % + \ifthenelse{\theposT>200}{% + \pgfmathsetmacro{\tikzumlRelationPositionT}{(\theposT-200)/100}% + \def\arcNumT{3}% + }{% + \ifthenelse{\theposT<100}{% + \def\arcNumT{1}% + }{% + \pgfmathsetmacro{\tikzumlRelationPositionT}{(\theposT-100)/100}% + \def\arcNumT{2}% + }% + }% + % + \ifnum\arcNumT=1% + \xdef\tikzumlFirstArc{\tikzumlFirstArc node[pos=\tikzumlRelationPositionT, \posAttrNameT, \attrAlignT] {\tikzumlRelationAttrNameT}% + node[pos=\tikzumlRelationPositionT, swap, \posMultiplicityT, \multAlignT] {\tikzumlRelationMultiplicityT} }% + \fi% + \ifnum\arcNumT=2% + \xdef\tikzumlMidOneArc{\tikzumlMidOneArc node[pos=\tikzumlRelationPositionT, \posAttrNameT, \attrAlignT] {\tikzumlRelationAttrNameT}% + node[pos=\tikzumlRelationPositionT, swap, \posMultiplicityT, \multAlignT] {\tikzumlRelationMultiplicityT} }% + \fi% + \ifnum\arcNumT=3% + \xdef\tikzumlLastArc{\tikzumlLastArc node[pos=\tikzumlRelationPositionT, \posAttrNameT, \attrAlignT] {\tikzumlRelationAttrNameT}% + node[pos=\tikzumlRelationPositionT, swap, \posMultiplicityT, \multAlignT] {\tikzumlRelationMultiplicityT} }% + \fi% + % + \xdef\tikzumlPath{(\tikzumlSrcClassNodeName\tikzumlRelationSrcAnchor) -- \tikzumlFirstArc (\tikzumlRelationName-2.base) -- \tikzumlMidOneArc (\tikzumlRelationName-4.base) -- \tikzumlLastArc (\tikzumlDestClassNodeName\tikzumlRelationDestAnchor) }% + \ifnum\thetikzumlStateLevel>0% + \def\tikzumlStateFitTmp{\csname tikzumlStateFit\tikzumlState@parent @@\tikzumlState@fitname\endcsname}% + \expandafter\xdef\csname tikzumlStateFit\tikzumlState@parent @@\tikzumlState@fitname\endcsname{\tikzumlStateFitTmp (\tikzumlRelationName-1) (\tikzumlRelationName-2) (\tikzumlRelationName-3) (\tikzumlRelationName-4) (\tikzumlRelationName-5) }% + \fi% + }{% + % first horizontal, then vertical, finally horizontal line + \ifthenelse{\equal{\tikzumlRelationGeometry}{-|-}}% + {% + %\setcounter{tikzumlControlNodesNum}{2}% + % + \def\tikzumlFirstArc{node[midway, inner sep=0, name=\tikzumlRelationName-1, anchor=center] {} }% + \def\tikzumlLastArc{node[midway, inner sep=0, name=\tikzumlRelationName-5, anchor=center] {} }% + \def\tikzumlMidOneArc{}% + % + \begin{pgfonlayer}{connections}% + % + \ifthenelse{\equal{\tikzumlRelationArmO}{auto}}{% + \ifthenelse{\equal{\tikzumlRelationArmT}{auto}}{% + \node[inner sep=0] (\tikzumlRelationName-3) at (\tikzumlRelationName-middle) {};% + \node[inner sep=0] (\tikzumlRelationName-2) at (\tikzumlSrcClassNodeName\tikzumlRelationSrcAnchor -| \tikzumlRelationName-3) {};% + \node[inner sep=0] (\tikzumlRelationName-4) at (\tikzumlRelationName-3 |- \tikzumlDestClassNodeName\tikzumlRelationDestAnchor) {};% + }{% + \draw (\tikzumlDestClassNodeName\tikzumlRelationDestAnchor)+(\tikzumlRelationArmT,0) node[inner sep=0, name=\tikzumlRelationName-4] {};% + \node[inner sep=0] (\tikzumlRelationName-2) at (\tikzumlRelationName-4 |- \tikzumlSrcClassNodeName\tikzumlRelationSrcAnchor) {};% + \node[inner sep=0] (\tikzumlRelationName-3) at (barycentric cs:\tikzumlRelationName-2=0.5,\tikzumlRelationName-4=0.5) {};% + }% + }{% + \draw (\tikzumlSrcClassNodeName\tikzumlRelationSrcAnchor)+(\tikzumlRelationArmO,0) node[inner sep=0, name=\tikzumlRelationName-2] {};% + \node[inner sep=0] (\tikzumlRelationName-4) at (\tikzumlRelationName-2 |- \tikzumlDestClassNodeName\tikzumlRelationDestAnchor) {};% + \node[inner sep=0] (\tikzumlRelationName-3) at (barycentric cs:\tikzumlRelationName-2=0.5,\tikzumlRelationName-4=0.5) {};% + }% + \end{pgfonlayer}% + % + \ifnum\theposStereo>200% + \pgfmathsetmacro{\tikzumlRelationPositionStereotype}{(\theposStereo-200)/100}% + \xdef\tikzumlLastArc{\tikzumlLastArc node[pos=\tikzumlRelationPositionStereotype, anchor=center] {\stereotype} }% + \else% + \ifnum\theposStereo<100% + \xdef\tikzumlFirstArc{\tikzumlFirstArc node[pos=\tikzumlRelationPositionStereotype, anchor=center] {\stereotype} }% + \else% + \pgfmathsetmacro{\tikzumlRelationPositionStereotype}{(\theposStereo-100)/100}% + \xdef\tikzumlMidOneArc{\tikzumlMidOneArc node[pos=\tikzumlRelationPositionStereotype, anchor=center] {\stereotype} }% + \fi% + \fi% + % + \ifthenelse{\thepos=200\OR\thepos=100}{% + \def\posAttrName{above left}% + \def\posMultiplicity{below right}% + }{}% + % + \ifthenelse{\thepos>200}{% + \pgfmathsetmacro{\tikzumlRelationPosition}{(\thepos-200)/100}% + \def\arcNum{3}% + }{% + \ifthenelse{\thepos<100}{% + \def\arcNum{1}% + }{% + \pgfmathsetmacro{\tikzumlRelationPosition}{(\thepos-100)/100}% + \def\arcNum{2}% + }% + }% + % + \ifnum\arcNum=1% + \xdef\tikzumlFirstArc{\tikzumlFirstArc node[pos=\tikzumlRelationPosition, \posAttrName, \attrAlign] {\tikzumlRelationAttrName}% + node[pos=\tikzumlRelationPosition, swap, \posMultiplicity, \multAlign] {\tikzumlRelationMultiplicity} }% + \fi% + \ifnum\arcNum=2% + \xdef\tikzumlMidOneArc{\tikzumlMidOneArc node[pos=\tikzumlRelationPosition, \posAttrName, \attrAlign] {\tikzumlRelationAttrName}% + node[pos=\tikzumlRelationPosition, swap, \posMultiplicity, \multAlign] {\tikzumlRelationMultiplicity} }% + \fi% + \ifnum\arcNum=3% + \xdef\tikzumlLastArc{\tikzumlLastArc node[pos=\tikzumlRelationPosition, \posAttrName, \attrAlign] {\tikzumlRelationAttrName}% + node[pos=\tikzumlRelationPosition, swap, \posMultiplicity, \multAlign] {\tikzumlRelationMultiplicity} }% + \fi% + % + \ifthenelse{\theposT=200\OR\theposT=100}{% + \def\posAttrNameT{above left}% + \def\posMultiplicityT{below right}% + }{}% + % + \ifthenelse{\theposT>200}{% + \pgfmathsetmacro{\tikzumlRelationPositionT}{(\theposT-200)/100}% + \def\arcNumT{3}% + }{% + \ifthenelse{\theposT<100}{% + \def\arcNumT{1}% + }{% + \pgfmathsetmacro{\tikzumlRelationPositionT}{(\theposT-100)/100}% + \def\arcNumT{2}% + }% + }% + % + \ifnum\arcNumT=1% + \xdef\tikzumlFirstArc{\tikzumlFirstArc node[pos=\tikzumlRelationPositionT, \posAttrNameT, \attrAlignT] {\tikzumlRelationAttrNameT}% + node[pos=\tikzumlRelationPositionT, swap, \posMultiplicityT, \multAlignT] {\tikzumlRelationMultiplicityT} }% + \fi% + \ifnum\arcNumT=2% + \xdef\tikzumlMidOneArc{\tikzumlMidOneArc node[pos=\tikzumlRelationPositionT, \posAttrNameT, \attrAlignT] {\tikzumlRelationAttrNameT}% + node[pos=\tikzumlRelationPositionT, swap, \posMultiplicityT, \multAlignT] {\tikzumlRelationMultiplicityT} }% + \fi% + \ifnum\arcNumT=3% + \xdef\tikzumlLastArc{\tikzumlLastArc node[pos=\tikzumlRelationPositionT, \posAttrNameT, \attrAlignT] {\tikzumlRelationAttrNameT}% + node[pos=\tikzumlRelationPositionT, swap, \posMultiplicityT, \multAlignT] {\tikzumlRelationMultiplicityT} }% + \fi% + % + \xdef\tikzumlPath{(\tikzumlSrcClassNodeName\tikzumlRelationSrcAnchor) -- \tikzumlFirstArc (\tikzumlRelationName-2.base) -- \tikzumlMidOneArc (\tikzumlRelationName-4.base) -- \tikzumlLastArc (\tikzumlDestClassNodeName\tikzumlRelationDestAnchor) }% + \ifnum\thetikzumlStateLevel>0% + \def\tikzumlStateFitTmp{\csname tikzumlStateFit\tikzumlState@parent @@\tikzumlState@fitname\endcsname}% + \expandafter\xdef\csname tikzumlStateFit\tikzumlState@parent @@\tikzumlState@fitname\endcsname{\tikzumlStateFitTmp (\tikzumlRelationName-1) (\tikzumlRelationName-2) (\tikzumlRelationName-3) (\tikzumlRelationName-4) (\tikzumlRelationName-5) }% + \fi% + }{% + \errmessage{TIKZUML ERROR : Unknown geometry value !!! It should be in the following list : --, |-, -|, |-|, -|-}% + }% + }% + }% + }% + }% + % + \begin{pgfonlayer}{connections}% + \ifthenelse{\equal{\tikzumlRelationStyle}{tikzuml nesting style}}{% + \pgfarrowsdeclare{nested}{nested}{...} + { + \tikzumlNestingSymbolSize=0.2pt% + \advance\tikzumlNestingSymbolSize by .5\pgflinewidth% + \pgfsetdash{}{0pt} % do not dash + \pgfsetroundjoin % fix join + \pgfsetroundcap % fix cap + \pgfpathmoveto{\pgfpoint{-16*\tikzumlNestingSymbolSize}{0pt}}% + \pgfpatharc{180}{90}{8*\tikzumlNestingSymbolSize}% + \pgfpatharc{90}{0}{8*\tikzumlNestingSymbolSize}% + \pgfpatharc{0}{-90}{8*\tikzumlNestingSymbolSize}% + \pgfpatharc{-90}{-180}{8*\tikzumlNestingSymbolSize}% + \pgfpathmoveto{\pgfpoint{-8*\tikzumlNestingSymbolSize}{8*\tikzumlNestingSymbolSize}}% + \pgfpathlineto{\pgfpoint{-8*\tikzumlNestingSymbolSize}{-8*\tikzumlNestingSymbolSize}}% + \pgfusepathqstroke% + }% + \draw[auto, nested-, font=\tikzumlDefaultFont, \tikzumlRelationStyle, /tikzuml/relation/style2] \tikzumlPath ;% + }{ + \draw[auto, font=\tikzumlDefaultFont, \tikzumlRelationStyle, /tikzuml/relation/style2] \tikzumlPath ;% + } + \end{pgfonlayer}% + % + \stepcounter{tikzumlRelationNum}% +}% +% +% shortcuts of \umlrelation +\newcommand{\umlHVrelation}[3][]{% + \pgfkeys{/tikzuml/HVrelation/.cd,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{geometry}}{% + \errmessage{TIKZUML ERROR : in umlHVrelation, forbidden option geometry}% + }{}% + }}% + \pgfkeys{/tikzuml/HVrelation/.cd, #1}% + \umlrelation[geometry=-|, #1]{#2}{#3}% +}% +% +\newcommand{\umlVHrelation}[3][]{% + \pgfkeys{/tikzuml/VHrelation/.cd,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{geometry}}{% + \errmessage{TIKZUML ERROR : in umlVHrelation, forbidden option geometry}% + }{}% + }}% + \pgfkeys{/tikzuml/VHrelation/.cd, #1}% + \umlrelation[geometry=|-, #1]{#2}{#3}% +}% +% +\newcommand{\umlHVHrelation}[3][]{% + \pgfkeys{/tikzuml/HVHrelation/.cd,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{geometry}}{% + \errmessage{TIKZUML ERROR : in umlHVHrelation, forbidden option geometry}% + }{}% + }}% + \pgfkeys{/tikzuml/HVHrelation/.cd, #1}% + \umlrelation[geometry=-|-, #1]{#2}{#3}% +}% +% +\newcommand{\umlVHVrelation}[3][]{% + \pgfkeys{/tikzuml/VHVrelation/.cd,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{geometry}}{% + \errmessage{TIKZUML ERROR : in umlVHVrelation, forbidden option geometry}% + }{}% + }}% + \pgfkeys{/tikzuml/VHVrelation/.cd, #1}% + \umlrelation[geometry=|-|, #1]{#2}{#3}% +}% +% +% +% shortcuts for relations +\newcommand{\umlinherit}[3][]{\umlrelation[style={tikzuml inherit style}, #1]{#2}{#3}}% +\newcommand{\umlimpl}[3][]{\umlrelation[style={tikzuml implements style}, #1]{#2}{#3}}% +\newcommand{\umlreal}[3][]{\umlrelation[style={tikzuml implements style}, #1]{#2}{#3}}% +\newcommand{\umlassoc}[3][]{\umlrelation[style={tikzuml association style}, #1]{#2}{#3}}% +\newcommand{\umlbiassoc}[3][]{\umlrelation[style={tikzuml bidirectional association style}, #1]{#2}{#3}}% +\newcommand{\umluniassoc}[3][]{\umlrelation[style={tikzuml unidirectional association style}, #1]{#2}{#3}}% +\newcommand{\umlaggreg}[3][]{\umlrelation[style={tikzuml aggregation style}, #1]{#2}{#3}}% +\newcommand{\umluniaggreg}[3][]{\umlrelation[style={tikzuml unidirectional aggregation style}, #1]{#2}{#3}}% +\newcommand{\umlcompo}[3][]{\umlrelation[style={tikzuml composition style}, #1]{#2}{#3}}% +\newcommand{\umlunicompo}[3][]{\umlrelation[style={tikzuml unidirectional composition style}, #1]{#2}{#3}}% +\newcommand{\umlimport}[3][]{\umlrelation[style={tikzuml import style}, #1]{#2}{#3}}% +\newcommand{\umlnest}[3][]{\umlrelation[style={tikzuml nesting style}, #1]{#2}{#3}}% +\newcommand{\umldep}[3][]{\umlrelation[style={tikzuml dependency style}, #1]{#2}{#3}}% +\newcommand{\umlfriend}[3][]{% + \pgfkeys{/tikzuml/friendrelation/.cd,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{stereo}}{% + \errmessage{TIKZUML ERROR : in umlfriend, forbidden option stereo}% + }{}% + }}% + \pgfkeys{/tikzuml/friendrelation/.cd, #1}% + \umlrelation[stereo=friend, style={tikzuml dependency style}, #1]{#2}{#3}% +}% +% +\newcommand{\umlHVinherit}[3][]{\umlHVrelation[style={tikzuml inherit style}, #1]{#2}{#3}}% +\newcommand{\umlHVimpl}[3][]{\umlHVrelation[style={tikzuml implements style}, #1]{#2}{#3}}% +\newcommand{\umlHVreal}[3][]{\umlHVrelation[style={tikzuml implements style}, #1]{#2}{#3}}% +\newcommand{\umlHVassoc}[3][]{\umlHVrelation[style={tikzuml association style}, #1]{#2}{#3}}% +\newcommand{\umlHVuniassoc}[3][]{\umlHVrelation[style={tikzuml unidirectional association style}, #1]{#2}{#3}}% +\newcommand{\umlHVaggreg}[3][]{\umlHVrelation[style={tikzuml aggregation style}, #1]{#2}{#3}}% +\newcommand{\umlHVuniaggreg}[3][]{\umlHVrelation[style={tikzuml unidirectional aggregation style}, #1]{#2}{#3}}% +\newcommand{\umlHVcompo}[3][]{\umlHVrelation[style={tikzuml composition style}, #1]{#2}{#3}}% +\newcommand{\umlHVunicompo}[3][]{\umlHVrelation[style={tikzuml unidirectional composition style}, #1]{#2}{#3}}% +\newcommand{\umlHVimport}[3][]{\umlHVrelation[style={tikzuml import style}, #1]{#2}{#3}}% +\newcommand{\umlHVnest}[3][]{\umlHVrelation[style={tikzuml nesting style}, #1]{#2}{#3}}% +\newcommand{\umlHVdep}[3][]{\umlHVrelation[style={tikzuml dependency style}, #1]{#2}{#3}}% +\newcommand{\umlHVfriend}[3][]{% + \pgfkeys{/tikzuml/friendrelation/.cd,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{stereo}}{% + \errmessage{TIKZUML ERROR : in umlHVfriend, forbidden option stereo}% + }{% + \ifthenelse{\equal{\keyname}{geometry}}{% + \errmessage{TIKZUML ERROR : in umlHVfriend, forbidden option geometry}% + }{}% + }% + }}% + \pgfkeys{/tikzuml/friendrelation/.cd, #1}% + \umlrelation[geometry=-|, stereo=friend, style={tikzuml dependency style}, #1]{#2}{#3}% +}% +% +\newcommand{\umlVHinherit}[3][]{\umlVHrelation[style={tikzuml inherit style}, #1]{#2}{#3}}% +\newcommand{\umlVHimpl}[3][]{\umlVHrelation[style={tikzuml implements style}, #1]{#2}{#3}}% +\newcommand{\umlVHreal}[3][]{\umlVHrelation[style={tikzuml implements style}, #1]{#2}{#3}}% +\newcommand{\umlVHassoc}[3][]{\umlVHrelation[style={tikzuml association style}, #1]{#2}{#3}}% +\newcommand{\umlVHuniassoc}[3][]{\umlVHrelation[style={tikzuml unidirectional association style}, #1]{#2}{#3}}% +\newcommand{\umlVHaggreg}[3][]{\umlVHrelation[style={tikzuml aggregation style}, #1]{#2}{#3}}% +\newcommand{\umlVHuniaggreg}[3][]{\umlVHrelation[style={tikzuml unidirectional aggregation style}, #1]{#2}{#3}}% +\newcommand{\umlVHcompo}[3][]{\umlVHrelation[style={tikzuml composition style}, #1]{#2}{#3}}% +\newcommand{\umlVHunicompo}[3][]{\umlVHrelation[style={tikzuml unidirectional composition style}, #1]{#2}{#3}}% +\newcommand{\umlVHimport}[3][]{\umlVHrelation[style={tikzuml import style}, #1]{#2}{#3}}% +\newcommand{\umlVHnest}[3][]{\umlVHrelation[style={tikzuml nesting style}, #1]{#2}{#3}}% +\newcommand{\umlVHdep}[3][]{\umlVHrelation[style={tikzuml dependency style}, #1]{#2}{#3}}% +\newcommand{\umlVHfriend}[3][]{% + \pgfkeys{/tikzuml/friendrelation/.cd,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{stereo}}{% + \errmessage{TIKZUML ERROR : in umlVHfriend, forbidden option stereo}% + }{% + \ifthenelse{\equal{\keyname}{geometry}}{% + \errmessage{TIKZUML ERROR : in umlVHfriend, forbidden option geometry}% + }{}% + }% + }}% + \pgfkeys{/tikzuml/friendrelation/.cd, #1}% + \umlrelation[geometry=|-, stereo=friend, style={tikzuml dependency style}, #1]{#2}{#3}% +}% +% +\newcommand{\umlHVHinherit}[3][]{\umlHVHrelation[style={tikzuml inherit style}, #1]{#2}{#3}}% +\newcommand{\umlHVHimpl}[3][]{\umlHVHrelation[style={tikzuml implements style}, #1]{#2}{#3}}% +\newcommand{\umlHVHreal}[3][]{\umlHVHrelation[style={tikzuml implements style}, #1]{#2}{#3}}% +\newcommand{\umlHVHassoc}[3][]{\umlHVHrelation[style={tikzuml association style}, #1]{#2}{#3}}% +\newcommand{\umlHVHuniassoc}[3][]{\umlHVHrelation[style={tikzuml unidirectional association style}, #1]{#2}{#3}}% +\newcommand{\umlHVHaggreg}[3][]{\umlHVHrelation[style={tikzuml aggregation style}, #1]{#2}{#3}}% +\newcommand{\umlHVHuniaggreg}[3][]{\umlHVHrelation[style={tikzuml unidirectional aggregation style}, #1]{#2}{#3}}% +\newcommand{\umlHVHcompo}[3][]{\umlHVHrelation[style={tikzuml composition style}, #1]{#2}{#3}}% +\newcommand{\umlHVHunicompo}[3][]{\umlHVHrelation[style={tikzuml unidirectional composition style}, #1]{#2}{#3}}% +\newcommand{\umlHVHimport}[3][]{\umlHVHrelation[style={tikzuml import style}, #1]{#2}{#3}}% +\newcommand{\umlHVHnest}[3][]{\umlHVHrelation[style={tikzuml nesting style}, #1]{#2}{#3}}% +\newcommand{\umlHVHdep}[3][]{\umlHVHrelation[style={tikzuml dependency style}, #1]{#2}{#3}}% +\newcommand{\umlHVHfriend}[3][]{% + \pgfkeys{/tikzuml/friendrelation/.cd,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{stereo}}{% + \errmessage{TIKZUML ERROR : in umlHVHfriend, forbidden option stereo}% + }{% + \ifthenelse{\equal{\keyname}{geometry}}{% + \errmessage{TIKZUML ERROR : in umlHVHfriend, forbidden option geometry}% + }{}% + }% + }}% + \pgfkeys{/tikzuml/friendrelation/.cd, #1}% + \umlrelation[geometry=-|-, stereo=friend, style={tikzuml dependency style}, #1]{#2}{#3}% +}% +% +\newcommand{\umlVHVinherit}[3][]{\umlVHVrelation[style={tikzuml inherit style}, #1]{#2}{#3}}% +\newcommand{\umlVHVimpl}[3][]{\umlVHVrelation[style={tikzuml implements style}, #1]{#2}{#3}}% +\newcommand{\umlVHVreal}[3][]{\umlVHVrelation[style={tikzuml implements style}, #1]{#2}{#3}}% +\newcommand{\umlVHVassoc}[3][]{\umlVHVrelation[style={tikzuml association style}, #1]{#2}{#3}}% +\newcommand{\umlVHVuniassoc}[3][]{\umlVHVrelation[style={tikzuml unidirectional association style}, #1]{#2}{#3}}% +\newcommand{\umlVHVaggreg}[3][]{\umlVHVrelation[style={tikzuml aggregation style}, #1]{#2}{#3}}% +\newcommand{\umlVHVuniaggreg}[3][]{\umlVHVrelation[style={tikzuml unidirectional aggregation style}, #1]{#2}{#3}}% +\newcommand{\umlVHVcompo}[3][]{\umlVHVrelation[style={tikzuml composition style}, #1]{#2}{#3}}% +\newcommand{\umlVHVunicompo}[3][]{\umlVHVrelation[style={tikzuml unidirectional composition style}, #1]{#2}{#3}}% +\newcommand{\umlVHVimport}[3][]{\umlVHVrelation[style={tikzuml import style}, #1]{#2}{#3}}% +\newcommand{\umlVHVnest}[3][]{\umlVHVrelation[style={tikzuml nesting style}, #1]{#2}{#3}}% +\newcommand{\umlVHVdep}[3][]{\umlVHVrelation[style={tikzuml dependency style}, #1]{#2}{#3}}% +\newcommand{\umlVHVfriend}[3][]{% + \pgfkeys{/tikzuml/friendrelation/.cd,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{stereo}}{% + \errmessage{TIKZUML ERROR : in umlVHVfriend, forbidden option stereo}% + }{% + \ifthenelse{\equal{\keyname}{geometry}}{% + \errmessage{TIKZUML ERROR : in umlVHVfriend, forbidden option geometry}% + }{}% + }% + }}% + \pgfkeys{/tikzuml/friendrelation/.cd, #1}% + \umlrelation[geometry=|-|, stereo=friend, style={tikzuml dependency style}, #1]{#2}{#3}% +}% +% +% define a node +\newcommand{\umlnode}[2]{% + \node (#2) at (#1) {};% +}% +% +% main command to define a relation between two classes through a control node +% args : src class +% control node +% dest class +% optional : arg1, arg2, arg: name of the src/dest/dest side class type attribute defined by the relation +% mult1, mult2, mult: multiplicity of the src/dest/dest side class type attribute defined by the relation +% pos1, pos2, pos: position of the src/dest/dest side class type attribute defined by the relation +% align1, align2, align: text justification of the src/dest/dest side class type attribute defined by the relation +% anchor1, anchor2: src/dest anchors on linked classes +% stereo: stereotype of the relation +% pos stereo: position of the stereotype on the relation +% style: style of the relation (association, aggregation, composition, inherit, ...) +% name: rootname used for naming nodes of the relation +\newcommand{\umlCNrelation}[4][]{% + \pgfkeys{/tikzuml/relation/.cd, arg1/.initial={}, arg2/.initial={}, arg/.initial={},% + mult1/.initial={}, mult2/.initial={}, mult/.initial={},% + pos1/.initial=0.2, pos2/.initial=0.8, pos/.initial=tikzumlEmpty,% + align1/.initial={}, align2/.initial={}, align/.initial={},% + anchor1/.initial=tikzumlEmpty, anchor2/.initial=tikzumlEmpty,% + stereo/.initial={}, pos stereo/.initial=1,% + style/.initial=->, name/.initial=relation-\thetikzumlRelationNum,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \errmessage{TIKZUML ERROR : in umlCNrelation, invalid option \keyname}% + }% + }% + \pgfkeys{/tikzuml/relation/.cd,#1}% + \pgfkeys{/tikzuml/relation/.cd, arg1/.get=\tikzumlCNRelationAttrName, arg2/.get=\tikzumlCNRelationAttrNameTO, arg/.get=\tikzumlCNRelationAttrNameTT,% + mult1/.get=\tikzumlCNRelationMultiplicity, mult2/.get=\tikzumlCNRelationMultiplicityTO, mult/.get=\tikzumlCNRelationMultiplicityTT,% + pos1/.get=\tikzumlCNRelationPosition, pos2/.get=\tikzumlCNRelationPositionTO, pos/.get=\tikzumlCNRelationPositionTT,% + align1/.get=\tikzumlCNRelationAlign, align2/.get=\tikzumlCNRelationAlignTO, align/.get=\tikzumlCNRelationAlignTT,% + anchor1/.get=\tikzumlCNRelationSrcAnchor, anchor2/.get=\tikzumlCNRelationDestAnchor,% + stereo/.get=\tikzumlCNRelationStereoType, pos stereo/.get=\tikzumlCNRelationPositionStereotype,% + style/.get=\tikzumlCNRelationStyle, name/.get=\tikzumlCNRelationName% + }% + % + % managing \_ in class names for node names + \def\tikzumlSrcClassName{#2}% + \begingroup% + \def\_{_}\edef\x{\endgroup% + \def\noexpand\tikzumlSrcClassNodeName{\tikzumlSrcClassName}}\x% + % + \StrSubstitute{\tikzumlSrcClassNodeName}{:}{@COLON@}[\tikzumlSrcClassNodeName]% + \StrSubstitute{\tikzumlSrcClassNodeName}{\_}{@UNDERSCORE@}[\tikzumlSrcClassNodeName]% + % + \def\tikzumlDestClassName{#4}% + \begingroup% + \def\_{_}\edef\x{\endgroup% + \def\noexpand\tikzumlDestClassNodeName{\tikzumlDestClassName}}\x% + % + \StrSubstitute{\tikzumlDestClassNodeName}{:}{@COLON@}[\tikzumlDestClassNodeName]% + \StrSubstitute{\tikzumlDestClassNodeName}{\_}{@UNDERSCORE@}[\tikzumlDestClassNodeName]% + % + % managing alias keys + \def\tikzumlCNRelationAttrNameT{\tikzumlCNRelationAttrNameTO\tikzumlCNRelationAttrNameTT}% + \def\tikzumlCNRelationMultiplicityT{\tikzumlCNRelationMultiplicityTO\tikzumlCNRelationMultiplicityTT}% + \def\tikzumlCNRelationAlignT{\tikzumlCNRelationAlignTO\tikzumlCNRelationAlignTT}% + \def\orientationT{\orientationTO\orientationTT}% + % + \ifthenelse{\equal{\tikzumlCNRelationPositionTT}{tikzumlEmpty}}{% + \def\tikzumlCNRelationPositionT{\tikzumlCNRelationPositionTO}% + }{% + \def\tikzumlCNRelationPositionT{\tikzumlCNRelationPositionTT}% + }% + % + \def\attrAlign{}% + \def\multAlign{}% + \def\attrAlignT{}% + \def\multAlignT{}% + % + \ifthenelse{\equal{\tikzumlCNRelationAlign}{left}}{% + \def\attrAlign{above right}% + \def\multAlign{below right}% + }{% + \ifthenelse{\equal{\tikzumlCNRelationAlign}{right}}{% + \def\attrAlign{above left}% + \def\multAlign{below left}% + }{}% + }% + % + \ifthenelse{\equal{\tikzumlCNRelationAlignT}{left}}{% + \def\attrAlignT{above right}% + \def\multAlignT{below right}% + }{% + \ifthenelse{\equal{\tikzumlCNRelationAlignT}{right}}{% + \def\attrAlignT{above left}% + \def\multAlignT{below left}% + }{}% + }% + % + % def stereotype + \ifthenelse{\equal{\tikzumlCNRelationStereoType}{}}{% + \def\stereotype{}% + }{% + \def\stereotype{$\ll$\tikzumlCNRelationStereoType$\gg$}% + }% + % + % def anchors macros + \ifthenelse{\equal{\tikzumlCNRelationSrcAnchor}{tikzumlEmpty}}{% + \def\tikzumlCNRelationSrcAnchor{}% + }{% + \let\tikzumlCNRelationSrcAnchorold\tikzumlCNRelationSrcAnchor% + \def\tikzumlCNRelationSrcAnchor{.\tikzumlCNRelationSrcAnchorold}% + }% + % + \ifthenelse{\equal{\tikzumlCNRelationDestAnchor}{tikzumlEmpty}}{% + \def\tikzumlCNRelationDestAnchor{}% + }{% + \let\tikzumlCNRelationDestAnchorold\tikzumlCNRelationDestAnchor% + \def\tikzumlCNRelationDestAnchor{.\tikzumlCNRelationDestAnchorold}% + }% + % + \setcounter{pos}{100*\real{\tikzumlCNRelationPosition}}% + \setcounter{posT}{100*\real{\tikzumlCNRelationPositionT}}% + \setcounter{posStereo}{100*\real{\tikzumlCNRelationPositionStereotype}}% + % + % straight line + %\setcounter{tikzumlControlNodesNum}{1}% + % + \def\tikzumlFirstArc{node[midway, name=\tikzumlCNRelationName-1, anchor=center] {} }% + \def\tikzumlLastArc{node[midway, name=\tikzumlCNRelationName-3, anchor=center]{} }% + \def\posAttrName{}% + \def\posMultiplicity{}% + \def\posAttrNameT{}% + \def\posMultiplicityT{}% + % + \begin{pgfonlayer}{connections}% + \node (\tikzumlCNRelationName-2) at (#3) {};% + \end{pgfonlayer}% + % + \ifnum\theposStereo>100% + \pgfmathsetmacro{\tikzumlCNRelationPositionStereotype}{(\theposStereo-100)/100}% + \xdef\tikzumlLastArc{\tikzumlLastArc node[pos=\tikzumlCNRelationPositionStereotype, anchor=center] {\stereotype} }% + \else% + \xdef\tikzumlFirstArc{\tikzumlFirstArc node[pos=\tikzumlCNRelationPositionStereotype, anchor=center] {\stereotype} }% + \fi% + % + \ifnum\thepos>100% + \pgfmathsetmacro{\tikzumlCNRelationPosition}{(\thepos-100)/100}% + \def\arcNum{2}% + \else% + \def\arcNum{1}% + \ifnum\thepos=100% + \def\posAttrName{above left}% + \def\posMultiplicity{below right}% + \fi% + \fi% + % + \ifnum\arcNum=1% + \xdef\tikzumlFirstArc{\tikzumlFirstArc node[pos=\tikzumlCNRelationPosition, \posAttrName, \attrAlign] {\tikzumlCNRelationAttrName}% + node[pos=\tikzumlCNRelationPosition, swap, \posMultiplicity, \multAlign] {\tikzumlCNRelationMultiplicity} }% + \fi% + \ifnum\arcNum=2% + \xdef\tikzumlLastArc{\tikzumlLastArc node[pos=\tikzumlCNRelationPosition, \posAttrName, \attrAlign] {\tikzumlCNRelationAttrName}% + node[pos=\tikzumlCNRelationPosition, swap, \posMultiplicity, \multAlign] {\tikzumlCNRelationMultiplicity} }% + \fi% + % + \ifnum\theposT>100% + \pgfmathsetmacro{\tikzumlCNRelationPositionT}{(\theposT-100)/100}% + \def\arcNumT{2}% + \else% + \def\arcNumT{1}% + \ifnum\theposT=100% + \def\posAttrNameT{above left}% + \def\posMultiplicityT{below right}% + \fi% + \fi% + % + \ifnum\arcNumT=1% + \xdef\tikzumlFirstArc{\tikzumlFirstArc node[pos=\tikzumlCNRelationPositionT, \posAttrNameT, \attrAlignT] {\tikzumlCNRelationAttrNameT}% + node[pos=\tikzumlCNRelationPositionT, swap, \posMultiplicityT, \multAlignT] {\tikzumlCNRelationMultiplicityT} }% + \fi% + \ifnum\arcNumT=2% + \xdef\tikzumlLastArc{\tikzumlLastArc node[pos=\tikzumlCNRelationPositionT, \posAttrNameT, \attrAlignT] {\tikzumlCNRelationAttrNameT}% + node[pos=\tikzumlCNRelationPositionT, swap, \posMultiplicityT, \multAlignT] {\tikzumlCNRelationMultiplicityT} }% + \fi% + % + \xdef\tikzumlPath{(\tikzumlSrcClassNodeName\tikzumlCNRelationSrcAnchor) -- \tikzumlFirstArc (\tikzumlCNRelationName-2.base) -- \tikzumlLastArc (\tikzumlDestClassNodeName\tikzumlCNRelationDestAnchor) }% + + \begin{pgfonlayer}{connections}% + \ifthenelse{\equal{\tikzumlCNRelationStyle}{tikzuml nesting style}}{% + \pgfarrowsdeclare{nested}{nested}{...} + { + \tikzumlNestingSymbolSize=0.2pt% + \advance\tikzumlNestingSymbolSize by .5\pgflinewidth% + \pgfsetdash{}{0pt} % do not dash + \pgfsetroundjoin % fix join + \pgfsetroundcap % fix cap + \pgfpathmoveto{\pgfpoint{-16*\tikzumlNestingSymbolSize}{0pt}}% + \pgfpatharc{180}{90}{8*\tikzumlNestingSymbolSize}% + \pgfpatharc{90}{0}{8*\tikzumlNestingSymbolSize}% + \pgfpatharc{0}{-90}{8*\tikzumlNestingSymbolSize}% + \pgfpatharc{-90}{-180}{8*\tikzumlNestingSymbolSize}% + \pgfpathmoveto{\pgfpoint{-8*\tikzumlNestingSymbolSize}{8*\tikzumlNestingSymbolSize}}% + \pgfpathlineto{\pgfpoint{-8*\tikzumlNestingSymbolSize}{-8*\tikzumlNestingSymbolSize}}% + \pgfusepathqstroke% + }% + \draw[auto, \tikzumlCNRelationStyle, nested-, font=\tikzumlDefaultFont] \tikzumlPath ;% + }{ + \draw[auto, \tikzumlCNRelationStyle, font=\tikzumlDefaultFont] \tikzumlPath ;% + } + \end{pgfonlayer}% + % + \stepcounter{tikzumlRelationNum}% +}% +% +% shortcuts for cnrelations +\newcommand{\umlCNinherit}[4][]{\umlCNrelation[style={tikzuml inherit style}, #1]{#2}{#3}{#4}}% +\newcommand{\umlCNimpl}[4][]{\umlCNrelation[style={tikzuml implements style}, #1]{#2}{#3}{#4}}% +\newcommand{\umlCNreal}[4][]{\umlCNrelation[style={tikzuml implements style}, #1]{#2}{#3}{#4}}% +\newcommand{\umlCNassoc}[4][]{\umlCNrelation[style={tikzuml association style}, #1]{#2}{#3}{#4}}% +\newcommand{\umlCNuniassoc}[4][]{\umlCNrelation[style={tikzuml unidirectional association style}, #1]{#2}{#3}{#4}}% +\newcommand{\umlCNaggreg}[4][]{\umlCNrelation[style={tikzuml aggregation style}, #1]{#2}{#3}{#4}}% +\newcommand{\umlCNuniaggreg}[4][]{\umlCNrelation[style={tikzuml unidirectional aggregation style}, #1]{#2}{#3}{#4}}% +\newcommand{\umlCNcompo}[4][]{\umlCNrelation[style={tikzuml composition style}, #1]{#2}{#3}{#4}}% +\newcommand{\umlCNunicompo}[4][]{\umlCNrelation[style={tikzuml unidirectional composition style}, #1]{#2}{#3}{#4}}% +\newcommand{\umlCNimport}[4][]{\umlCNrelation[style={tikzuml import style}, #1]{#2}{#3}{#4}}% +\newcommand{\umlCNnest}[4][]{\umlCNrelation[style={tikzuml nesting style}, #1]{#2}{#3}{#4}}% +\newcommand{\umlCNdep}[4][]{\umlCNrelation[style={tikzuml dependency style}, #1]{#2}{#3}{#4}}% +\newcommand{\umlCNfriend}[4][]{% + \pgfkeys{/tikzuml/friendrelation/.cd,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{stereo}}{% + \errmessage{TIKZUML ERROR : in umlCNfriend, forbidden option stereo}% + }{}% + }}% + \pgfkeys{/tikzuml/friendrelation/.cd, #1}% + \umlCNrelation[stereo=friend, style={tikzuml dependency style}, #1]{#2}{#3}{#4}% +}% +% +% define a note +% arg : attached class +% label of the note +% optional : x,y: coordinates of the note +% width: width of the note +% geometry: geometry of the relation between the note and what it is about +% weight: barycentric weight for a 3-line relation +% arm: length of the first arm +% anchor1, anchor2: anchors of the relation +% draw, fill, text: colors +% style: to manage every default TikZ option +% no coords: to tell that the note position is defined relatively +% to another node (automatically used with TikZ options above, below, left, right, below left, ...) +\newcommand{\umlnote}[3][]{% + \pgfkeys{/tikzuml/note/.cd, x/.initial=\tikzumlDefaultX, y/.initial=\tikzumlDefaultX, width/.initial=\tikzumlNoteDefaultWidth,% + geometry/.initial=\tikzumlRelationDefaultGeometry,% + weight/.initial=\tikzumlRelationDefaultWeight, arm/.initial=auto, style/.style={},% + anchor1/.initial=tikzumlEmpty, anchor2/.initial=tikzumlEmpty,% + draw/.initial=\tikzumlDefaultDrawColor, fill/.initial=\tikzumlNoteDefaultFillColor,% + text/.initial=\tikzumlDefaultTextColor,% + no coords/.is if=tikzumlnoteWithoutCoords,% + no coords=false,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{above}\OR% + \equal{\keyname}{left}\OR% + \equal{\keyname}{below}\OR% + \equal{\keyname}{right}\OR% + \equal{\keyname}{above left}\OR% + \equal{\keyname}{above right}\OR% + \equal{\keyname}{below left}\OR% + \equal{\keyname}{below right}}{% + \IfSubStr{\keyvalue}{ of }{% + \pgfkeys{/tikzuml/note/.cd, no coords}% + }{}% + }{}% + \ifx\keyvalue\pgfkeysnovalue% + \pgfkeys{/tikzuml/note/.cd, style/.append style/.expand once={\keyname}}% + \else% + \pgfkeys{/tikzuml/note/.cd, style/.append style/.expand twice={\expandafter\keyname\expandafter=\keyvalue}}% + \fi% + %\errmessage{TIKZUML ERROR : in umlnote, invalid option \keyname}% + }% + }% + \pgfkeys{/tikzuml/note/.cd, #1}% + \pgfkeys{/tikzuml/note/.cd, x/.get=\tikzumlNoteX, y/.get=\tikzumlNoteY, width/.get=\tikzumlNoteTextWidth,% + geometry/.get=\tikzumlNoteGeometry,% + weight/.get=\tikzumlNoteWeight, arm/.get=\tikzumlNoteArm,% + anchor1/.get=\tikzumlNoteSrcAnchor, anchor2/.get=\tikzumlNoteDestAnchor,% + draw/.get=\tikzumlNoteDrawColor, fill/.get=\tikzumlNoteFillColor,% + text/.get=\tikzumlNoteTextColor% + }% + % + \def\tikzumlClassName{#2}% + \begingroup% + \def\_{_}\edef\x{\endgroup% + \def\noexpand\tikzumlClassNodeName{\tikzumlClassName}}\x% + % + % def anchors macros + \ifthenelse{\equal{\tikzumlNoteSrcAnchor}{tikzumlEmpty}}{% + \def\tikzumlNoteSrcAnchor{}% + }{% + \let\tikzumlNoteSrcAnchorold\tikzumlNoteSrcAnchor% + \def\tikzumlNoteSrcAnchor{.\tikzumlNoteSrcAnchorold}% + }% + % + \ifthenelse{\equal{\tikzumlNoteDestAnchor}{tikzumlEmpty}}{% + \def\tikzumlNoteDestAnchor{}% + }{% + \let\tikzumlNoteDestAnchorold\tikzumlNoteDestAnchor% + \def\tikzumlNoteDestAnchor{.\tikzumlNoteDestAnchorold}% + }% + % + \iftikzumlnoteWithoutCoords% + \node[text=\tikzumlNoteTextColor, text width=\tikzumlNoteTextWidth, font=\tikzumlDefaultFont, outer sep=0, inner xsep=1ex, inner ysep=3ex, /tikzuml/note/style] (note-\thetikzumlNoteNum-coord) {#3};% + \else% + \node[text=\tikzumlNoteTextColor, text width=\tikzumlNoteTextWidth, font=\tikzumlDefaultFont, outer sep=0, inner xsep=1ex, inner ysep=3ex, /tikzuml/note/style] (note-\thetikzumlNoteNum-coord) at (\tikzumlNoteX, \tikzumlNoteY) {#3};% + \fi% + \draw (note-\thetikzumlNoteNum-coord.north east) node[name=note-\thetikzumlNoteNum-right-top, below=2ex, coordinate] {};% + \draw (note-\thetikzumlNoteNum-coord.north east) node[name=note-\thetikzumlNoteNum-top-right, left=2ex, coordinate] {};% + \draw[draw=\tikzumlNoteDrawColor, fill=\tikzumlNoteFillColor] (note-\thetikzumlNoteNum-coord.south west) -- (note-\thetikzumlNoteNum-coord.south east) -- (note-\thetikzumlNoteNum-right-top.base) -- (note-\thetikzumlNoteNum-top-right.base) -- (note-\thetikzumlNoteNum-coord.north west) -- cycle;% + \node[text=\tikzumlNoteTextColor, text width=\tikzumlNoteTextWidth, outer sep=0, inner xsep=1ex, inner ysep=3ex, font=\tikzumlDefaultFont] (note-\thetikzumlNoteNum) at (note-\thetikzumlNoteNum-coord) {#3};% + \draw[draw=\tikzumlNoteDrawColor] (note-\thetikzumlNoteNum-right-top) -| (note-\thetikzumlNoteNum-top-right);% + % + \pgfmathsetmacro{\tikzumlNoteWeightT}{1.0-\tikzumlNoteWeight}% + \node (note-\thetikzumlNoteNum-middle) at (barycentric cs:note-\thetikzumlNoteNum-coord=\tikzumlNoteWeight,\tikzumlClassNodeName=\tikzumlNoteWeightT) {};% + % + \ifthenelse{\equal{\tikzumlNoteGeometry}{--}% + \OR\equal{\tikzumlNoteGeometry}{-|}% + \OR\equal{\tikzumlNoteGeometry}{|-}}{% + \edef\tikzumlnotepath{\tikzumlNoteGeometry} + }{% + \ifthenelse{\equal{\tikzumlNoteGeometry}{-|-}}{% + \ifthenelse{\equal{\tikzumlNoteArm}{auto}}{% + \edef\tikzumlnotepath{-- (note-\thetikzumlNoteNum-coord\tikzumlNoteSrcAnchor -| note-\thetikzumlNoteNum-middle.center) -- (note-\thetikzumlNoteNum-middle.center) -- (note-\thetikzumlNoteNum-middle.center |- \tikzumlClassNodeName\tikzumlNoteDestAnchor) --}% + }{% + \draw (note-\thetikzumlNoteNum-coord\tikzumlNoteSrcAnchor)+(\tikzumlNoteArm,0) node[name=note-\thetikzumlNoteNum-tmp] {}; + \edef\tikzumlnotepath{-- (note-\thetikzumlNoteNum-tmp.center) |-}% + }% + }{% + \ifthenelse{\equal{\tikzumlNoteGeometry}{|-|}}{% + \ifthenelse{\equal{\tikzumlNoteArm}{auto}}{% + \edef\tikzumlnotepath{-- (note-\thetikzumlNoteNum-coord\tikzumlNoteSrcAnchor |- note-\thetikzumlNoteNum-middle.center) -- (note-\thetikzumlNoteNum-middle.center) -- (note-\thetikzumlNoteNum-middle.center -| \tikzumlClassNodeName\tikzumlNoteDestAnchor) --}% + }{% + \draw (note-\thetikzumlNoteNum-coord\tikzumlNoteSrcAnchor)+(0,\tikzumlNoteArm) node[name=note-\thetikzumlNoteNum-tmp] {}; + \edef\tikzumlnotepath{-- (note-\thetikzumlNoteNum-tmp.center) -|}% + }% + + }{% + \errmessage{TIKZUML ERROR : Unknown geometry value !!! It should be in the following list : --, |-, -|, |-|, -|-}% + }% + }% + }% + % + \begin{pgfonlayer}{connections}% + \draw[dashed] (note-\thetikzumlNoteNum-coord\tikzumlNoteSrcAnchor) \tikzumlnotepath (\tikzumlClassNodeName\tikzumlNoteDestAnchor);% + \end{pgfonlayer}% + % + \stepcounter{tikzumlNoteNum}% +}% +% +% shortcuts for note with geometry +\newcommand{\umlHVnote}[3][]{% + \pgfkeys{/tikzuml/note/.cd,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{geometry}}{% + \errmessage{TIKZUML ERROR : in umlHVnote, forbidden option geometry}% + }{}% + }}% + \pgfkeys{/tikzuml/note/.cd, #1}% + \umlnote[geometry=-|, #1]{#2}{#3}% +}% +\newcommand{\umlVHnote}[3][]{% + \pgfkeys{/tikzuml/note/.cd,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{geometry}}{% + \errmessage{TIKZUML ERROR : in umlVHnote, forbidden option geometry}% + }{}% + }}% + \pgfkeys{/tikzuml/note/.cd, #1}% + \umlnote[geometry=|-, #1]{#2}{#3}% +}% +\newcommand{\umlVHVnote}[3][]{% + \pgfkeys{/tikzuml/note/.cd,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{geometry}}{% + \errmessage{TIKZUML ERROR : in umlVHVnote, forbidden option geometry}% + }{}% + }}% + \pgfkeys{/tikzuml/note/.cd, #1}% + \umlnote[geometry=|-|, #1]{#2}{#3}% +}% +\newcommand{\umlHVHnote}[3][]{% + \pgfkeys{/tikzuml/note/.cd,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{geometry}}{% + \errmessage{TIKZUML ERROR : in umlHVHnote, forbidden option geometry}% + }{}% + }}% + \pgfkeys{/tikzuml/note/.cd, #1}% + \umlnote[geometry=-|-, #1]{#2}{#3}% +}% +% +% define a uml association class (command) +% args : name of the class +% attributes of the class +% operations of the class +% optional : x,y: coordinates of the class +% width: width of the class node +% type: type of of class (class, interface, typedef, enum) +% template: template parameters +% name: name of the class node +% geometry: geometry of the line +% weight: barycentric weight of the middle part when geometry is a 3-line +% arm: length of first part when geometry id a 3-line +% anchor1, anchor2: src/dest anchors on linked classes +% style: style of the association class (association, aggregation, composition, inherit, ...) +% draw, fill, fill template, text: colors +% style: to manage every default TikZ option +% no coords: to tell that the class position is defined relatively +% to another node (automatically used with TikZ options above, below, left, right, below left, ...) +\newcommand{\umlassocclass}[5][]{% + \pgfkeys{/tikzuml/assocclass/.cd, x/.initial=\tikzumlDefaultX, y/.initial=\tikzumlDefaultX, + width/.initial=\tikzumlClassDefaultWidth, type/.initial=\tikzumlClassDefaultType, style/.style={},% + template/.initial={}, name/.initial=tikzumlEmpty, geometry/.initial=\tikzumlRelationDefaultGeometry,% + weight/.initial=\tikzumlRelationDefaultWeight, arm/.initial=auto,% + anchor1/.initial=tikzumlEmpty, anchor2/.initial=tikzumlEmpty,% + draw/.initial=\tikzumlDefaultDrawColor,% + fill template/.initial=\tikzumlClassTemplateFillColorDefaultFillColor,% + fill/.initial=\tikzumlClassDefaultFillColor,% + text/.initial=\tikzumlDefaultTextColor,% + no coords/.is if=tikzumlassocclassWithoutCoords,% + no coords=false,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{above}\OR% + \equal{\keyname}{left}\OR% + \equal{\keyname}{below}\OR% + \equal{\keyname}{right}\OR% + \equal{\keyname}{above left}\OR% + \equal{\keyname}{above right}\OR% + \equal{\keyname}{below left}\OR% + \equal{\keyname}{below right}}{% + \IfSubStr{\keyvalue}{ of }{% + \pgfkeys{/tikzuml/assocclass/.cd, no coords}% + }{}% + }{}% + \ifx\keyvalue\pgfkeysnovalue% + \pgfkeys{/tikzuml/assocclass/.cd, style/.append style/.expand once={\keyname}}% + \else% + \pgfkeys{/tikzuml/assocclass/.cd, style/.append style/.expand twice={\expandafter\keyname\expandafter=\keyvalue}}% + \fi% + %\errmessage{TIKZUML ERROR : in umlassocclass, invalid option \keyname}% + }% + }% + % + \pgfkeys{/tikzuml/assocclass/.cd,#1}% + \pgfkeys{/tikzuml/assocclass/.cd, x/.get=\tikzumlAssocClassX, y/.get=\tikzumlAssocClassY,% + width/.get=\tikzumlAssocClassMinimumWidth, type/.get=\tikzumlAssocClassTypeTmp,% + template/.get=\tikzumlAssocClassTemplateParam,% + name/.get=\tikzumlAssocClassName, geometry/.get=\tikzumlAssocClassGeometry,% + weight/.get=\tikzumlAssocClassWeight, arm/.get=\tikzumlAssocClassArm,% + anchor1/.get=\tikzumlAssocClassSrcAnchor,% + anchor2/.get=\tikzumlAssocClassDestAnchor,% + draw/.get=\tikzumlAssocClassDrawColor, fill/.get=\tikzumlAssocClassFillColor,% + text/.get=\tikzumlAssocClassTextColor, fill template/.get=\tikzumlAssocClassTemplateFillColor% + }% + % + \ifthenelse{\equal{\tikzumlAssocClassTypeTmp}{class}\OR\equal{\tikzumlAssocClassTypeTmp}{abstract}}{% + \def\tikzumlAssocClassType{}% + }{% + \def\tikzumlAssocClassType{$\ll$\tikzumlAssocClassTypeTmp$\gg$ \\}% + }% + % + \ifthenelse{\equal{\tikzumlAssocClassTemplateParam}{}}{% + \def\tikzumlAssocClassVPadding{}% + \def\tikzumlAssocClassHPadding{}% + }{% + \def\tikzumlAssocClassVPadding{\vspace{0.1em} \\}% + \def\tikzumlAssocClassHPadding{\hspace{0.5ex} $ $}% + }% + % + \def\tikzumlAssocClassName{#2}% + \def\tikzumlAssocClassRelationName{#3}% + % + \begingroup% + \def\_{_}\edef\x{\endgroup% + \def\noexpand\tikzumlAssocClassNodeName{\tikzumlAssocClassName}}\x% + % + \begingroup% + \def\_{_}\edef\x{\endgroup% + \def\noexpand\tikzumlAssocClassRelationNodeName{\tikzumlAssocClassRelationName}}\x% + % + \ifthenelse{\equal{\tikzumlAssocClassName}{tikzumlEmpty}}{}{% + \def\tikzumlAssocClassNodeName{\tikzumlAssocClassName}% + }% + % + \StrSubstitute{\tikzumlAssocClassNodeName}{:}{@COLON@}[\tikzumlAssocClassNodeName]% + \StrSubstitute{\tikzumlAssocClassNodeName}{\_}{@UNDERSCORE@}[\tikzumlAssocClassNodeName]% + % + \ifthenelse{\equal{\tikzumlAssocClassTypeTmp}{abstract}}{% + \let\tikzumlAssocClassNameOld\tikzumlAssocClassName% + \def\tikzumlAssocClassName{{\it \tikzumlAssocClassNameOld}}% + }{}% + % + \def\tikzumlAssocClassPos{\tikzumlAssocClassX,\tikzumlAssocClassY}% + \def\tikzumlAssocClassAttributes{#4}% + \def\tikzumlAssocClassOperations{#5}% + % + % def anchors macros + \ifthenelse{\equal{\tikzumlAssocClassSrcAnchor}{tikzumlEmpty}}{% + \def\tikzumlAssocClassSrcAnchor{}% + }{% + \let\tikzumlAssocClassSrcAnchorold\tikzumlAssocClassSrcAnchor% + \def\tikzumlAssocClassSrcAnchor{.\tikzumlAssocClassSrcAnchorold}% + }% + % + \ifthenelse{\equal{\tikzumlAssocClassDestAnchor}{tikzumlEmpty}}{% + \def\tikzumlAssocClassDestAnchor{}% + }{% + \let\tikzumlAssocClassDestAnchorold\tikzumlAssocClassDestAnchor% + \def\tikzumlAssocClassDestAnchor{.\tikzumlAssocClassDestAnchorold}% + }% + % + \iftikzumlassocclassWithoutCoords% + \node[tikzuml class style, draw=\tikzumlAssocClassDrawColor, fill=\tikzumlAssocClassFillColor, text=\tikzumlAssocClassTextColor, font=\tikzumlDefaultFont, minimum width=\tikzumlAssocClassMinimumWidth, /tikzuml/assocclass/style] (\tikzumlAssocClassNodeName) {\begin{tabular}{c}\tikzumlAssocClassVPadding \tikzumlAssocClassType \tikzumlAssocClassHPadding \textbf{\tikzumlAssocClassName} \tikzumlAssocClassHPadding \end{tabular}% + \nodepart{second}% + \begin{tabular}{l}% + \tikzumlAssocClassAttributes% + \end{tabular}% + \nodepart{third}% + \begin{tabular}{l}% + \tikzumlAssocClassOperations% + \end{tabular}% + };% + \else% + \node[tikzuml class style, draw=\tikzumlAssocClassDrawColor, fill=\tikzumlAssocClassFillColor, text=\tikzumlAssocClassTextColor, font=\tikzumlDefaultFont, minimum width=\tikzumlAssocClassMinimumWidth, /tikzuml/assocclass/style] (\tikzumlAssocClassNodeName) at (\tikzumlAssocClassPos) {\begin{tabular}{c}\tikzumlAssocClassVPadding \tikzumlAssocClassType \tikzumlAssocClassHPadding \textbf{\tikzumlAssocClassName} \tikzumlAssocClassHPadding \end{tabular}% + \nodepart{second}% + \begin{tabular}{l}% + \tikzumlAssocClassAttributes% + \end{tabular}% + \nodepart{third}% + \begin{tabular}{l}% + \tikzumlAssocClassOperations% + \end{tabular}% + };% + \fi% + % + \ifthenelse{\equal{\tikzumlAssocClassTemplateParam}{}}{}{% + \draw (\tikzumlAssocClassNodeName.north east) node[tikzuml template style, name=\tikzumlAssocClassNodeName-template, draw=\tikzumlAssocClassDrawColor, fill=\tikzumlAssocClassTemplateFillColor, text=\tikzumlAssocClassTextColor, font=\tikzumlDefaultFont] {\tikzumlAssocClassTemplateParam};% + }% + % + \pgfmathsetmacro{\tikzumlAssocClassWeightT}{1.0-\tikzumlAssocClassWeight} + \node (\tikzumlAssocClassNodeName-middle) at (barycentric cs:\tikzumlAssocClassNodeName=\tikzumlAssocClassWeight,\tikzumlAssocClassRelationNodeName=\tikzumlAssocClassWeightT) {};% + % + \ifthenelse{\equal{\tikzumlAssocClassGeometry}{--}\OR\equal{\tikzumlAssocClassGeometry}{-|}\OR\equal{\tikzumlAssocClassGeometry}{|-}}{% + \edef\tikzumlassocclasspath{\tikzumlAssocClassGeometry} + }{% + \ifthenelse{\equal{\tikzumlAssocClassGeometry}{-|-}}{% + \ifthenelse{\equal{\tikzumlAssocClassArm}{auto}}{% + \edef\tikzumlassocclasspath{-- (\tikzumlAssocClassNodeName\tikzumlAssocClassSrcAnchor -| \tikzumlAssocClassNodeName-middle.center) -- (\tikzumlAssocClassNodeName-middle.center) -- (\tikzumlAssocClassNodeName-middle.center |- \tikzumlAssocClassRelationNodeName\tikzumlAssocClassDestAnchor) --}% + }{% + \draw (\tikzumlAssocClassNodeName\tikzumlAssocClassSrcAnchor)+(\tikzumlAssocClassArm,0) node[name=\tikzumlAssocClassNodeName-tmp] {}; + \edef\tikzumlnotepath{-- (\tikzumlAssocClassNodeName-tmp.center) |-}% + }% + }{% + \ifthenelse{\equal{\tikzumlAssocClassGeometry}{|-|}}{% + \ifthenelse{\equal{\tikzumlAssocClassArm}{auto}}{% + \edef\tikzumlassocclasspath{-- (\tikzumlAssocClassNodeName\tikzumlAssocClassSrcAnchor |- \tikzumlAssocClassNodeName-middle.center) -- (\tikzumlAssocClassNodeName-middle.center) -- (\tikzumlAssocClassNodeName-middle.center -| \tikzumlAssocClassRelationNodeName\tikzumlAssocClassDestAnchor) --}% + }{% + \draw (\tikzumlAssocClassNodeName\tikzumlAssocClassSrcAnchor)+(0,\tikzumlAssocClassArm) node[name=\tikzumlAssocClassNodeName-tmp] {}; + \edef\tikzumlassocclasspath{-- (\thetikzumlAssocClassNodeName-tmp.center) -|}% + }% + + }{% + \errmessage{TIKZUML ERROR : Unknown geometry value !!! It should be in the following list : --, |-, -|, |-|, -|-}% + }% + }% + }% + % + \begin{pgfonlayer}{connections}% + \draw[dashed] (\tikzumlAssocClassNodeName\tikzumlAssocClassSrcAnchor) \tikzumlassocclasspath (\tikzumlAssocClassRelationNodeName\tikzumlAssocClassDestAnchor);% + \end{pgfonlayer}% + % + % add to fit + \ifnum\c@tikzumlPackageLevel>0% + \edef\tikzumlPackageFitOld{\csname tikzumlPackageFit\tikzumlPackage@parent @@\tikzumlPackage@fitname\endcsname}% + \ifthenelse{\equal{\tikzumlAssocClassTemplateParam}{}}{% + \expandafter\xdef\csname tikzumlPackageFit\tikzumlPackage@parent @@\tikzumlPackage@fitname\endcsname{\tikzumlPackageFitOld (\tikzumlAssocClassNodeName)(\tikzumlAssocClassNodeName-middle)}% + }{% + \expandafter\xdef\csname tikzumlPackageFit\tikzumlPackage@parent @@\tikzumlPackage@fitname\endcsname{\tikzumlPackageFitOld (\tikzumlAssocClassNodeName) (\tikzumlAssocClassNodeName-template)(\tikzumlAssocClassNodeName-middle)}% + }% + \stepcounter{tikzumlPackageClassNum}% + \fi% +}% +% +% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% use case diagrams % +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +\tikzstyle{tikzuml usecase style}=[ellipse, text centered]% +\tikzstyle{tikzuml actor style}=[ellipse, inner sep=0, outer sep=0]% +% +\newcounter{tikzumlSystemUseCaseNum}% +\newcounter{tikzumlSystemLevel}% +\newcounter{tikzumlUseCaseNum}% +\newcounter{tikzumlActorNum}% +% +\newif\iftikzumlusecaseWithoutCoords% +\newif\iftikzumlactorWithoutCoords% +% +% define a system +% arg : name +% optional : x, y: coordinates of the system +% draw, fill, text: colors +\newenvironment{umlsystem}[2][]{% + \gdef\tikzumlSystemFit{}% + \def\tikzumlSystemName{#2}% + \setcounter{tikzumlSystemUseCaseNum}{0}% + % + \pgfkeys{/tikzuml/system/.cd, x/.initial=\tikzumlDefaultX, y/.initial=\tikzumlDefaultX,% + draw/.initial=\tikzumlDefaultDrawColor, fill/.initial=\tikzumlSystemDefaultFillColor,% + text/.initial=\tikzumlDefaultTextColor,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \errmessage{TIKZUML ERROR : in umlsystem, invalid option \keyname}% + }% + }% + % + \pgfkeys{/tikzuml/system/.cd, #1}% + \pgfkeys{/tikzuml/system/.cd, x/.get=\tikzumlSystemXShift, y/.get=\tikzumlSystemYShift,% + draw/.get=\tikzumlSystemDrawColor, fill/.get=\tikzumlSystemFillColor,% + text/.get=\tikzumlSystemTextColor}% + % + \stepcounter{tikzumlSystemLevel}% + % + \begin{scope}[xshift=\tikzumlSystemXShift cm, yshift=\tikzumlSystemYShift cm]% +}{% + \addtocounter{tikzumlSystemLevel}{-1}% + % if contains no usecase, one define a fictive node to enable the fit option + \ifnum\c@tikzumlSystemUseCaseNum=0% + \node[inner xsep=10ex, inner ysep=1em] (\tikzumlSystemName-root) at (0,0) {};% + \xdef\tikzumlSystemFit{(\tikzumlSystemName-root)}% + \fi% + % + \begin{pgfonlayer}{background}% + \node[inner ysep=1em, inner xsep=2ex, fit = \tikzumlSystemFit] (\tikzumlSystemName-tmp) {};% + \node[text=\tikzumlSystemTextColor, font=\tikzumlDefaultFont] (\tikzumlSystemName-caption-tmp) at (\tikzumlSystemName-tmp.north) {\tikzumlSystemName};% + \node[draw=\tikzumlSystemDrawColor, fill=\tikzumlSystemFillColor, text=\tikzumlSystemTextColor, font=\tikzumlDefaultFont, inner ysep=1em, inner xsep=2ex, fit = (\tikzumlSystemName-tmp) (\tikzumlSystemName-caption-tmp)] (\tikzumlSystemName) {};% + \node[text=\tikzumlSystemTextColor, font=\tikzumlDefaultFont] (\tikzumlSystemName-caption) at (\tikzumlSystemName-caption-tmp.north) {\tikzumlSystemName};% + \end{pgfonlayer}% + \end{scope}% + % +}% +% +% define a use case +% arg : label of the use case +% optional : x, y: coordinates of the use case +% name: name of the node +% width: node width +% draw, fill, text: colors +% style: to manage every default TikZ option +% no coords: to tell that the use case position is defined relatively +% to another node (automatically used with TikZ options above, below, left, right, below left, ...) +\newcommand{\umlusecase}[2][]{% + \stepcounter{tikzumlUseCaseNum}% + \pgfkeys{/tikzuml/usecase/.cd, x/.initial=\tikzumlDefaultX, y/.initial=\tikzumlDefaultX, width/.initial=auto,% + name/.initial=usecase-\thetikzumlUseCaseNum,% + draw/.initial=\tikzumlDefaultDrawColor, fill/.initial=\tikzumlUseCaseDefaultFillColor,% + text/.initial=\tikzumlDefaultTextColor, style/.style={},% + no coords/.is if=tikzumlusecaseWithoutCoords,% + no coords=false,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{above}\OR% + \equal{\keyname}{left}\OR% + \equal{\keyname}{below}\OR% + \equal{\keyname}{right}\OR% + \equal{\keyname}{above left}\OR% + \equal{\keyname}{above right}\OR% + \equal{\keyname}{below left}\OR% + \equal{\keyname}{below right}}{% + \IfSubStr{\keyvalue}{ of }{% + \pgfkeys{/tikzuml/usecase/.cd, no coords}% + }{}% + }{}% + \ifx\keyvalue\pgfkeysnovalue% + \pgfkeys{/tikzuml/usecase/.cd, style/.append style/.expand once={\keyname}}% + \else% + \pgfkeys{/tikzuml/usecase/.cd, style/.append style/.expand twice={\expandafter\keyname\expandafter=\keyvalue}}% + \fi% + %\errmessage{TIKZUML ERROR : in umlusecase, invalid option \keyname}% + }% + }% + \pgfkeys{/tikzuml/usecase/.cd, #1}% + \pgfkeys{/tikzuml/usecase/.cd, x/.get=\tikzumlUseCaseX, y/.get=\tikzumlUseCaseY, width/.get=\tikzumlUseCaseTextWidth,% + name/.get=\tikzumlUseCaseName,% + draw/.get=\tikzumlUseCaseDrawColor, fill/.get=\tikzumlUseCaseFillColor,% + text/.get=\tikzumlUseCaseTextColor% + }% + % + \def\tikzumlUseCaseText{#2}% + % + \def\tikzumlUseCasePos{\tikzumlUseCaseX,\tikzumlUseCaseY}% + % + \ifthenelse{\equal{\tikzumlUseCaseTextWidth}{auto}}{% + \iftikzumlusecaseWithoutCoords% + \node[tikzuml usecase style, draw=\tikzumlUseCaseDrawColor, fill=\tikzumlUseCaseFillColor, text=\tikzumlUseCaseTextColor, font=\tikzumlDefaultFont, /tikzuml/usecase/style] (\tikzumlUseCaseName) {\tikzumlUseCaseText};% + \else% + \node[tikzuml usecase style, draw=\tikzumlUseCaseDrawColor, fill=\tikzumlUseCaseFillColor, text=\tikzumlUseCaseTextColor, font=\tikzumlDefaultFont, /tikzuml/usecase/style] (\tikzumlUseCaseName) at (\tikzumlUseCasePos) {\tikzumlUseCaseText};% + \fi% + }{% + \iftikzumlusecaseWithoutCoords% + \node[tikzuml usecase style, draw=\tikzumlUseCaseDrawColor, fill=\tikzumlUseCaseFillColor, text=\tikzumlUseCaseTextColor, font=\tikzumlDefaultFont, text width=\tikzumlUseCaseTextWidth, /tikzuml/usecase/style] (\tikzumlUseCaseName) {\tikzumlUseCaseText};% + \else% + \node[tikzuml usecase style, draw=\tikzumlUseCaseDrawColor, fill=\tikzumlUseCaseFillColor, text=\tikzumlUseCaseTextColor, font=\tikzumlDefaultFont, text width=\tikzumlUseCaseTextWidth, /tikzuml/usecase/style] (\tikzumlUseCaseName) at (\tikzumlUseCasePos) {\tikzumlUseCaseText};% + \fi% + }% + % + % add to fit + \ifnum\c@tikzumlSystemLevel>0% + \let\tikzumlSystemFitOld\tikzumlSystemFit% + \xdef\tikzumlSystemFit{\tikzumlSystemFitOld (\tikzumlUseCaseName)}% + \stepcounter{tikzumlSystemUseCaseNum}% + \fi% +}% +% +% define the actor symbol +% optional : global tikzpicture styles +\newcommand{\picturedactor}[1]{% + \pgfkeys{/tikzuml/picactor/.cd, scale/.initial=1, .unknown/.code={}}% + \pgfkeys{/tikzuml/picactor/.cd,#1}% + \pgfkeys{/tikzuml/picactor/.cd, scale/.get=\tikzumlPicturedActorScale}% + % + \begin{tikzpicture}[#1]% + \coordinate (head) at (0,4ex);% + \coordinate (left-hand) at (-2ex,2ex);% + \coordinate (right-hand) at (2ex,2ex);% + \coordinate (left-foot) at (-2ex,-2ex);% + \coordinate (right-foot) at (2ex,-2ex);% + \coordinate (empty) at (0,-3ex);% + \draw (empty) (0,0) -- (head);% + \draw (left-hand) -- (right-hand);% + \draw (0,0) -- (left-foot) (0,0) -- (right-foot);% + \node[fill, draw, circle, inner sep=\tikzumlPicturedActorScale*0.3333ex, minimum size=\tikzumlPicturedActorScale*2ex, anchor=base] at (head) {};% + \end{tikzpicture}% +}% +% +% define an actor +% arg : var name +% optional : x, y: coordinates of the actor +% scale: scale factor of the actor symbol +% below: distance between the actor symbol and its name below +% draw, text: colors +% style: to manage every default TikZ option +% no coords: to tell that the actor position is defined relatively +% to another node (automatically used with TikZ options above, below, left, right, below left, ...) +\newcommand{\umlactor}[2][]{% + \stepcounter{tikzumlActorNum}% + \pgfkeys{/tikzuml/actor/.cd, x/.initial=\tikzumlDefaultX, y/.initial=\tikzumlDefaultX, scale/.initial=1, below/.initial=\tikzumlActorDefaultBelow,% + draw/.initial=\tikzumlDefaultDrawColor, text/.initial=\tikzumlDefaultTextColor,% + style/.style={},% + no coords/.is if=tikzumlactorWithoutCoords,% + no coords=false,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{above}\OR% + \equal{\keyname}{left}\OR% + \equal{\keyname}{below}\OR% + \equal{\keyname}{right}\OR% + \equal{\keyname}{above left}\OR% + \equal{\keyname}{above right}\OR% + \equal{\keyname}{below left}\OR% + \equal{\keyname}{below right}}{% + \IfSubStr{\keyvalue}{ of }{% + \pgfkeys{/tikzuml/actor/.cd, no coords}% + }{}% + }{}% + \ifx\keyvalue\pgfkeysnovalue% + \pgfkeys{/tikzuml/actor/.cd, style/.append style/.expand once={\keyname}}% + \else% + \pgfkeys{/tikzuml/actor/.cd, style/.append style/.expand twice={\expandafter\keyname\expandafter=\keyvalue}}% + \fi% + %\errmessage{TIKZUML ERROR : in umlactor, invalid option \keyname}% + }% + }% + \pgfkeys{/tikzuml/actor/.cd, #1}% + \pgfkeys{/tikzuml/actor/.cd,% + x/.get=\tikzumlActorX, y/.get=\tikzumlActorY, scale/.get=\tikzumlActorScale,% + below/.get=\tikzumlActorBelow,% + draw/.get=\tikzumlActorDrawColor, text/.get=\tikzumlActorTextColor}% + % + \def\tikzumlActorName{#2}% + \begingroup% + \def\_{_}\edef\x{\endgroup% + \def\noexpand\tikzumlActorNodeName{\tikzumlActorName}}\x% + % + \def\tikzumlActorPos{\tikzumlActorX,\tikzumlActorY}% + % + \iftikzumlactorWithoutCoords% + \node[tikzuml actor style, text=\tikzumlActorTextColor, font=\tikzumlDefaultFont, /tikzuml/actor/style] (\tikzumlActorNodeName) {\picturedactor{scale=\tikzumlActorScale, fill=white, draw=\tikzumlActorDrawColor, thick}};% + \else% + \node[tikzuml actor style, text=\tikzumlActorTextColor, font=\tikzumlDefaultFont, /tikzuml/actor/style] (\tikzumlActorNodeName) at (\tikzumlActorPos) {\picturedactor{scale=\tikzumlActorScale, fill=white, draw=\tikzumlActorDrawColor, thick}};% + \fi% + \node[text=\tikzumlActorTextColor, font=\tikzumlDefaultFont, below=\tikzumlActorScale*\tikzumlActorBelow] at (\tikzumlActorNodeName) {\tikzumlActorName};% + % +}% + +% shortcuts for include and extend relation +\newcommand{\umlinclude}[3][]{% + \pgfkeys{/tikzuml/includerelation/.cd,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{stereo}}{% + \errmessage{TIKZUML ERROR : in umlinclude, forbidden option stereo}% + }{% + \ifthenelse{\equal{\keyname}{geometry}}{% + \errmessage{TIKZUML ERROR : in umlinclude, forbidden option geometry}% + }{}% + }% + }}% + \pgfkeys{/tikzuml/includerelation/.cd, #1}% + \umlrelation[stereo=include, style={tikzuml dependency style}, #1]{#2}{#3}% +}% +\newcommand{\umlextend}[3][]{% + \pgfkeys{/tikzuml/extendrelation/.cd,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{stereo}}{% + \errmessage{TIKZUML ERROR : in umlextend, forbidden option stereo}% + }{% + \ifthenelse{\equal{\keyname}{geometry}}{% + \errmessage{TIKZUML ERROR : in umlextend, forbidden option geometry}% + }{}% + }% + }}% + \pgfkeys{/tikzuml/extendrelation/.cd, #1}% + \umlrelation[stereo=extend, style={tikzuml dependency style}, #1]{#2}{#3}% +}% +% +\newcommand{\umlHVinclude}[3][]{% + \pgfkeys{/tikzuml/includerelation/.cd,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{stereo}}{% + \errmessage{TIKZUML ERROR : in umlHVinclude, forbidden option stereo}% + }{% + \ifthenelse{\equal{\keyname}{geometry}}{% + \errmessage{TIKZUML ERROR : in umlHVinclude, forbidden option geometry}% + }{}% + }% + }}% + \pgfkeys{/tikzuml/includerelation/.cd, #1}% + \umlrelation[geometry=-|, stereo=include, style={tikzuml dependency style}, #1]{#2}{#3}% +}% +\newcommand{\umlHVextend}[3][]{% + \pgfkeys{/tikzuml/extendrelation/.cd,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{stereo}}{% + \errmessage{TIKZUML ERROR : in umlHVextend, forbidden option stereo}% + }{% + \ifthenelse{\equal{\keyname}{geometry}}{% + \errmessage{TIKZUML ERROR : in umlHVextend, forbidden option geometry}% + }{}% + }% + }}% + \pgfkeys{/tikzuml/extendrelation/.cd, #1}% + \umlrelation[geometry=-|, stereo=extend, style={tikzuml dependency style}, #1]{#2}{#3}% +}% +% +\newcommand{\umlVHinclude}[3][]{% + \pgfkeys{/tikzuml/includerelation/.cd,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{stereo}}{% + \errmessage{TIKZUML ERROR : in umlVHinclude, forbidden option stereo}% + }{% + \ifthenelse{\equal{\keyname}{geometry}}{% + \errmessage{TIKZUML ERROR : in umlVHinclude, forbidden option geometry}% + }{}% + }% + }}% + \pgfkeys{/tikzuml/includerelation/.cd, #1}% + \umlrelation[geometry=|-, stereo=include, style={tikzuml dependency style}, #1]{#2}{#3}% +}% +\newcommand{\umlVHextend}[3][]{% + \pgfkeys{/tikzuml/extendrelation/.cd,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{stereo}}{% + \errmessage{TIKZUML ERROR :in umlVHextend, forbidden option stereo}% + }{% + \ifthenelse{\equal{\keyname}{geometry}}{% + \errmessage{TIKZUML ERROR : in umlVHextend, forbidden option geometry}% + }{}% + }% + }}% + \pgfkeys{/tikzuml/extendrelation/.cd, #1}% + \umlrelation[geometry=|-, stereo=extend, style={tikzuml dependency style}, #1]{#2}{#3}% +}% +% +\newcommand{\umlHVHinclude}[3][]{% + \pgfkeys{/tikzuml/includerelation/.cd,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{stereo}}{% + \errmessage{TIKZUML ERROR : in umlHVHinclude, forbidden option stereo}% + }{% + \ifthenelse{\equal{\keyname}{geometry}}{% + \errmessage{TIKZUML ERROR : in umlHVHinclude, forbidden option geometry}% + }{}% + }% + }}% + \pgfkeys{/tikzuml/includerelation/.cd, #1}% + \umlrelation[geometry=-|-, stereo=include, style={tikzuml dependency style}, #1]{#2}{#3}% +}% +\newcommand{\umlHVHextend}[3][]{% + \pgfkeys{/tikzuml/extendrelation/.cd,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{stereo}}{% + \errmessage{TIKZUML ERROR : in umlHVHextend, forbidden option stereo}% + }{% + \ifthenelse{\equal{\keyname}{geometry}}{% + \errmessage{TIKZUML ERROR : in umlHVHextend, forbidden option geometry}% + }{}% + }% + }}% + \pgfkeys{/tikzuml/extendrelation/.cd, #1}% + \umlrelation[geometry=-|-, stereo=extend, style={tikzuml dependency style}, #1]{#2}{#3}% +}% +% +\newcommand{\umlVHVinclude}[3][]{% + \pgfkeys{/tikzuml/includerelation/.cd,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{stereo}}{% + \errmessage{TIKZUML ERROR : in umlVHVinclude, forbidden option stereo}% + }{% + \ifthenelse{\equal{\keyname}{geometry}}{% + \errmessage{TIKZUML ERROR : in umlVHVinclude, forbidden option geometry}% + }{}% + }% + }}% + \pgfkeys{/tikzuml/includerelation/.cd, #1}% + \umlrelation[geometry=|-|, stereo=include, style={tikzuml dependency style}, #1]{#2}{#3}% +}% +\newcommand{\umlVHVextend}[3][]{% + \pgfkeys{/tikzuml/extendrelation/.cd,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{stereo}}{% + \errmessage{TIKZUML ERROR : in umlVHVextend, forbidden option stereo}% + }{% + \ifthenelse{\equal{\keyname}{geometry}}{% + \errmessage{TIKZUML ERROR : in umlVHVextend, forbidden option geometry}% + }{}% + }% + }}% + \pgfkeys{/tikzuml/extendrelation/.cd, #1}% + \umlrelation[geometry=|-|, stereo=extend, style={tikzuml dependency style}, #1]{#2}{#3}% +}% +% +\newcommand{\umlCNinclude}[4][]{% + \pgfkeys{/tikzuml/includerelation/.cd,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{stereo}}{% + \errmessage{TIKZUML ERROR : in umlCNinclude, forbidden option stereo}% + }{}% + }}% + \pgfkeys{/tikzuml/includerelation/.cd, #1}% + \umlCNrelation[stereo=include, style={tikzuml dependency style}, #1]{#2}{#3}{#4}% +}% +\newcommand{\umlCNextend}[4][]{% + \pgfkeys{/tikzuml/extendrelation/.cd,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{stereo}}{% + \errmessage{TIKZUML ERROR : in umlCNextend, forbidden option stereo}% + }{}% + }}% + \pgfkeys{/tikzuml/extendrelation/.cd, #1}% + \umlCNrelation[stereo=extend, style={tikzuml dependency style}, #1]{#2}{#3}{#4}% +}% +% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% state diagrams % +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +\tikzstyle{tikzuml state style}=[rectangle split, rectangle split parts=2, rounded corners, inner xsep=1.5ex]% +\tikzstyle{tikzuml transition style}=[color=\tikzumlDefaultDrawColor, rounded corners, -angle 45]% +% +\newcounter{tikzumlStateJoinNum}% +\newcounter{tikzumlStateDecisionNum}% +\newcounter{tikzumlStateInitialNum}% +\newcounter{tikzumlStateFinalNum}% +\newcounter{tikzumlStateEnterNum}% +\newcounter{tikzumlStateExitNum}% +\newcounter{tikzumlStateEndNum}% +\newcounter{tikzumlStateHistoryNum}% +\newcounter{tikzumlStateDeepHistoryNum}% +\newcounter{tikzumlStateLevel}% +\newcounter{tikzumlStateSubStateNum}% +\newcounter{tikzumlStateText}% +% +\newif\iftikzumlstatejoinWithoutCoords% +\newif\iftikzumlstatedecisionWithoutCoords% +\newif\iftikzumlstateinitialWithoutCoords% +\newif\iftikzumlstatefinalWithoutCoords% +\newif\iftikzumlstateenterWithoutCoords% +\newif\iftikzumlstateexitWithoutCoords% +\newif\iftikzumlstateendWithoutCoords% +\newif\iftikzumlstatehistoryWithoutCoords% +\newif\iftikzumlstatedeephistoryWithoutCoords% +\newif\iftikzumlstateWithoutCoords% +% +% define a uml join state +% args : name of the state +% optional : x,y: coordinates of the state +% width: width of the state node +% name: name of the state node +% color: color of the join symbol +% style: to manage every default TikZ option +% no coords: to tell that the state position is defined relatively +% to another node (automatically used with TikZ options above, below, left, right, below left, ...) +\newcommand{\umlstatejoin}[1][]{% + \pgfkeys{/tikzuml/statejoin/.cd, x/.initial=\tikzumlDefaultX, y/.initial=\tikzumlDefaultX, width/.initial=\tikzumlStateJoinDefaultWidth,% + name/.initial=statejoin-\thetikzumlStateJoinNum,% + color/.initial=\tikzumlDefaultDrawColor, style/.style={},% + no coords/.is if=tikzumlstatejoinWithoutCoords,% + no coords=false,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{above}\OR% + \equal{\keyname}{left}\OR% + \equal{\keyname}{below}\OR% + \equal{\keyname}{right}\OR% + \equal{\keyname}{above left}\OR% + \equal{\keyname}{above right}\OR% + \equal{\keyname}{below left}\OR% + \equal{\keyname}{below right}}{% + \IfSubStr{\keyvalue}{ of }{% + \pgfkeys{/tikzuml/statejoin/.cd, no coords}% + }{}% + }{}% + \ifx\keyvalue\pgfkeysnovalue% + \pgfkeys{/tikzuml/statejoin/.cd, style/.append style/.expand once={\keyname}}% + \else% + \pgfkeys{/tikzuml/statejoin/.cd, style/.append style/.expand twice={\expandafter\keyname\expandafter=\keyvalue}}% + \fi% + %\errmessage{TIKZUML ERROR : in umlstatejoin, invalid option \keyname}% + }% + }% + \pgfkeys{/tikzuml/statejoin/.cd, #1}% + \pgfkeys{/tikzuml/statejoin/.cd, x/.get=\tikzumlStateJoinX, y/.get=\tikzumlStateJoinY, width/.get=\tikzumlStateJoinMinimumWidth,% + name/.get=\tikzumlStateJoinName, color/.get=\tikzumlStateJoinColor% + }% + % + \def\tikzumlStateJoinPos{\tikzumlStateJoinX,\tikzumlStateJoinY}% + % + \iftikzumlstatejoinWithoutCoords% + \node[circle, minimum size=\tikzumlStateJoinMinimumWidth, draw=\tikzumlStateJoinColor, fill=\tikzumlStateJoinColor, /tikzuml/statejoin/style] (\tikzumlStateJoinName) {};% + \else% + \node[circle, minimum size=\tikzumlStateJoinMinimumWidth, draw=\tikzumlStateJoinColor, fill=\tikzumlStateJoinColor, /tikzuml/statejoin/style] (\tikzumlStateJoinName) at (\tikzumlStateJoinPos) {};% + \fi% + % + % add to fit + \ifnum\c@tikzumlStateLevel>0% + \edef\tikzumlStateFitOld{\csname tikzumlStateFit\tikzumlState@parent @@\tikzumlState@fitname\endcsname}% + \expandafter\xdef\csname tikzumlStateFit\tikzumlState@parent @@\tikzumlState@fitname\endcsname{\tikzumlStateFitOld (\tikzumlStateJoinName)}% + \stepcounter{tikzumlStateSubStateNum}% + \fi% + \stepcounter{tikzumlStateJoinNum}% +}% +% +% define a uml decision state +% args : name of the state +% optional : x,y: coordinates of the state +% width: width of the state node +% name: name of the state node +% color: color of the join symbol +% style: to manage every default TikZ option +% no coords: to tell that the state position is defined relatively +% to another node (automatically used with TikZ options above, below, left, right, below left, ...) +\newcommand{\umlstatedecision}[1][]{% + \pgfkeys{/tikzuml/statedecision/.cd, x/.initial=\tikzumlDefaultX, y/.initial=\tikzumlDefaultX, width/.initial=\tikzumlStateDecisionDefaultWidth,% + name/.initial=statedecision-\thetikzumlStateDecisionNum,% + color/.initial=\tikzumlDefaultDrawColor, style/.style={},% + no coords/.is if=tikzumlstatedecisionWithoutCoords,% + no coords=false,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{above}\OR% + \equal{\keyname}{left}\OR% + \equal{\keyname}{below}\OR% + \equal{\keyname}{right}\OR% + \equal{\keyname}{above left}\OR% + \equal{\keyname}{above right}\OR% + \equal{\keyname}{below left}\OR% + \equal{\keyname}{below right}}{% + \IfSubStr{\keyvalue}{ of }{% + \pgfkeys{/tikzuml/statedecision/.cd, no coords}% + }{}% + }{}% + \ifx\keyvalue\pgfkeysnovalue% + \pgfkeys{/tikzuml/statedecision/.cd, style/.append style/.expand once={\keyname}}% + \else% + \pgfkeys{/tikzuml/statedecision/.cd, style/.append style/.expand twice={\expandafter\keyname\expandafter=\keyvalue}}% + \fi% + %\errmessage{TIKZUML ERROR : in umlstatedecision, invalid option \keyname}% + }% + }% + \pgfkeys{/tikzuml/statedecision/.cd, #1}% + \pgfkeys{/tikzuml/statedecision/.cd, x/.get=\tikzumlStateDecisionX, y/.get=\tikzumlStateDecisionY, width/.get=\tikzumlStateDecisionMinimumWidth,% + name/.get=\tikzumlStateDecisionName, color/.get=\tikzumlStateDecisionColor% + }% + % + \def\tikzumlStateDecisionPos{\tikzumlStateDecisionX,\tikzumlStateDecisionY}% + % + \iftikzumlstatedecisionWithoutCoords% + \node[rectangle, rotate=45, minimum size=\tikzumlStateDecisionMinimumWidth, draw=\tikzumlStateDecisionColor, /tikzuml/statedecision/style] (\tikzumlStateDecisionName) {};% + \else% + \node[rectangle, rotate=45, minimum size=\tikzumlStateDecisionMinimumWidth, draw=\tikzumlStateDecisionColor, /tikzuml/statedecision/style] (\tikzumlStateDecisionName) at (\tikzumlStateDecisionPos) {};% + \fi% + % + % add to fit + \ifnum\c@tikzumlStateLevel>0% + \edef\tikzumlStateFitOld{\csname tikzumlStateFit\tikzumlState@parent @@\tikzumlState@fitname\endcsname}% + \expandafter\xdef\csname tikzumlStateFit\tikzumlState@parent @@\tikzumlState@fitname\endcsname{\tikzumlStateFitOld (\tikzumlStateDecisionName)}% + \stepcounter{tikzumlStateSubStateNum}% + \fi% + \stepcounter{tikzumlStateDecisionNum}% +}% +% +% define a uml initial state +% args : name of the state +% optional : x,y: coordinates of the state +% width: width of the state node +% name: name of the state node +% entry, do, exit: entry/do/exit action of the state +% color: color of the join symbol +% style: to manage every default TikZ option +% no coords: to tell that the state position is defined relatively +% to another node (automatically used with TikZ options above, below, left, right, below left, ...) +\newcommand{\umlstateinitial}[1][]{% + \pgfkeys{/tikzuml/stateinitial/.cd, x/.initial=\tikzumlDefaultX, y/.initial=\tikzumlDefaultX, width/.initial=\tikzumlStateInitialDefaultWidth,% + name/.initial=stateinitial-\thetikzumlStateInitialNum,% + color/.initial=\tikzumlDefaultDrawColor, style/.style={},% + no coords/.is if=tikzumlstateinitialWithoutCoords,% + no coords=false,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{above}\OR% + \equal{\keyname}{left}\OR% + \equal{\keyname}{below}\OR% + \equal{\keyname}{right}\OR% + \equal{\keyname}{above left}\OR% + \equal{\keyname}{above right}\OR% + \equal{\keyname}{below left}\OR% + \equal{\keyname}{below right}}{% + \IfSubStr{\keyvalue}{ of }{% + \pgfkeys{/tikzuml/stateinitial/.cd, no coords}% + }{}% + }{}% + \ifx\keyvalue\pgfkeysnovalue% + \pgfkeys{/tikzuml/stateinitial/.cd, style/.append style/.expand once={\keyname}}% + \else% + \pgfkeys{/tikzuml/stateinitial/.cd, style/.append style/.expand twice={\expandafter\keyname\expandafter=\keyvalue}}% + \fi% + %\errmessage{TIKZUML ERROR : in umlstateinitial, invalid option \keyname}% + }% + }% + \pgfkeys{/tikzuml/stateinitial/.cd, #1}% + \pgfkeys{/tikzuml/stateinitial/.cd, x/.get=\tikzumlStateInitialX, y/.get=\tikzumlStateInitialY, width/.get=\tikzumlStateInitialMinimumWidth,% + name/.get=\tikzumlStateInitialName, color/.get=\tikzumlStateInitialColor% + }% + % + \def\tikzumlStateInitialPos{\tikzumlStateInitialX,\tikzumlStateInitialY}% + % + \iftikzumlstateinitialWithoutCoords% + \node[circle, minimum size=\tikzumlStateInitialMinimumWidth, fill=\tikzumlStateInitialColor, /tikzuml/stateinitial/style] (\tikzumlStateInitialName) {};% + \else% + \node[circle, minimum size=\tikzumlStateInitialMinimumWidth, fill=\tikzumlStateInitialColor, /tikzuml/stateinitial/style] (\tikzumlStateInitialName) at (\tikzumlStateInitialPos) {};% + \fi% + % + % add to fit + \ifnum\c@tikzumlStateLevel>0% + \edef\tikzumlStateFitOld{\csname tikzumlStateFit\tikzumlState@parent @@\tikzumlState@fitname\endcsname}% + \expandafter\xdef\csname tikzumlStateFit\tikzumlState@parent @@\tikzumlState@fitname\endcsname{\tikzumlStateFitOld (\tikzumlStateInitialName)}% + \stepcounter{tikzumlStateSubStateNum}% + \fi% + \stepcounter{tikzumlStateInitialNum}% +}% +% +% define a uml final state +% args : name of the state +% optional : x,y: coordinates of the state +% width: width of the state node +% name: name of the state node +% color: color of the join symbol +% style: to manage every default TikZ option +% no coords: to tell that the state position is defined relatively +% to another node (automatically used with TikZ options above, below, left, right, below left, ...) +\newcommand{\umlstatefinal}[1][]{% + \pgfkeys{/tikzuml/statefinal/.cd, x/.initial=\tikzumlDefaultX, y/.initial=\tikzumlDefaultX, width/.initial=\tikzumlStateFinalDefaultWidth,% + name/.initial=statefinal-\thetikzumlStateFinalNum,% + color/.initial=\tikzumlDefaultDrawColor, style/.style={},% + no coords/.is if=tikzumlstatefinalWithoutCoords,% + no coords=false,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{above}\OR% + \equal{\keyname}{left}\OR% + \equal{\keyname}{below}\OR% + \equal{\keyname}{right}\OR% + \equal{\keyname}{above left}\OR% + \equal{\keyname}{above right}\OR% + \equal{\keyname}{below left}\OR% + \equal{\keyname}{below right}}{% + \IfSubStr{\keyvalue}{ of }{% + \pgfkeys{/tikzuml/statefinal/.cd, no coords}% + }{}% + }{}% + \ifx\keyvalue\pgfkeysnovalue% + \pgfkeys{/tikzuml/statefinal/.cd, style/.append style/.expand once={\keyname}}% + \else% + \pgfkeys{/tikzuml/statefinal/.cd, style/.append style/.expand twice={\expandafter\keyname\expandafter=\keyvalue}}% + \fi% + %\errmessage{TIKZUML ERROR : in umlstatefinal, invalid option \keyname}% + }% + }% + \pgfkeys{/tikzuml/statefinal/.cd, #1}% + \pgfkeys{/tikzuml/statefinal/.cd, x/.get=\tikzumlStateFinalX, y/.get=\tikzumlStateFinalY, width/.get=\tikzumlStateFinalMinimumWidth,% + name/.get=\tikzumlStateFinalName, color/.get=\tikzumlStateFinalColor% + }% + % + \def\tikzumlStateFinalPos{\tikzumlStateFinalX,\tikzumlStateFinalY}% + % + \iftikzumlstatefinalWithoutCoords% + \node[circle, minimum size=\tikzumlStateFinalMinimumWidth, draw=\tikzumlStateFinalColor, fill=\tikzumlStateFinalColor, double, double distance=0.1cm, /tikzuml/statefinal/style] (\tikzumlStateFinalName) {};% + \else% + \node[circle, minimum size=\tikzumlStateFinalMinimumWidth, draw=\tikzumlStateFinalColor, fill=\tikzumlStateFinalColor, double, double distance=0.1cm, /tikzuml/statefinal/style] (\tikzumlStateFinalName) at (\tikzumlStateFinalPos) {};% + \fi% + % + % add to fit + \ifnum\c@tikzumlStateLevel>0% + \edef\tikzumlStateFitOld{\csname tikzumlStateFit\tikzumlState@parent @@\tikzumlState@fitname\endcsname}% + \expandafter\xdef\csname tikzumlStateFit\tikzumlState@parent @@\tikzumlState@fitname\endcsname{\tikzumlStateFitOld (\tikzumlStateFinalName)}% + \stepcounter{tikzumlStateSubStateNum}% + \fi% + \stepcounter{tikzumlStateFinalNum}% +}% +% +% define a uml enter state +% args : name of the state +% optional : x,y: coordinates of the state +% width: width of the state node +% name: name of the state node +% color: color of the join symbol +% style: to manage every default TikZ option +% no coords: to tell that the state position is defined relatively +% to another node (automatically used with TikZ options above, below, left, right, below left, ...) +\newcommand{\umlstateenter}[1][]{% + \pgfkeys{/tikzuml/stateenter/.cd, x/.initial=\tikzumlDefaultX, y/.initial=\tikzumlDefaultX, width/.initial=\tikzumlStateEnterDefaultWidth,% + name/.initial=stateenter-\thetikzumlStateEnterNum,% + color/.initial=\tikzumlDefaultDrawColor, style/.style={},% + no coords/.is if=tikzumlstateenterWithoutCoords,% + no coords=false,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{above}\OR% + \equal{\keyname}{left}\OR% + \equal{\keyname}{below}\OR% + \equal{\keyname}{right}\OR% + \equal{\keyname}{above left}\OR% + \equal{\keyname}{above right}\OR% + \equal{\keyname}{below left}\OR% + \equal{\keyname}{below right}}{% + \IfSubStr{\keyvalue}{ of }{% + \pgfkeys{/tikzuml/stateenter/.cd, no coords}% + }{}% + }{}% + \ifx\keyvalue\pgfkeysnovalue% + \pgfkeys{/tikzuml/stateenter/.cd, style/.append style/.expand once={\keyname}}% + \else% + \pgfkeys{/tikzuml/stateenter/.cd, style/.append style/.expand twice={\expandafter\keyname\expandafter=\keyvalue}}% + \fi% + %\errmessage{TIKZUML ERROR : in umlstateenter, invalid option \keyname}% + }% + }% + \pgfkeys{/tikzuml/stateenter/.cd, #1}% + \pgfkeys{/tikzuml/stateenter/.cd, x/.get=\tikzumlStateEnterX, y/.get=\tikzumlStateEnterY, width/.get=\tikzumlStateEnterMinimumWidth,% + name/.get=\tikzumlStateEnterName, color/.get=\tikzumlStateEnterColor% + }% + % + \def\tikzumlStateEnterPos{\tikzumlStateEnterX,\tikzumlStateEnterY}% + % + \iftikzumlstateenterWithoutCoords% + \node[circle, minimum size=\tikzumlStateEnterMinimumWidth, draw=\tikzumlStateEnterColor, /tikzuml/stateenter/style] (\tikzumlStateEnterName) {};% + \else% + \node[circle, minimum size=\tikzumlStateEnterMinimumWidth, draw=\tikzumlStateEnterColor, /tikzuml/stateenter/style] (\tikzumlStateEnterName) at (\tikzumlStateEnterPos) {};% + \fi% + % + % add to fit + \ifnum\c@tikzumlStateLevel>0% + \edef\tikzumlStateFitOld{\csname tikzumlStateFit\tikzumlState@parent @@\tikzumlState@fitname\endcsname}% + \expandafter\xdef\csname tikzumlStateFit\tikzumlState@parent @@\tikzumlState@fitname\endcsname{\tikzumlStateFitOld (\tikzumlStateEnterName)}% + \stepcounter{tikzumlStateSubStateNum}% + \fi% + \stepcounter{tikzumlStateEnterNum}% +}% +% +% define a uml exit state +% args : name of the state +% optional : x,y: coordinates of the state +% width: width of the state node +% name: name of the state node +% color: color of the join symbol +% style: to manage every default TikZ option +% no coords: to tell that the state position is defined relatively +% to another node (automatically used with TikZ options above, below, left, right, below left, ...) +\newcommand{\umlstateexit}[1][]{% + \pgfkeys{/tikzuml/stateexit/.cd, x/.initial=\tikzumlDefaultX, y/.initial=\tikzumlDefaultX, width/.initial=\tikzumlStateExitDefaultWidth,% + name/.initial=stateexit-\thetikzumlStateExitNum,% + color/.initial=\tikzumlDefaultDrawColor, style/.style={},% + no coords/.is if=tikzumlstateexitWithoutCoords,% + no coords=false,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{above}\OR% + \equal{\keyname}{left}\OR% + \equal{\keyname}{below}\OR% + \equal{\keyname}{right}\OR% + \equal{\keyname}{above left}\OR% + \equal{\keyname}{above right}\OR% + \equal{\keyname}{below left}\OR% + \equal{\keyname}{below right}}{% + \IfSubStr{\keyvalue}{ of }{% + \pgfkeys{/tikzuml/stateexit/.cd, no coords}% + }{}% + }{}% + \ifx\keyvalue\pgfkeysnovalue% + \pgfkeys{/tikzuml/stateexit/.cd, style/.append style/.expand once={\keyname}}% + \else% + \pgfkeys{/tikzuml/stateexit/.cd, style/.append style/.expand twice={\expandafter\keyname\expandafter=\keyvalue}}% + \fi% + %\errmessage{TIKZUML ERROR : in umlstateexit, invalid option \keyname}% + }% + }% + \pgfkeys{/tikzuml/stateexit/.cd, #1}% + \pgfkeys{/tikzuml/stateexit/.cd, x/.get=\tikzumlStateExitX, y/.get=\tikzumlStateExitY, width/.get=\tikzumlStateExitMinimumWidth,% + name/.get=\tikzumlStateExitName, color/.get=\tikzumlStateExitColor% + }% + % + \def\tikzumlStateExitPos{\tikzumlStateExitX,\tikzumlStateExitY}% + % + \iftikzumlstateexitWithoutCoords% + \node[circle, minimum size=\tikzumlStateExitMinimumWidth, draw=\tikzumlStateExitColor, /tikzuml/stateexit/style] (\tikzumlStateExitName) {};% + \else% + \node[circle, minimum size=\tikzumlStateExitMinimumWidth, draw=\tikzumlStateExitColor, /tikzuml/stateexit/style] (\tikzumlStateExitName) at (\tikzumlStateExitPos) {};% + \fi% + \draw[draw=\tikzumlStateExitColor] (\tikzumlStateExitName.north east) -- (\tikzumlStateExitName.south west) (\tikzumlStateExitName.north west) -- (\tikzumlStateExitName.south east); + % + % add to fit + \ifnum\c@tikzumlStateLevel>0% + \edef\tikzumlStateFitOld{\csname tikzumlStateFit\tikzumlState@parent @@\tikzumlState@fitname\endcsname}% + \expandafter\xdef\csname tikzumlStateFit\tikzumlState@parent @@\tikzumlState@fitname\endcsname{\tikzumlStateFitOld (\tikzumlStateExitName)}% + \stepcounter{tikzumlStateSubStateNum}% + \fi% + \stepcounter{tikzumlStateExitNum}% +}% +% +% define a uml end state +% args : name of the state +% optional : x,y: coordinates of the state +% width: width of the state node +% name: name of the state node +% color: color of the join symbol +% style: to manage every default TikZ option +% no coords: to tell that the state position is defined relatively +% to another node (automatically used with TikZ options above, below, left, right, below left, ...) +\newcommand{\umlstateend}[1][]{% + \pgfkeys{/tikzuml/stateend/.cd, x/.initial=\tikzumlDefaultX, y/.initial=\tikzumlDefaultX, width/.initial=\tikzumlStateEndDefaultWidth,% + name/.initial=stateend-\thetikzumlStateEndNum,% + color/.initial=\tikzumlDefaultDrawColor, style/.style={},% + no coords/.is if=tikzumlstateendWithoutCoords,% + no coords=false,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{above}\OR% + \equal{\keyname}{left}\OR% + \equal{\keyname}{below}\OR% + \equal{\keyname}{right}\OR% + \equal{\keyname}{above left}\OR% + \equal{\keyname}{above right}\OR% + \equal{\keyname}{below left}\OR% + \equal{\keyname}{below right}}{% + \IfSubStr{\keyvalue}{ of }{% + \pgfkeys{/tikzuml/stateend/.cd, no coords}% + }{}% + }{}% + \ifx\keyvalue\pgfkeysnovalue% + \pgfkeys{/tikzuml/stateend/.cd, style/.append style/.expand once={\keyname}}% + \else% + \pgfkeys{/tikzuml/stateend/.cd, style/.append style/.expand twice={\expandafter\keyname\expandafter=\keyvalue}}% + \fi% + %\errmessage{TIKZUML ERROR : in umlstateend, invalid option \keyname}% + }% + }% + \pgfkeys{/tikzuml/stateend/.cd, #1}% + \pgfkeys{/tikzuml/stateend/.cd, x/.get=\tikzumlStateEndX, y/.get=\tikzumlStateEndY, width/.get=\tikzumlStateEndMinimumWidth,% + name/.get=\tikzumlStateEndName, color/.get=\tikzumlStateEndColor% + }% + % + \def\tikzumlStateEndPos{\tikzumlStateEndX,\tikzumlStateEndY}% + % + \iftikzumlstateendWithoutCoords% + \node[circle, minimum size=\tikzumlStateEndMinimumWidth, /tikzuml/stateend/style] (\tikzumlStateEndName) {};% + \else% + \node[circle, minimum size=\tikzumlStateEndMinimumWidth, /tikzuml/stateend/style] (\tikzumlStateEndName) at (\tikzumlStateEndPos) {};% + \fi% + \draw[draw=\tikzumlStateEndColor] (\tikzumlStateEndName.north east) -- (\tikzumlStateEndName.south west) (\tikzumlStateEndName.north west) -- (\tikzumlStateEndName.south east); + % + % add to fit + \ifnum\c@tikzumlStateLevel>0% + \edef\tikzumlStateFitOld{\csname tikzumlStateFit\tikzumlState@parent @@\tikzumlState@fitname\endcsname}% + \expandafter\xdef\csname tikzumlStateFit\tikzumlState@parent @@\tikzumlState@fitname\endcsname{\tikzumlStateFitOld (\tikzumlStateEndName)}% + \stepcounter{tikzumlStateSubStateNum}% + \fi% + \stepcounter{tikzumlStateEndNum}% +}% +% +\newcommand{\picturedhistory}[1]{% + \begin{tikzpicture}[#1]% + \draw[thick] (-0.1cm,-0.15cm) -- (-0.1cm,0.15cm) + (-0.1cm,0) -- (0.1cm,0) + (0.1cm,-0.15cm) -- (0.1cm,0.15cm);% + \end{tikzpicture}% +}% +% +% define a uml history state +% args : name of the state +% optional : x,y: coordinates of the state +% width: width of the state node +% name: name of the state node +% color: color of the join symbol +% style: to manage every default TikZ option +% no coords: to tell that the state position is defined relatively +% to another node (automatically used with TikZ options above, below, left, right, below left, ...) +\newcommand{\umlstatehistory}[1][]{% + \pgfkeys{/tikzuml/statehistory/.cd, x/.initial=\tikzumlDefaultX, y/.initial=\tikzumlDefaultX, width/.initial=\tikzumlStateHistoryDefaultWidth,% + name/.initial=statehistory-\thetikzumlStateHistoryNum,% + color/.initial=\tikzumlDefaultDrawColor, style/.style={},% + no coords/.is if=tikzumlstatehistoryWithoutCoords,% + no coords=false,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{above}\OR% + \equal{\keyname}{left}\OR% + \equal{\keyname}{below}\OR% + \equal{\keyname}{right}\OR% + \equal{\keyname}{above left}\OR% + \equal{\keyname}{above right}\OR% + \equal{\keyname}{below left}\OR% + \equal{\keyname}{below right}}{% + \IfSubStr{\keyvalue}{ of }{% + \pgfkeys{/tikzuml/statehistory/.cd, no coords}% + }{}% + }{}% + \ifx\keyvalue\pgfkeysnovalue% + \pgfkeys{/tikzuml/statehistory/.cd, style/.append style/.expand once={\keyname}}% + \else% + \pgfkeys{/tikzuml/statehistory/.cd, style/.append style/.expand twice={\expandafter\keyname\expandafter=\keyvalue}}% + \fi% + %\errmessage{TIKZUML ERROR : in umlstatehistory, invalid option \keyname}% + }% + }% + \pgfkeys{/tikzuml/statehistory/.cd, #1}% + \pgfkeys{/tikzuml/statehistory/.cd, x/.get=\tikzumlStateHistoryX, y/.get=\tikzumlStateHistoryY, width/.get=\tikzumlStateHistoryMinimumWidth,% + name/.get=\tikzumlStateHistoryName, color/.get=\tikzumlStateHistoryColor% + }% + % + \def\tikzumlStateHistoryPos{\tikzumlStateHistoryX,\tikzumlStateHistoryY}% + % + \iftikzumlstatehistoryWithoutCoords% + \node[circle, minimum size=\tikzumlStateHistoryMinimumWidth, draw=\tikzumlStateHistoryColor, /tikzuml/statehistory/style] (\tikzumlStateHistoryName) {\picturedhistory{draw=\tikzumlStateHistoryColor}};% + \else% + \node[circle, minimum size=\tikzumlStateHistoryMinimumWidth, draw=\tikzumlStateHistoryColor, /tikzuml/statehistory/style] (\tikzumlStateHistoryName) at (\tikzumlStateHistoryPos) {\picturedhistory{draw=\tikzumlStateHistoryColor}};% + \fi% + % + % add to fit + \ifnum\c@tikzumlStateLevel>0% + \edef\tikzumlStateFitOld{\csname tikzumlStateFit\tikzumlState@parent @@\tikzumlState@fitname\endcsname}% + \expandafter\xdef\csname tikzumlStateFit\tikzumlState@parent @@\tikzumlState@fitname\endcsname{\tikzumlStateFitOld (\tikzumlStateHistoryName)}% + \stepcounter{tikzumlStateSubStateNum}% + \fi% + \stepcounter{tikzumlStateHistoryNum}% +}% +% +\newcommand{\pictureddeephistory}[1]{% + \begin{tikzpicture}[#1]% + \draw[thick] (-0.1cm,-0.15cm) -- (-0.1cm,0.15cm) + (-0.1cm,0) -- (0.1cm,0) + (0.1cm,-0.15cm) -- (0.1cm,0.15cm) + (0.23cm,0.19cm) -- (0.23cm,0.11cm) + (0.20cm,0.17cm) -- (0.26cm,0.13cm) + (0.20cm,0.13cm) -- (0.26cm,0.17cm);% + \end{tikzpicture}% +}% +% +% define a uml deep-history state +% args : name of the state +% optional : x,y: coordinates of the state +% width: width of the state node +% name: name of the state node +% color: color of the join symbol +% style: to manage every default TikZ option +% no coords: to tell that the state position is defined relatively +% to another node (automatically used with TikZ options above, below, left, right, below left, ...) +\newcommand{\umlstatedeephistory}[1][]{% + \pgfkeys{/tikzuml/statedeephistory/.cd, x/.initial=\tikzumlDefaultX, y/.initial=\tikzumlDefaultX, width/.initial=\tikzumlStateDeepHistoryDefaultWidth,% + name/.initial=statedeephistory-\thetikzumlStateDeepHistoryNum,% + color/.initial=\tikzumlDefaultDrawColor, style/.style={},% + no coords/.is if=tikzumlstatedeephistoryWithoutCoords,% + no coords=false,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{above}\OR% + \equal{\keyname}{left}\OR% + \equal{\keyname}{below}\OR% + \equal{\keyname}{right}\OR% + \equal{\keyname}{above left}\OR% + \equal{\keyname}{above right}\OR% + \equal{\keyname}{below left}\OR% + \equal{\keyname}{below right}}{% + \IfSubStr{\keyvalue}{ of }{% + \pgfkeys{/tikzuml/statedeephistory/.cd, no coords}% + }{}% + }{}% + \ifx\keyvalue\pgfkeysnovalue% + \pgfkeys{/tikzuml/statedeephistory/.cd, style/.append style/.expand once={\keyname}}% + \else% + \pgfkeys{/tikzuml/statedeephistory/.cd, style/.append style/.expand twice={\expandafter\keyname\expandafter=\keyvalue}}% + \fi% + %\errmessage{TIKZUML ERROR : in umlstatedeephistory, invalid option \keyname}% + }% + }% + \pgfkeys{/tikzuml/statedeephistory/.cd, #1}% + \pgfkeys{/tikzuml/statedeephistory/.cd, x/.get=\tikzumlStateDeepHistoryX, y/.get=\tikzumlStateDeepHistoryY, width/.get=\tikzumlStateDeepHistoryMinimumWidth,% + name/.get=\tikzumlStateDeepHistoryName, color/.get=\tikzumlStateDeepHistoryColor% + }% + % + \def\tikzumlStateDeepHistoryPos{\tikzumlStateDeepHistoryX,\tikzumlStateDeepHistoryY}% + % + \iftikzumlstatedeephistoryWithoutCoords% + \node[circle, minimum size=\tikzumlStateDeepHistoryMinimumWidth, draw=\tikzumlStateDeepHistoryColor, /tikzuml/statedeephistory/style] (\tikzumlStateDeepHistoryName) {\pictureddeephistory{draw=\tikzumlStateDeepHistoryColor}};% + \else% + \node[circle, minimum size=\tikzumlStateDeepHistoryMinimumWidth, draw=\tikzumlStateDeepHistoryColor, /tikzuml/statedeephistory/style] (\tikzumlStateDeepHistoryName) at (\tikzumlStateDeepHistoryPos) {\pictureddeephistory{draw=\tikzumlStateDeepHistoryColor}};% + \fi% + % + % add to fit + \ifnum\c@tikzumlStateLevel>0% + \edef\tikzumlStateFitOld{\csname tikzumlStateFit\tikzumlState@parent @@\tikzumlState@fitname\endcsname}% + \expandafter\xdef\csname tikzumlStateFit\tikzumlState@parent @@\tikzumlState@fitname\endcsname{\tikzumlStateFitOld (\tikzumlStateDeepHistoryName)}% + \stepcounter{tikzumlStateSubStateNum}% + \fi% + \stepcounter{tikzumlStateDeepHistoryNum}% +}% +% +% define a uml state +% args : name of the state +% content of the state +% optional : x,y: coordinates of the state +% width: width of the state node +% name: name of the state node +% entry, do, exit: entry/do/exit action of the state +% draw, fill, text: colors +% style: to manage every default TikZ option +% no coords: to tell that the state position is defined relatively +% to another node (automatically used with TikZ options above, below, left, right, below left, ...) +\newenvironment{umlstate}[2][]{% + \ifnum\thetikzumlStateLevel>0% + \let\tikzumlState@nameold\tikzumlState@fitname% + \let\tikzumlState@parentold\tikzumlState@parent% + \edef\tikzumlState@parent{\tikzumlState@parentold @@\tikzumlState@nameold}% + \else% + \def\tikzumlState@parent{}% + \fi% + % + \stepcounter{tikzumlStateLevel}% + % + \pgfkeys{/tikzuml/state/.cd, x/.initial=\tikzumlDefaultX, y/.initial=\tikzumlDefaultX, width/.initial=\tikzumlStateDefaultWidth,% + name/.initial={},% + entry/.initial={}, do/.initial={}, exit/.initial={},% + draw/.initial=\tikzumlDefaultDrawColor, fill/.initial=\tikzumlStateDefaultFillColor,% + text/.initial=\tikzumlDefaultTextColor, style/.style={},% + no coords/.is if=tikzumlstateWithoutCoords,% + no coords=false,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{above}\OR% + \equal{\keyname}{left}\OR% + \equal{\keyname}{below}\OR% + \equal{\keyname}{right}\OR% + \equal{\keyname}{above left}\OR% + \equal{\keyname}{above right}\OR% + \equal{\keyname}{below left}\OR% + \equal{\keyname}{below right}}{% + \IfSubStr{\keyvalue}{ of }{% + \pgfkeys{/tikzuml/state/.cd, no coords}% + }{}% + }{}% + \ifx\keyvalue\pgfkeysnovalue% + \pgfkeys{/tikzuml/state/.cd, style/.append style/.expand once={\keyname}}% + \else% + \pgfkeys{/tikzuml/state/.cd, style/.append style/.expand twice={\expandafter\keyname\expandafter=\keyvalue}}% + \fi% + % \errmessage{TIKZUML ERROR : in umlstate, invalid option \keyname}% + }% + }% + \pgfkeys{/tikzuml/state/.cd, #1}% + \pgfkeys{/tikzuml/state/.cd, x/.get=\tikzumlStateXShift, y/.get=\tikzumlStateYShift, width/.get=\tikzumlStateMinimumWidth, name/.get=\tikzumlStateName,% + entry/.get=\tikzumlStateEntry, do/.get=\tikzumlStateDo, exit/.get=\tikzumlStateExit,% + draw/.get=\tikzumlStateDrawColor, fill/.get=\tikzumlStateFillColor,% + text/.get=\tikzumlStateTextColor% + }% + % + \ifthenelse{\equal{\tikzumlStateName}{}}{% + \edef\tikzumlState@name{#2}% + }{% + \edef\tikzumlState@name{\tikzumlStateName}% + }% + % + \begingroup% + \def\_{@}\edef\x{\endgroup% + \def\noexpand\tikzumlState@fitname{\tikzumlState@name}}\x% + % + \let\tikzumlState@nodeNameold\tikzumlState@nodeName% + \def\tikzumlState@caption{#2}% + % + \begingroup% + \def\_{_}\edef\x{\endgroup% + \def\noexpand\tikzumlState@nodeName{\tikzumlState@name}}\x% + % + \expandafter\gdef\csname tikzumlStateFit\tikzumlState@parent @@\tikzumlState@fitname\endcsname{}% + % + \setcounter{tikzumlStateSubStateNum}{0}% + \setcounter{tikzumlStateText}{0}% + % + \def\tikzumlStateText{tikzumlEmpty}% + \begin{scope}[xshift=\tikzumlStateXShift cm, yshift=\tikzumlStateYShift cm]% +}{% + % + \def\tikzumlstaterootlabel{\phantom{\tikzumlState@nodeName}}% + % + \def\tikzumlstaterootinnerysep{0.5ex}% + \def\tikzumlstatebodyinnerysep{2ex}% + % + \ifthenelse{\equal{\tikzumlStateEntry}{}}{}{% + \ifnum\c@tikzumlStateText=0% + \def\tikzumlStateText{entry/\tikzumlStateEntry}% + \else% + \let\tikzumlStateTextOld\tikzumlStateText% + \ifthenelse{\equal{\tikzumlStateText}{tikzumlEmpty}}{% + \def\tikzumlStateText{entry/\tikzumlStateEntry}% + }{% + \expandafter\def\expandafter\tikzumlStateText\expandafter{\tikzumlStateTextOld \\ entry/\tikzumlStateEntry}% + }% + \fi% + \setcounter{tikzumlStateText}{1}% + \ifnum\c@tikzumlStateSubStateNum=0% + \def\tikzumlstatebodyinnerysep{0}% + \def\tikzumlstaterootinnerysep{0}% + \fi% + }% + \ifthenelse{\equal{\tikzumlStateDo}{}}{}{% + \ifnum\c@tikzumlStateText=0% + \def\tikzumlStateText{do/\tikzumlStateDo}% + \else% + \let\tikzumlStateTextOld\tikzumlStateText% + \ifthenelse{\equal{\tikzumlStateText}{tikzumlEmpty}}{% + \def\tikzumlStateText{do/\tikzumlStateDo}% + }{% + \expandafter\def\expandafter\tikzumlStateText\expandafter{\tikzumlStateTextOld \\ do/\tikzumlStateDo}% + }% + \fi% + \setcounter{tikzumlStateText}{1}% + \ifnum\c@tikzumlStateSubStateNum=0% + \def\tikzumlstatebodyinnerysep{0}% + \def\tikzumlstaterootinnerysep{0}% + \fi% + }% + \ifthenelse{\equal{\tikzumlStateExit}{}}{}{% + \ifnum\c@tikzumlStateText=0% + \def\tikzumlStateText{exit/\tikzumlStateExit}% + \else% + \let\tikzumlStateTextOld\tikzumlStateText% + \ifthenelse{\equal{\tikzumlStateText}{tikzumlEmpty}}{% + \def\tikzumlStateText{exit/\tikzumlStateExit}% + }{% + \expandafter\def\expandafter\tikzumlStateText\expandafter{\tikzumlStateTextOld \\ exit/\tikzumlStateExit}% + }% + \fi% + \setcounter{tikzumlStateText}{1}% + \ifnum\c@tikzumlStateSubStateNum=0% + \def\tikzumlstatebodyinnerysep{0}% + \def\tikzumlstaterootinnerysep{0}% + \fi% + }% + % + \addtocounter{tikzumlStateLevel}{-1}% + \begin{pgfonlayer}{state\thetikzumlStateLevel}% + % + % if contains nothing, one define a fictive node to enable the fit option + \ifnum\c@tikzumlStateSubStateNum=0% + \iftikzumlstateWithoutCoords% + \node[inner ysep=\tikzumlstaterootinnerysep, minimum width=\tikzumlStateMinimumWidth, /tikzuml/state/style] (\tikzumlState@nodeName-root) {\tikzumlstaterootlabel};% + \else% + \node[inner ysep=\tikzumlstaterootinnerysep, minimum width=\tikzumlStateMinimumWidth, /tikzuml/state/style] (\tikzumlState@nodeName-root) at (0,0) {\tikzumlstaterootlabel};% + \fi% + \expandafter\xdef\csname tikzumlStateFit\tikzumlState@parent @@\tikzumlState@fitname\endcsname{(\tikzumlState@nodeName-root)}% + \fi% + % + \ifnum\c@tikzumlStateLevel>0% + \def\tikzumlStateFitTmp{\csname tikzumlStateFit\tikzumlState@parent\endcsname}% + \expandafter\xdef\csname tikzumlStateFit\tikzumlState@parent\endcsname{\tikzumlStateFitTmp (\tikzumlState@nodeName-body) (\tikzumlState@nodeName-caption)}% + \stepcounter{tikzumlStateSubStateNum}% + \fi% + % + \node[inner xsep=2ex, inner ysep=\tikzumlstatebodyinnerysep, fit = \csname tikzumlStateFit\tikzumlState@parent @@\tikzumlState@fitname\endcsname, /tikzuml/state/style ] (\tikzumlState@nodeName-body) {};% + \def\tikzumlState@orig{body}% + \ifnum\c@tikzumlStateText=1% + \node[above=0] (\tikzumlState@nodeName-texttmp) at (\tikzumlState@nodeName-\tikzumlState@orig.north) {\begin{tabular}{l}\tikzumlStateText \end{tabular}};% + \def\tikzumlState@orig{texttmp}% + \fi% + \node[above] (\tikzumlState@nodeName-captiontmp) at (\tikzumlState@nodeName-\tikzumlState@orig.north) {\tikzumlState@caption};% + \ifnum\c@tikzumlStateText=1% + \node[rounded corners, draw=\tikzumlStateDrawColor, fill=\tikzumlStateFillColor, name=\tikzumlState@nodeName, fit=(\tikzumlState@nodeName-body) (\tikzumlState@nodeName-texttmp) (\tikzumlState@nodeName-captiontmp)] {};% + \else% + \node[rounded corners, draw=\tikzumlStateDrawColor, fill=\tikzumlStateFillColor, name=\tikzumlState@nodeName, fit=(\tikzumlState@nodeName-body) (\tikzumlState@nodeName-captiontmp)] {};% + \fi% + \ifnum\c@tikzumlStateText=1% + \node (\tikzumlState@nodeName-text) at (\tikzumlState@nodeName-texttmp) {\begin{tabular}{l}\tikzumlStateText \end{tabular}};% + \fi% + \node (\tikzumlState@nodeName-caption) at (\tikzumlState@nodeName-captiontmp) {\tikzumlState@caption};% + \draw (\tikzumlState@nodeName-caption.south -| \tikzumlState@nodeName.north west) -- (\tikzumlState@nodeName-caption.south -| \tikzumlState@nodeName.north east);% + \end{pgfonlayer}% + \end{scope}% +}% +% +% shortcut for empty state +\newcommand{\umlbasicstate}[2][]{\begin{umlstate}[#1]{#2}\end{umlstate}}% +% +% command to add text in a state, to be used inside umlstate environment +\newcommand{\umlstatetext}[1]{% + \def\tikzumlStateText{#1}% + \setcounter{tikzumlStateText}{1}% +}% +% +% shortcuts for state transitions macros +\newcommand{\umltrans}[3][]{% + \ifthenelse{\equal{#2}{#3}}{% + \umlrelation[style={tikzuml transition style}, recursive mode=transition, #1]{#2}{#3}% + }{% + \umlrelation[style={tikzuml transition style}, #1]{#2}{#3}% + }% +}% +\newcommand{\umlHVtrans}[3][]{\umlHVrelation[style={tikzuml transition style}, #1]{#2}{#3}}% +\newcommand{\umlVHtrans}[3][]{\umlVHrelation[style={tikzuml transition style}, #1]{#2}{#3}}% +\newcommand{\umlVHVtrans}[3][]{\umlVHVrelation[style={tikzuml transition style}, #1]{#2}{#3}}% +\newcommand{\umlHVHtrans}[3][]{\umlHVHrelation[style={tikzuml transition style}, #1]{#2}{#3}}% +\newcommand{\umlCNtrans}[4][]{\umlCNrelation[style={tikzuml transition style}, #1]{#2}{#3}{#4}}% +% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% sequence diagrams % +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +\tikzstyle{tikzuml synchron-msg style}=[color=\tikzumlDefaultDrawColor, -triangle 45]% +\tikzstyle{tikzuml asynchron-msg style}=[color=\tikzumlDefaultDrawColor, -angle 45]% +\tikzstyle{tikzuml return-msg style}=[color=\tikzumlDefaultDrawColor, dashed, -angle 45]% +\tikzstyle{tikzuml call return style}=[color=\tikzumlDefaultDrawColor, dashed, -angle 45]% +\tikzstyle{tikzuml activity style}=[inner xsep=1ex, inner ysep=1ex]% +% +\newcounter{tikzumlObjectNum}% +\newcounter{tikzumlCallLevel}% +\newcounter{tikzumlCallNum}% +\newcounter{tikzumlFragmentLevel}% +\newcounter{tikzumlFragmentLevelNum}% +\newcounter{tikzumlFragmentNum}% +\newcounter{tikzumlFragmentPartNum}% +\newcounter{tikzumlCallStartFragmentNum}% +\newcounter{tikzumlCallEndFragmentNum}% +% +\newif\iftikzumlobjectNoDDots% +\newif\iftikzumlcreatecallNoDDots% +% +% define a sequence diagram +% +\newenvironment{umlseqdiag}{% + \gdef\tikzumlInCreateCall{0}% + \setcounter{tikzumlObjectNum}{0}% + \setcounter{tikzumlCallLevel}{0}% + \setcounter{tikzumlCallNum}{0}% + \setcounter{tikzumlFragmentLevel}{0}% + \setcounter{tikzumlFragmentLevelNum}{0}% + \setcounter{tikzumlFragmentNum}{0}% + \setcounter{tikzumlFragmentPartNum}{0}% + \setcounter{tikzumlCallStartFragmentNum}{0}% + \setcounter{tikzumlCallEndFragmentNum}{0}% + % + \ifx \@umlactor \@empty + \newcommand{\umlactor}[2][]{% + \pgfkeys{/tikzuml/actorobj/.cd, .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{stereo}}{% + \errmessage{TIKZUML ERROR : in umlactor, forbidden option stereo}% + }{}% + }% + }% + % + \pgfkeys{/tikzuml/actorobj/.cd, ##1}% + \umlobject[stereo=actor, ##1]{##2}% + }% + \else% + \renewcommand{\umlactor}[2][]{% + \pgfkeys{/tikzuml/actorobj/.cd, .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{stereo}}{% + \errmessage{TIKZUML ERROR : in umlactor, forbidden option stereo}% + }{}% + }% + }% + % + \pgfkeys{/tikzuml/actorobj/.cd, ##1}% + \umlobject[stereo=actor, ##1]{##2}% + }% + \fi% + \begin{scope}[font=\tikzumlDefaultFont]% +}{% + % draw lifelines of each object + \begin{pgfonlayer}{lifelines}% + \foreach \id in \tikzumlIdList {% + \draw (\csname tikzumlLastChild@\id \endcsname)+(0,-2.5ex) node[inner sep=0, name=end-\id] {};% + \draw[dotted] (\id) -- (end-\id);% + }% + \end{pgfonlayer}% + \end{scope}% +}% +% +% define the database symbol +% optional : global tikzpicture styles +\newcommand{\pictureddatabase}[1]{% + \pgfkeys{/tikzuml/database/.cd, scale/.initial=1, .unknown/.code={}}% + \pgfkeys{/tikzuml/database/.cd,#1}% + \pgfkeys{/tikzuml/database/.cd, scale/.get=\tikzumlDatabaseScale}% + % + \begin{tikzpicture}[#1]% + \node[fill, draw, ellipse, minimum width=\tikzumlDatabaseScale*4ex, minimum height=\tikzumlDatabaseScale*2ex, inner sep=0] (bottom) at (0,-2ex) {};% + \node[fill, draw, ellipse, minimum width=\tikzumlDatabaseScale*4ex, minimum height=\tikzumlDatabaseScale*2ex, inner sep=0] (top) at (0,4ex) {};% + \fill (bottom.west) rectangle (top.east);% + \begin{scope}% + \clip (-3.5ex,-0.5ex) rectangle (3.5ex,2.5ex);% + \node[draw, dashed, ellipse, minimum width=\tikzumlDatabaseScale*4ex, minimum height=\tikzumlDatabaseScale*2ex, inner sep=0] (bottom2) at (0,-2ex) {};% + \end{scope}% + \node[draw, ellipse, minimum width=\tikzumlDatabaseScale*4ex, minimum height=\tikzumlDatabaseScale*2ex, inner sep=0] (top2) at (0,4ex) {};% + \draw (bottom.west) -- (top.west) (bottom.east) -- (top.east);% + \end{tikzpicture}% +}% +% +% define the entity symbol +% optional : global tikzpicture styles +\newcommand{\picturedentity}[1]{% + \pgfkeys{/tikzuml/entity/.cd, scale/.initial=1, .unknown/.code={}}% + \pgfkeys{/tikzuml/entity/.cd,#1}% + \pgfkeys{/tikzuml/entity/.cd, scale/.get=\tikzumlEntityScale}% + % + \begin{tikzpicture}[#1]% + \node[fill, draw, circle, inner sep=0, minimum size=\tikzumlEntityScale*5ex] (center) at (0,0) {};% + \draw (center.south) node[coordinate, name=bottom] {};% + \draw (bottom)+(-2ex,0) node[coordinate, name=bottom-left] {};% + \draw (bottom)+(2ex,0) node[coordinate, name=bottom-right] {};% + \draw (center) -- (bottom);% + \draw (bottom-left) -- (bottom-right);% + \end{tikzpicture}% +}% +% +% define the boundary symbol +% optional : global tikzpicture styles +\newcommand{\picturedboundary}[1]{% + \pgfkeys{/tikzuml/boundary/.cd, scale/.initial=1, .unknown/.code={}}% + \pgfkeys{/tikzuml/boundary/.cd,#1}% + \pgfkeys{/tikzuml/boundary/.cd, scale/.get=\tikzumlBoundaryScale}% + % + \begin{tikzpicture}[#1] + \node[fill, draw, circle, inner sep=0, minimum size=\tikzumlBoundaryScale*5ex] (center) at (0,0) {}; + \draw (center.west)+(-0.8ex,0) node[coordinate, name=left] {}; + \draw (left)+(0,0.2ex) node[coordinate, name=left-top] {}; + \draw (left)+(0,-0.2ex) node[coordinate, name=left-bottom] {}; + \draw (center) -- (left); + \draw (left-top) -- (left-bottom); + \end{tikzpicture} +}% +% +% define the control symbol +% optional : global tikzpicture styles +\newcommand{\picturedcontrol}[1]{% + \pgfkeys{/tikzuml/control/.cd, scale/.initial=1, .unknown/.code={}}% + \pgfkeys{/tikzuml/control/.cd,#1}% + \pgfkeys{/tikzuml/control/.cd, scale/.get=\tikzumlControlScale}% + % + \begin{tikzpicture}[#1, decoration={markings, mark=at position 0.25 with {\arrow{>}}}] + \node[fill, draw, circle, inner sep=0, minimum size=\tikzumlControlScale*5ex, postaction={decorate}] (center) at (0,0) {}; + \end{tikzpicture} +}% +% +% define a uml object for a sequence diagram +% args : name of the object +% optional : x, y: coordinates of the object +% stereo: stereotype of the object (object, actor, database, boundary, control, entity, multiobject) +% class: class of the object +% scale: scale factor of the object symbol +% draw, fill, text; colors +% no ddots: when used, disable printing of double dots +\newcommand{\umlobject}[2][]{ + \stepcounter{tikzumlObjectNum}% + % + \edef\tikzumlobject@ddot{:}% + \pgfkeys{/tikzuml/obj/.cd, x/.initial=tikzumlEmpty, y/.initial=\tikzumlDefaultX, stereo/.initial=\tikzumlObjectDefaultStereo,% + class/.initial={}, scale/.initial=1,% + draw/.initial=\tikzumlDefaultDrawColor, fill/.initial=\tikzumlObjectDefaultFillColor,% + text/.initial=\tikzumlDefaultTextColor,% + no ddots/.is if=tikzumlobjectNoDDots,% + no ddots=false,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \errmessage{TIKZUML ERROR : in umlobject, invalid option \keyname}% + }% + }% + \pgfkeys{/tikzuml/obj/.cd, #1}% + \pgfkeys{/tikzuml/obj/.cd, x/.get=\tikzumlObjectX, y/.get=\tikzumlObjectY,% + stereo/.get=\tikzumlObjectStereo, class/.get=\tikzumlObjectClass,% + scale/.get=\tikzumlObjectScale,% + draw/.get=\tikzumlObjectDrawColor, fill/.get=\tikzumlObjectFillColor,% + text/.get=\tikzumlObjectTextColor% + }% + % + \iftikzumlobjectNoDDots% + \edef\tikzumlobject@ddot{}% + \fi% + % + \ifthenelse{\equal{\tikzumlObjectX}{tikzumlEmpty}}{% + \pgfmathsetmacro{\tikzumlObjectX}{4*(\thetikzumlObjectNum-1)}% + }{}% + % + \def\tikzumlObjectName{#2}% + \expandafter\xdef\csname tikzumlLastChild@\tikzumlObjectName \endcsname{\tikzumlObjectName}% + % + \ifnum\thetikzumlObjectNum=1% + \xdef\tikzumlIdList{\tikzumlObjectName}% + \else% + \let\tikzumlIdListOld\tikzumlIdList% + \expandafter\xdef\expandafter\tikzumlIdList\expandafter{\tikzumlIdListOld,\tikzumlObjectName}% + \fi% + % + \tikzstyle{tikzuml object box style}=[rectangle, text=\tikzumlObjectTextColor, font=\tikzumlDefaultFont]% + % + \ifthenelse{\equal{\tikzumlObjectStereo}{object}}{% + \tikzstyle{tikzuml object box style}+=[draw=\tikzumlObjectDrawColor, fill=\tikzumlObjectFillColor]% + }{% + \ifthenelse{\equal{\tikzumlObjectStereo}{multi}}{% + \tikzstyle{tikzuml object box style}+=[fill=\tikzumlObjectFillColor]% + }{}% + }% + % + \ifnum\tikzumlInCreateCall=1% + \draw (\tikzumlCreateCallObjectSrc -| \tikzumlObjectX,0) node[tikzuml object box style] (\tikzumlObjectName) {\tikzumlObjectName\tikzumlobject@ddot\tikzumlObjectClass};% + \else% + \node[tikzuml object box style] (\tikzumlObjectName) at (\tikzumlObjectX,\tikzumlObjectY) {\tikzumlObjectName\tikzumlobject@ddot\tikzumlObjectClass};% + \fi% + % + \ifthenelse{\equal{\tikzumlObjectStereo}{multi}}{% + \draw (\tikzumlObjectName.north east)+(0.4ex,0.4ex) node[name=\tikzumlObjectName-tr, coordinate] {}; + \draw (\tikzumlObjectName.north west)+(0.4ex,0.4ex) node[name=\tikzumlObjectName-tl, coordinate] {}; + \draw (\tikzumlObjectName.south east)+(0.4ex,0.4ex) node[name=\tikzumlObjectName-br, coordinate] {}; + \draw (\tikzumlObjectName-tr)+(0.4ex,0.4ex) node[name=\tikzumlObjectName-ttr, coordinate] {}; + \draw (\tikzumlObjectName-tl)+(0.4ex,0.4ex) node[name=\tikzumlObjectName-ttl, coordinate] {}; + \draw (\tikzumlObjectName-br)+(0.4ex,0.4ex) node[name=\tikzumlObjectName-tbr, coordinate] {}; + \fill[fill=\tikzumlObjectFillColor] (\tikzumlObjectName-ttl |- \tikzumlObjectName.north) -- (\tikzumlObjectName-ttl) -- (\tikzumlObjectName-ttr) -- (\tikzumlObjectName-tbr) -- (\tikzumlObjectName-tbr -| \tikzumlObjectName.east) -- (\tikzumlObjectName.north east) -- (\tikzumlObjectName-ttl |- \tikzumlObjectName.north); + \draw[draw=\tikzumlObjectDrawColor] (\tikzumlObjectName-ttl |- \tikzumlObjectName.north) -- (\tikzumlObjectName-ttl) -- (\tikzumlObjectName-ttr) -- (\tikzumlObjectName-tbr) -- (\tikzumlObjectName-tbr -| \tikzumlObjectName.east); + \fill[fill=\tikzumlObjectFillColor] (\tikzumlObjectName-tl |- \tikzumlObjectName.north) -- (\tikzumlObjectName-tl) -- (\tikzumlObjectName-tr) -- (\tikzumlObjectName-br) -- (\tikzumlObjectName-br -| \tikzumlObjectName.east) -- (\tikzumlObjectName.north east) -- (\tikzumlObjectName-tl |- \tikzumlObjectName.north); + \draw[draw=\tikzumlObjectDrawColor] (\tikzumlObjectName-tl |- \tikzumlObjectName.north) -- (\tikzumlObjectName-tl) -- (\tikzumlObjectName-tr) -- (\tikzumlObjectName-br) -- (\tikzumlObjectName-br -| \tikzumlObjectName.east); + \draw[draw=\tikzumlObjectDrawColor] (\tikzumlObjectName.north west) rectangle (\tikzumlObjectName.south east); + }{% + \ifthenelse{\equal{\tikzumlObjectStereo}{object}}{}{% + \node[above=1ex, name=\tikzumlObjectName-picture] at (\tikzumlObjectName) {\csname pictured\tikzumlObjectStereo \endcsname{draw=\tikzumlObjectDrawColor, fill=\tikzumlObjectFillColor, scale=\tikzumlObjectScale}}; + }% + }% +}% +% +% shortcuts for objects +\newcommand{\umlbasicobject}[2][]{% + \pgfkeys{/tikzuml/basicobj/.cd, .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{no ddots}}{% + \errmessage{TIKZUML ERROR : in umlbasicobject, forbidden option no ddots}% + }{}% + }% + }% + \pgfkeys{/tikzuml/basicobj/.cd, #1}% + \umlobject[no ddots, #1]{#2}% +}% +% +\newcommand{\umldatabase}[2][]{% + \pgfkeys{/tikzuml/databaseobj/.cd, .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{stereo}}{% + \errmessage{TIKZUML ERROR : in umldatabase, forbidden option stereo}% + }{}% + }% + }% + \pgfkeys{/tikzuml/databaseobj/.cd, #1}% + \umlobject[stereo=database, #1]{#2}% +}% +\newcommand{\umlentity}[2][]{% + \pgfkeys{/tikzuml/entityobj/.cd, .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{stereo}}{% + \errmessage{TIKZUML ERROR : in umlentity, forbidden option stereo}% + }{}% + }% + }% + \pgfkeys{/tikzuml/entityobj/.cd, #1}% + \umlobject[stereo=entity, #1]{#2}% +}% +\newcommand{\umlcontrol}[2][]{% + \pgfkeys{/tikzuml/controlobj/.cd, .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{stereo}}{% + \errmessage{TIKZUML ERROR : in umlcontrol, forbidden option stereo}% + }{}% + }% + }% + \pgfkeys{/tikzuml/controlobj/.cd, #1}% + \umlobject[stereo=control, #1]{#2}% +}% +\newcommand{\umlboundary}[2][]{% + \pgfkeys{/tikzuml/boundaryobj/.cd, .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{stereo}}{% + \errmessage{TIKZUML ERROR : in umlboundary, forbidden option stereo}% + }{}% + }% + }% + \pgfkeys{/tikzuml/boundaryobj/.cd, #1}% + \umlobject[stereo=boundary, #1]{#2}% +}% +\newcommand{\umlmulti}[2][]{% + \pgfkeys{/tikzuml/multiobj/.cd, .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{stereo}}{% + \errmessage{TIKZUML ERROR : in umlmulti, forbidden option stereo}% + }{}% + }% + }% + \pgfkeys{/tikzuml/multiobj/.cd, #1}% + \umlobject[stereo=multi, #1]{#2}% +}% +% +\newcounter{tikzumlSDNodeNum}% +% +% define a hidden node to lengthen lifeline of a object +% args : object node +% optional : dt: distance between the sdnode and the last call defined on the lifeline of the object +% name: name of the sdnode +\newcommand{\umlsdnode}[2][]{% + \pgfkeys{/tikzuml/sdnode/.cd, dt/.initial=0, name/.initial=tikzumlEmpty}% + \pgfkeys{/tikzuml/sdnode/.cd, #1}% + \pgfkeys{/tikzuml/sdnode/.cd, dt/.get=\tikzumlSDNodeDT, name/.get=\tikzumlSDNodeName}% + % + \ifthenelse{\equal{\tikzumlSDNodeName}{tikzumlEmpty}}{% + \expandafter\def\expandafter\tikzumlSDNode@nodeName{sdnode-\thetikzumlSDNodeNum}% + }{% + \expandafter\def\expandafter\tikzumlSDNode@nodeName{\tikzumlSDNodeName}% + }% + % + \stepcounter{tikzumlSDNodeNum}% + % + \begingroup% + \def\_{_}\edef\x{\endgroup% + \def\noexpand\tikzumlSDNode@objnodeName{#2}}\x% + % + \draw (\expandafter\csname tikzumlLastChild@\tikzumlSDNode@objnodeName \endcsname)+(0,-\tikzumlSDNodeDT ex) node[name=\tikzumlSDNode@nodeName,coordinate] {};% + % + % update last node drawn on sender lifeline + \expandafter\xdef\csname tikzumlLastChild@\tikzumlSDNode@objnodeName \endcsname{\tikzumlSDNode@nodeName}% +}% +% +\newlength{\tikzumlCall@xa}% +\newlength{\tikzumlCall@xb}% +% +% define a uml operation call for sequence diagrams +% args : call sender +% call receiver +% optional : dt: time delay from precedent event end +% name: name of the call +% op: operation name and input args +% return: return value +% type: type of the call (synchron, asynchron) +% draw, fill, text: colors +% padding: time padding from call start and to call end +\newenvironment{umlcall}[3][]{% + \stepcounter{tikzumlCallNum}% + \def\tikzumlCallWithReturn{tikzumlFalse}% + \edef\tikzumlCall@lastchildNum{\thetikzumlCallNum}% for testing presence of sub-calls + \gdef\tikzumlCallBottom{0}% + % + \pgfkeys{/tikzuml/call/.cd, dt/.initial=\tikzumlCallDefaultDT, name/.initial={call-\thetikzumlCallNum},% + op/.initial={}, return/.initial={}, type/.initial=\tikzumlCallDefaultType,% + draw/.initial=\tikzumlDefaultDrawColor, fill/.initial=\tikzumlCallDefaultFillColor,% + text/.initial=\tikzumlDefaultTextColor,% + padding/.initial=\tikzumlCallDefaultPadding,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{with return}}{% + \def\tikzumlCallWithReturn{tikzumlTrue}% + }{% + \errmessage{TIKZUML ERROR : in umlcall, invalid option \keyname}% + }% + }% + }% + \pgfkeys{/tikzuml/call/.cd, #1}% + \pgfkeys{/tikzuml/call/.cd, dt/.get=\tikzumlCallDT, name/.get=\tikzumlCallName, op/.get=\tikzumlCallOp,% + return/.get=\tikzumlCallReturn, type/.get=\tikzumlCallType,% + padding/.get=\tikzumlCallPadding,% + draw/.get=\tikzumlCallDrawColor, fill/.get=\tikzumlCallFillColor,% + text/.get=\tikzumlCallTextColor% + }% + % + \edef\tikzumlfillcall{\tikzumlCallFillColor}% + \edef\tikzumldrawcall{\tikzumlCallDrawColor}% + \edef\tikzumltextcall{\tikzumlCallTextColor}% + \edef\tikzumltypecall{\tikzumlCallType}% + % + \ifthenelse{\equal{\tikzumlCallDT}{tikzumlEmpty}}{% + \ifnum\thetikzumlCallNum=1% + \def\tikzumlCallDT{2}% + \def\tikzumlcallSrc{2}% + \else% + \def\tikzumlCallDT{2}% + \def\tikzumlcallSrc{1}% + \fi% + }{% + \def\tikzumlcallSrc{0}% + }% + % + \let\tikzumlCallStartNodeNameold\tikzumlCallStartNodeName% + \def\tikzumlCallStartNodeName{#2}% + \let\tikzumlCallEndNodeNameold\tikzumlCallEndNodeName% + \def\tikzumlCallEndNodeName{#3}% + \def\tikzumlcallheight{\tikzumlCallPadding}% + % + % managing time delays from previous/parent fragments + \ifnum\thetikzumlCallStartFragmentNum>0% + \let\tikzumlCallDTold\tikzumlCallDT% + \pgfmathparse{0.5*\tikzumlFragment@paddingy+\tikzumlCallDTold}% + \edef\tikzumlCallDT{\pgfmathresult}% + \addtocounter{tikzumlCallStartFragmentNum}{-1} + \fi% + \ifnum\thetikzumlCallEndFragmentNum>0% + \let\tikzumlCallDTold\tikzumlCallDT% + \pgfmathparse{0.5*\tikzumlFragment@paddingy+\tikzumlCallDTold}% + \edef\tikzumlCallDT{\pgfmathresult}% + \addtocounter{tikzumlCallEndFragmentNum}{-1} + \fi% + \ifnum\thetikzumlFragmentPartNum>0% + \let\tikzumlCallDTold\tikzumlCallDT% + \pgfmathparse{0.5*\tikzumlFragment@paddingy+\tikzumlCallDTold}% + \edef\tikzumlCallDT{\pgfmathresult}% + \fi% + % + % managing parent-child structure + \ifnum\thetikzumlCallLevel>0% + \let\tikzumlCall@nameold\tikzumlCall@name% + \edef\tikzumlCall@name{\tikzumlCallName}% + \let\tikzumlCall@parentold\tikzumlCall@parent% + \edef\tikzumlCall@parent{\tikzumlCall@parentold @@\tikzumlCall@nameold}% + \else% + \edef\tikzumlCall@parent{}% + \edef\tikzumlCall@parentold{}% + \edef\tikzumlCall@nameold{} + \edef\tikzumlCall@name{\tikzumlCallName}% + \fi% + % + \begingroup% + \def\_{_}\edef\x{\endgroup% + \def\noexpand\tikzumlCall@nodeName{\tikzumlCall@name}}\x% + % + \let\tikzumlCall@nodeNameold\tikzumlCall@nodeName% + % + \def\tikzumlcallstyle{tikzuml \tikzumlCallType-msg style}% + % + % top node of activity period of call sender + \begin{pgfonlayer}{connections}% + \pgfmathparse{\tikzumlCallDT+\tikzumlcallSrc}% + \draw (\csname tikzumlLastChild@\tikzumlCallStartNodeName \endcsname)+(0,-\pgfmathresult ex) node[coordinate, name=tikzumlTmpNode] {};% + \node[tikzuml activity style] (st-\tikzumlCall@nodeName) at (\tikzumlCallStartNodeName |- tikzumlTmpNode) {};% + % + % update last node drawn on sender lifeline + \expandafter\xdef\csname tikzumlLastChild@\tikzumlCallStartNodeName \endcsname{st-\tikzumlCall@nodeName}% + % + % top node of activity period of call receiver + \ifstrequal{\tikzumlCallStartNodeName}{\tikzumlCallEndNodeName}{% + \draw (st-\tikzumlCall@nodeName)+(0,-0.75*\tikzumlCallPadding ex) node[coordinate, name=tikzumlTmpNode] {};% + \node[tikzuml activity style] (et-\tikzumlCall@nodeName) at (\tikzumlCallStartNodeName |- tikzumlTmpNode) {};% + }{% + \node[tikzuml activity style] (et-\tikzumlCall@nodeName) at (\tikzumlCallEndNodeName |- st-\tikzumlCall@nodeName) {};% + }% + % + % update last node drawn on receiver lifeline + \expandafter\xdef\csname tikzumlLastChild@\tikzumlCallEndNodeName \endcsname{et-\tikzumlCall@nodeName}% + \xdef\tikzumlCallBottomSrc{et-\tikzumlCall@nodeName}% + \end{pgfonlayer}% + % + \stepcounter{tikzumlCallLevel}% +}{% + \addtocounter{tikzumlCallLevel}{-1}% + % + % bottom nodes of activity periods of call sender and receiver + \begin{pgfonlayer}{connections}% + \ifnum\tikzumlCall@lastchildNum=\thetikzumlCallNum% + % + % this test occurs a bug with latex package preview + \ifstrequal{\tikzumlCallStartNodeName}{\tikzumlCallEndNodeName}{% + \draw (\tikzumlCallBottomSrc)+(0,-\tikzumlCallPadding ex) node[coordinate, name=tikzumlTmpNode] {};% + \node[tikzuml activity style] (eb-\tikzumlCall@nodeName) at (\tikzumlCallEndNodeName |- tikzumlTmpNode) {};% + \draw (eb-\tikzumlCall@nodeName)+(0,-0.75*\tikzumlCallPadding ex) node[coordinate, name=tikzumlTmpNode] {};% + \node[tikzuml activity style] (sb-\tikzumlCall@nodeName) at (\tikzumlCallStartNodeName |- tikzumlTmpNode) {};% + }{% + \ifthenelse{\equal{\tikzumlCallReturn}{tikzumlEmpty}}{% + \pgfmathsetmacro{\tikzumlCallPaddingd}{0.5*\tikzumlCallPadding}% + }{% + \pgfmathsetmacro{\tikzumlCallPaddingd}{1.2*\tikzumlCallPadding}% + }% + \draw (\tikzumlCallBottomSrc)+(0,-\tikzumlCallPaddingd ex) node[coordinate, name=tikzumlTmpNode] {};% + \node[tikzuml activity style] (eb-\tikzumlCall@nodeName) at (\tikzumlCallEndNodeName |- tikzumlTmpNode) {};% + \node[tikzuml activity style] (sb-\tikzumlCall@nodeName) at (\tikzumlCallStartNodeName |- eb-\tikzumlCall@nodeName) {};% + }% + \xdef\tikzumlCallBottomSrc{sb-\tikzumlCall@nodeName}% + \else% + % + % managing time delays from previous/parent fragments + \ifnum\thetikzumlCallStartFragmentNum>0% + \let\tikzumlcallheightold\tikzumlCallPadding% + \pgfmathparse{\tikzumlcallheightold+0.5*\tikzumlFragment@paddingy}% + \edef\tikzumlcallheight{\pgfmathresult}% + \addtocounter{tikzumlCallStartFragmentNum}{-1}% + \fi% + \ifnum\thetikzumlCallEndFragmentNum>0% + \let\tikzumlcallheightold\tikzumlCallPadding% + \pgfmathparse{\tikzumlcallheightold+0.5*\tikzumlFragment@paddingy}% + \edef\tikzumlcallheight{\pgfmathresult}% + \addtocounter{tikzumlCallEndFragmentNum}{-1}% + \fi% + % + \ifstrequal{\tikzumlCallStartNodeName}{\tikzumlCallEndNodeName}{% + \draw (\tikzumlCallBottomSrc)+(0,-\tikzumlcallheight ex) node[coordinate, name=tikzumlTmpNode] {};% + \node[tikzuml activity style] (eb-\tikzumlCall@nodeName) at (\tikzumlCallEndNodeName |- tikzumlTmpNode) {};% + \draw (eb-\tikzumlCall@nodeName)+(0,-0.75*\tikzumlCallPadding ex) node[coordinate, name=tikzumlTmpNode] {};% + \node[tikzuml activity style] (sb-\tikzumlCall@nodeName) at (\tikzumlCallStartNodeName |- tikzumlTmpNode) {};% + }{% + \draw (\tikzumlCallBottomSrc)+(0,-\tikzumlcallheight ex) node[coordinate, name=tikzumlTmpNode] {};% + \node[tikzuml activity style] (eb-\tikzumlCall@nodeName) at (\tikzumlCallEndNodeName |- tikzumlTmpNode) {};% + \node[tikzuml activity style] (sb-\tikzumlCall@nodeName) at (\tikzumlCallStartNodeName |- eb-\tikzumlCall@nodeName) {};% + }% + % + \xdef\tikzumlCallBottomSrc{sb-\tikzumlCall@nodeName}% + \fi% + \end{pgfonlayer}% + % + % draw activity periods + \begin{pgfonlayer}{activity}% + \ifstrequal{\tikzumlCallStartNodeName}{\tikzumlCallEndNodeName}{% + % draw root activity period only + \ifnum\thetikzumlCallLevel=0% + \draw[draw=\tikzumldrawcall, fill=\tikzumlfillcall] (st-\tikzumlCall@nodeName.north west) rectangle (sb-\tikzumlCall@nodeName.south east);% + \else% + % draw root activity from inner call + \ifthenelse{\equal{\tikzumlCallStartNodeName}{\tikzumlCallEndNodeNameold}}{}{% + \draw[draw=\tikzumldrawcall, fill=\tikzumlfillcall] (st-\tikzumlCall@nodeName.north west) rectangle (sb-\tikzumlCall@nodeName.south east);% + }% + \fi% + }{% + % draw root activity period + \ifnum\thetikzumlCallLevel=0% + \draw[draw=\tikzumldrawcall, fill=\tikzumlfillcall] (st-\tikzumlCall@nodeName.north west) rectangle (sb-\tikzumlCall@nodeName.south east);% + \else% + % draw root activity from inner call + \ifthenelse{\equal{\tikzumlCallStartNodeName}{\tikzumlCallEndNodeNameold}}{}{% + \draw[draw=\tikzumldrawcall, fill=\tikzumlfillcall] (st-\tikzumlCall@nodeName.north west) rectangle (sb-\tikzumlCall@nodeName.south east);% + }% + \fi% + % draw receiver activity period + \draw[draw=\tikzumldrawcall, fill=\tikzumlfillcall] (et-\tikzumlCall@nodeName.north west) rectangle (eb-\tikzumlCall@nodeName.south east);% + }% + \end{pgfonlayer}% + \ifthenelse{\equal{\tikzumlCallDefaultFillColor}{\tikzumlCallFillColor}}{}{% + \fill[\tikzumlfillcall] (st-\tikzumlCall@nodeName.north west) rectangle (sb-\tikzumlCall@nodeName.south east);% + \draw[\tikzumldrawcall] (st-\tikzumlCall@nodeName.north west) rectangle (sb-\tikzumlCall@nodeName.south west) (st-\tikzumlCall@nodeName.north east) rectangle (sb-\tikzumlCall@nodeName.south east);% + }% + % + % update last nodes drawn on sender and receiver lifelines + \expandafter\xdef\csname tikzumlLastChild@\tikzumlCallEndNodeName \endcsname{eb-\tikzumlCall@nodeName}% + \expandafter\xdef\csname tikzumlLastChild@\tikzumlCallStartNodeName \endcsname{sb-\tikzumlCall@nodeName}% + % + % draw call arrows + \begin{pgfonlayer}{connections}% + \ifstrequal{\tikzumlCallStartNodeName}{\tikzumlCallEndNodeName}{% + \draw[\tikzumlcallstyle, \tikzumldrawcall] (st-\tikzumlCall@nodeName.east) -- ++(2.5*\tikzumlCallPadding ex,0) % + -- ++(0,-0.75*\tikzumlCallPadding ex) % + node[font=\tikzumlDefaultFont, text=\tikzumltextcall, midway, right, name=\tikzumlCall@nodeName-op] {\tikzumlCallOp} % + -- (et-\tikzumlCall@nodeName.east);% + % + % draw return arrow and update fit for parent fragment + \ifthenelse{\equal{\tikzumltypecall}{synchron}}{% + \ifthenelse{\NOT\equal{\tikzumlCallReturn}{}\OR\equal{\tikzumlCallWithReturn}{tikzumlTrue}}{% + \ifnum\c@tikzumlFragmentLevel>0% + \edef\tikzumlFragmentFitOld{\csname tikzumlFragmentFit\tikzumlFragment@name \endcsname}% + \expandafter\xdef\csname tikzumlFragmentFit\tikzumlFragment@name \endcsname{\tikzumlFragmentFitOld (st-\tikzumlCall@nodeName) (et-\tikzumlCall@nodeName) (eb-\tikzumlCall@nodeName) (sb-\tikzumlCall@nodeName) (\tikzumlCall@nodeName-op) (\tikzumlCall@nodeName-return)}% + \fi% + % + \draw[tikzuml call return style, \tikzumldrawcall] (eb-\tikzumlCall@nodeName.east) -- ++(2.5*\tikzumlCallPadding ex,0) + -- ++(0,-0.75*\tikzumlCallPadding ex) % + node[font=\tikzumlDefaultFont, text=\tikzumltextcall, midway, right, name=\tikzumlCall@nodeName-return] {\tikzumlCallReturn} % + -- (sb-\tikzumlCall@nodeName.east);% + }{% + \ifnum\c@tikzumlFragmentLevel>0% + \edef\tikzumlFragmentFitOld{\csname tikzumlFragmentFit\tikzumlFragment@name \endcsname}% + \expandafter\xdef\csname tikzumlFragmentFit\tikzumlFragment@name \endcsname{\tikzumlFragmentFitOld (st-\tikzumlCall@nodeName) (et-\tikzumlCall@nodeName) (eb-\tikzumlCall@nodeName) (sb-\tikzumlCall@nodeName) (\tikzumlCall@nodeName-op)}% + \fi% + }% + }{% + \ifnum\c@tikzumlFragmentLevel>0% + \edef\tikzumlFragmentFitOld{\csname tikzumlFragmentFit\tikzumlFragment@name \endcsname}% + \expandafter\xdef\csname tikzumlFragmentFit\tikzumlFragment@name \endcsname{\tikzumlFragmentFitOld (st-\tikzumlCall@nodeName) (et-\tikzumlCall@nodeName) (eb-\tikzumlCall@nodeName) (sb-\tikzumlCall@nodeName) (\tikzumlCall@nodeName-op)}% + \fi% + }% + }{% + % draw call arrows + \pgfextractx{\tikzumlCall@xa}{\pgfpointanchor{\tikzumlCallStartNodeName}{center}}% + \pgfextractx{\tikzumlCall@xb}{\pgfpointanchor{\tikzumlCallEndNodeName}{center}}% + % + \ifthenelse{\tikzumlCall@xb>\tikzumlCall@xa}{% + \draw[\tikzumlcallstyle, \tikzumldrawcall] (st-\tikzumlCall@nodeName.east) -- (et-\tikzumlCall@nodeName.west) % + node[font=\tikzumlDefaultFont, text=\tikzumltextcall, midway, above=-0.4ex, name=\tikzumlCall@nodeName-op] {\tikzumlCallOp};% + }{% + \draw[\tikzumlcallstyle, \tikzumldrawcall] (st-\tikzumlCall@nodeName.west) -- (et-\tikzumlCall@nodeName.east) % + node[font=\tikzumlDefaultFont, text=\tikzumltextcall, midway, above=-0.4ex, name=\tikzumlCall@nodeName-op] {\tikzumlCallOp};% + }% + % + % draw return arrow and update fit for parent fragment + \ifthenelse{\equal{\tikzumltypecall}{synchron}}{% + \ifthenelse{\NOT\equal{\tikzumlCallReturn}{}\OR\equal{\tikzumlCallWithReturn}{tikzumlTrue}}{% + \ifnum\c@tikzumlFragmentLevel>0% + \edef\tikzumlFragmentFitOld{\csname tikzumlFragmentFit\tikzumlFragment@name \endcsname}% + \expandafter\xdef\csname tikzumlFragmentFit\tikzumlFragment@name \endcsname{\tikzumlFragmentFitOld (st-\tikzumlCall@nodeName) (et-\tikzumlCall@nodeName) (eb-\tikzumlCall@nodeName) (sb-\tikzumlCall@nodeName) (\tikzumlCall@nodeName-op) (\tikzumlCall@nodeName-return)}% + \fi% + % + \ifthenelse{\tikzumlCall@xb>\tikzumlCall@xa}{% + \draw[tikzuml call return style, \tikzumldrawcall] (eb-\tikzumlCall@nodeName.west) -- (sb-\tikzumlCall@nodeName.east) % + node[font=\tikzumlDefaultFont, text=\tikzumltextcall, midway, above=-0.4ex, name=\tikzumlCall@nodeName-return] {\tikzumlCallReturn};% + }{% + \draw[tikzuml call return style, \tikzumldrawcall] (eb-\tikzumlCall@nodeName.east) -- (sb-\tikzumlCall@nodeName.west) % + node[font=\tikzumlDefaultFont, text=\tikzumltextcall, midway, above=-0.4ex, name=\tikzumlCall@nodeName-return] {\tikzumlCallReturn};% + }% + }{% + \ifnum\c@tikzumlFragmentLevel>0% + \edef\tikzumlFragmentFitOld{\csname tikzumlFragmentFit\tikzumlFragment@name \endcsname}% + \expandafter\xdef\csname tikzumlFragmentFit\tikzumlFragment@name \endcsname{\tikzumlFragmentFitOld (st-\tikzumlCall@nodeName) (et-\tikzumlCall@nodeName) (eb-\tikzumlCall@nodeName) (sb-\tikzumlCall@nodeName) (\tikzumlCall@nodeName-op)}% + \fi% + }% + }{% + \ifnum\c@tikzumlFragmentLevel>0% + \edef\tikzumlFragmentFitOld{\csname tikzumlFragmentFit\tikzumlFragment@name \endcsname}% + \expandafter\xdef\csname tikzumlFragmentFit\tikzumlFragment@name \endcsname{\tikzumlFragmentFitOld (st-\tikzumlCall@nodeName) (et-\tikzumlCall@nodeName) (eb-\tikzumlCall@nodeName) (sb-\tikzumlCall@nodeName) (\tikzumlCall@nodeName-op)}% + \fi% + }% + }% + \end{pgfonlayer}% +}% +% +% alias for function self call +\newenvironment{umlcallself}[2][]{\begin{umlcall}[#1]{#2}{#2} }{\end{umlcall}}% +% +% define a combined fragment +% optional : name: name of fragment +% type: type of fragment (opt, alt, break, loop, par, critical, ignore, consider, assert, neg, weak, strict, ref) +% label: label of fragment (ex : condition for opt, iterator for loop, ...) +% inner xsep, inner ysep: padding of the fragment box +% draw, fill, text: colors +\newenvironment{umlfragment}[1][]{% + % define a fragment separator + % optional : label of the fragment part (ex : else for alt) + \providecommand{\umlfpart}[1][]{% + \stepcounter{tikzumlFragmentPartNum}% + % + \node[outer sep=0, inner xsep=\tikzumlFragmentXSep ex, inner ysep=\tikzumlFragmentYSep ex, fit=\csname tikzumlFragmentFit\tikzumlFragment@name \endcsname, name=\tikzumlFragment@name-Part-tmp] {};% + \node[anchor=east, name=\tikzumlFragment@name-PartType-\thetikzumlFragmentPartNum] at (\tikzumlFragment@name-Part-tmp.north west |- \tikzumlCallBottomSrc) {\phantom{\tikzumlFragmentType}};% + \draw (\tikzumlFragment@name-PartType-\thetikzumlFragmentPartNum.north west |- \tikzumlCallBottomSrc)+(0,-0.4*\tikzumlFragment@paddingy ex) node[name=\tikzumlFragment@name-PartWest-\thetikzumlFragmentPartNum] {};% + \draw (\tikzumlFragment@name-Part-tmp.north east |- \tikzumlCallBottomSrc)+(0,-0.4*\tikzumlFragment@paddingy ex) node[name=\tikzumlFragment@name-PartEast-\thetikzumlFragmentPartNum] {};% + \draw[dashed] (\tikzumlFragment@name-PartWest-\thetikzumlFragmentPartNum) -- (\tikzumlFragment@name-PartEast-\thetikzumlFragmentPartNum); + \draw (\tikzumlFragment@name-PartType-\thetikzumlFragmentPartNum)+(0,-0.4*\tikzumlFragment@paddingy ex) node[name=tikzumlTmpNode] {\phantom{\tikzumlFragmentType}};% + \node[anchor=north west] at (tikzumlTmpNode.south west) {[##1]};% + }% + % + \stepcounter{tikzumlFragmentNum}% + % + \pgfkeys{/tikzuml/fragment/.cd, name/.initial=fragment@\alph{tikzumlFragmentNum}, type/.initial=\tikzumlFragmentDefaultType,% + label/.initial=tikzumlEmpty,% + inner xsep/.initial=\tikzumlFragmentDefaultXSep, inner ysep/.initial=\tikzumlFragmentDefaultYSep,% + draw/.initial=\tikzumlDefaultDrawColor, fill/.initial=\tikzumlFragmentDefaultFillColor,% + text/.initial=\tikzumlDefaultTextColor,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \errmessage{TIKZUML ERROR : in umlfragment, invalid option \keyname}% + }% + }% + \pgfkeys{/tikzuml/fragment/.cd, #1}% + \pgfkeys{/tikzuml/fragment/.cd, name/.get=\tikzumlFragmentName, type/.get=\tikzumlFragmentType,% + label/.get=\tikzumlFragmentLabel,% + inner xsep/.get=\tikzumlFragmentXSep, inner ysep/.get=\tikzumlFragmentYSep,% + draw/.get=\tikzumlFragmentDrawColor, fill/.get=\tikzumlFragmentFillColor,% + text/.get=\tikzumlFragmentTextColor% + }% + % + \ifthenelse{\equal{\tikzumlFragmentLabel}{tikzumlEmpty}}{% + \def\tikzumlFragmentLabel{}% + }{% + \let\tikzumlFragmentLabelold\tikzumlFragmentLabel% + \def\tikzumlFragmentLabel{[\tikzumlFragmentLabelold]}% + }% + % + \ifnum\thetikzumlFragmentLevel>0% + \let\tikzumlFragment@parentold\tikzumlFragment@parent% + \let\tikzumlFragment@nameold\tikzumlFragment@name% + \edef\tikzumlFragment@parent{\tikzumlFragment@nameold}% + \else% + \setcounter{tikzumlFragmentPartNum}{0}% + \edef\tikzumlFragment@parent{}% + \edef\tikzumlFragment@parentold{}% + \edef\tikzumlFragment@nameold{}% + \fi% + % + \edef\tikzumlFragment@name{\tikzumlFragmentName}% + \expandafter\gdef\csname tikzumlFragmentFit\tikzumlFragment@name \endcsname{}% + % + \stepcounter{tikzumlFragmentLevel}% + % + \ifnum\thetikzumlCallLevel>0% + \stepcounter{tikzumlCallStartFragmentNum}% + \fi% + % + \pgfmathparse{6*\tikzumlFragmentYSep}% + \xdef\tikzumlFragment@paddingy{\pgfmathresult}% + \if\c@tikzumlFragmentLevelNum=0% + \setcounter{tikzumlFragmentLevelNum}{\thetikzumlFragmentLevel}% + \fi% + % + % time delay adjustment for two consecutive fragments + \ifnum\thetikzumlCallEndFragmentNum>0% + \addtocounter{tikzumlCallEndFragmentNum}{-1} + \fi% +}{% + % + \addtocounter{tikzumlFragmentLevel}{-1}% + % + \ifnum\thetikzumlFragmentLevel>0% + \edef\tikzumlFragmentFitOld{\csname tikzumlFragmentFit\tikzumlFragment@parent \endcsname}% + \expandafter\xdef\csname tikzumlFragmentFit\tikzumlFragment@parent \endcsname{\tikzumlFragmentFitOld (\tikzumlFragment@name)}% + \fi% + % + % draw working fragment box + \begin{pgfonlayer}{fragment\thetikzumlFragmentLevel}% + \node[outer sep=0, inner xsep=\tikzumlFragmentXSep ex, inner ysep=\tikzumlFragmentYSep ex, fit=\csname tikzumlFragmentFit\tikzumlFragment@name \endcsname, name=\tikzumlFragment@name-back] {};% + \end{pgfonlayer}% + % + % draw type and label + \node[text=\tikzumlFragmentTextColor, font=\tikzumlDefaultFont, anchor=north east, name=\tikzumlFragment@name-type] % + at (\tikzumlFragment@name-back.north west) {\tikzumlFragmentType};% + \node[text=\tikzumlFragmentTextColor, font=\tikzumlDefaultFont, anchor=north west, name=\tikzumlFragment@name-label] % + at (\tikzumlFragment@name-type.south west) {\tikzumlFragmentLabel};% + % + % draw final fragment box + \begin{pgfonlayer}{fragment\thetikzumlFragmentLevel}% + \node[draw=\tikzumlFragmentDrawColor, fill=\tikzumlFragmentFillColor, outer sep=0, inner sep=0, font=\tikzumlDefaultFont, fit=(\tikzumlFragment@name-back) (\tikzumlFragment@name-type) (\tikzumlFragment@name-label), name=\tikzumlFragment@name] {};% + \end{pgfonlayer}% + % + \draw[draw=\tikzumlFragmentDrawColor] (\tikzumlFragment@name.north west) rectangle (\tikzumlFragment@name.south east);% + \draw (\tikzumlFragment@name-type.south east)+(0,1ex) node[name=\tikzumlFragment@name-typetop, inner sep=0] {};% + \draw (\tikzumlFragment@name-type.south east)+(-1ex,0) node[name=\tikzumlFragment@name-typeleft, inner sep=0] {};% + \draw (\tikzumlFragment@name.north west) -| (\tikzumlFragment@name-typetop.center) -- (\tikzumlFragment@name-typeleft.center) -| (\tikzumlFragment@name.north west);% + % + \ifnum\thetikzumlCallLevel>0% + \stepcounter{tikzumlCallEndFragmentNum}% + \fi% +}% +% +% define a constructor call +% arg : call sender +% name of constructed object +% optional : x: coordinate of the new object +% stereo: stereotype of the new object +% class: class type of the new object +% dt: time delay from last event +% name: name of the call +% draw, fill, text: colors +% no ddots: when used, disable printing of double dots +\newcommand{\umlcreatecall}[3][]{% + \stepcounter{tikzumlCallNum}% + \edef\tikzumlCall@lastchildNum{\thetikzumlCallNum}% for testing presence of sub-calls + \gdef\tikzumlInCreateCall{1}% + \pgfkeys{/tikzuml/createcall/.cd, x/.initial=tikzumlEmpty, stereo/.initial=\tikzumlObjectDefaultStereo, class/.initial={},% + dt/.initial=\tikzumlCreateCallDefaultDT, name/.initial=call-\thetikzumlCallNum,% + draw/.initial=\tikzumlDefaultDrawColor, fill/.initial=\tikzumlCallDefaultFillColor,% + text/.initial=\tikzumlDefaultTextColor,% + draw obj/.initial=\tikzumlDefaultDrawColor, fill obj/.initial=\tikzumlObjectDefaultFillColor,% + text obj/.initial=\tikzumlDefaultTextColor,% + no ddots/.is if=tikzumlcreatecallNoDDots,% + no ddots=false,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \errmessage{TIKZUML ERROR : in umlcreatecall, invalid option \keyname}% + }% + }% + \pgfkeys{/tikzuml/createcall/.cd, #1}% + \pgfkeys{/tikzuml/createcall/.cd, x/.get=\tikzumlCreateCallX, stereo/.get=\tikzumlCreateCallStereo,% + class/.get=\tikzumlCreateCallClass,% + dt/.get=\tikzumlCreateCallDT, name/.get=\tikzumlCreateCallName,% + draw/.get=\tikzumlCreateCallDrawColor, fill/.get=\tikzumlCreateCallFillColor,% + text/.get=\tikzumlCreateCallTextColor,% + draw obj/.get=\tikzumlCreateCallObjectDrawColor, fill obj/.get=\tikzumlCreateCallObjectFillColor,% + text obj/.get=\tikzumlCreateCallObjectTextColor% + }% + % + \def\tikzumlCreateCallSrc@name{#2}% + % + \begingroup% + \def\_{_}\edef\x{\endgroup% + \def\noexpand\tikzumlCreateCallSrc@nodeName{\tikzumlCreateCallSrc@name}}\x% + % + % managing time delays from previous/parent fragments + \ifnum\thetikzumlCallStartFragmentNum>0% + \let\tikzumlCreateCallDTold\tikzumlCreateCallDT% + \pgfmathparse{0.5*\tikzumlFragment@paddingy+\tikzumlCreateCallDTold}% + \edef\tikzumlCreateCallDT{\pgfmathresult}% + \addtocounter{tikzumlCallStartFragmentNum}{-1} + \fi% + \ifnum\thetikzumlCallEndFragmentNum>0% + \let\tikzumlCreateCallDTold\tikzumlCreateCallDT% + \pgfmathparse{0.5*\tikzumlFragment@paddingy+\tikzumlCreateCallDTold}% + \edef\tikzumlCreateCallDT{\pgfmathresult}% + \addtocounter{tikzumlCallEndFragmentNum}{-1} + \fi% + \ifnum\thetikzumlFragmentPartNum>0% + \let\tikzumlCreateCallDTold\tikzumlCreateCallDT% + \pgfmathparse{0.5*\tikzumlFragment@paddingy+\tikzumlCreateCallDTold}% + \edef\tikzumlCreateCallDT{\pgfmathresult}% + \fi% + % + % managing parent-child structure + \ifnum\thetikzumlCallLevel>0% + \let\tikzumlCall@nameold\tikzumlCall@name% + \def\tikzumlCall@name{\tikzumlCreateCallName}% + \let\tikzumlCall@parentold\tikzumlCall@parent% + \edef\tikzumlCall@parent{\tikzumlCall@parentold @@\tikzumlCall@nameold}% + \else% + \edef\tikzumlCall@parent{}% + \edef\tikzumlCall@parentold{}% + \edef\tikzumlCall@nameold{} + \edef\tikzumlCall@name{\tikzumlCreateCallName}% + \fi% + % + \begingroup% + \def\_{_}\edef\x{\endgroup% + \def\noexpand\tikzumlCreateCall@nodeName{\tikzumlCall@name}}\x% + % + \draw (\csname tikzumlLastChild@\tikzumlCreateCallSrc@nodeName \endcsname)+(0,-\tikzumlCreateCallDT ex) node[name=st-\tikzumlCreateCall@nodeName, tikzuml activity style] {};% + % + \xdef\tikzumlCreateCallObjectSrc{st-\tikzumlCreateCall@nodeName}% + % + \iftikzumlcreatecallNoDDots% + \umlobject[x=\tikzumlCreateCallX, stereo=\tikzumlCreateCallStereo, class=\tikzumlCreateCallClass, draw=\tikzumlCreateCallObjectDrawColor, fill=\tikzumlCreateCallObjectFillColor, text=\tikzumlCreateCallObjectTextColor, no ddots]{#3}% + \else + \umlobject[x=\tikzumlCreateCallX, stereo=\tikzumlCreateCallStereo, class=\tikzumlCreateCallClass, draw=\tikzumlCreateCallObjectDrawColor, fill=\tikzumlCreateCallObjectFillColor, text=\tikzumlCreateCallObjectTextColor]{#3}% + \fi + % + \draw (\csname tikzumlLastChild@\tikzumlCreateCallSrc@nodeName \endcsname |- #3)+(0,-0.5*\tikzumlCreateCallDT ex) node[name=sb-\tikzumlCreateCall@nodeName, tikzuml activity style] {};% + % + \expandafter\xdef\csname tikzumlLastChild@\tikzumlCreateCallSrc@nodeName \endcsname{sb-\tikzumlCreateCall@nodeName}% + \xdef\tikzumlCallBottomSrc{sb-\tikzumlCreateCall@nodeName}% + % + \begin{pgfonlayer}{connections}% + \draw[tikzuml synchron-msg style, \tikzumlCreateCallDrawColor] (st-\tikzumlCreateCall@nodeName) -- (#3) node[midway, above, font=\tikzumlDefaultFont, text=\tikzumlCreateCallTextColor, name=\tikzumlCreateCall@nodeName-op] {create};% + \end{pgfonlayer}% + % + \ifnum\thetikzumlCallLevel=0% + \begin{pgfonlayer}{activity}% + \draw[draw=\tikzumlCreateCallDrawColor, fill=\tikzumlCreateCallFillColor] (st-\tikzumlCreateCall@nodeName.north west) rectangle (sb-\tikzumlCreateCall@nodeName.south east);% + \end{pgfonlayer}% + \fi% + % add to fit fragment + \ifnum\c@tikzumlFragmentLevel>0% + \edef\tikzumlFragmentFitOld{\csname tikzumlFragmentFit\tikzumlFragment@name \endcsname}% + \expandafter\xdef\csname tikzumlFragmentFit\tikzumlFragment@name \endcsname{\tikzumlFragmentFitOld (st-\tikzumlCreateCall@nodeName) (sb-\tikzumlCreateCall@nodeName) (\tikzumlCreateCall@nodeName-op) (#3) }% + \fi% +} +% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% component diagrams % +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +\tikzstyle{tikzuml connector style}=[color=\tikzumlDefaultDrawColor, -]% +% +\newcounter{tikzumlComponentLevel}% +\newcounter{tikzumlComponentSubComponentNum}% +\newcounter{tikzumlConnectorNum}% +\setcounter{tikzumlConnectorNum}{1}% +% +\newif\iftikzumlcomponentWithoutCoords% +% +\newcommand{\picturedcomponent}[1]{% + \pgfkeys{/tikzuml/component/picture/.cd, scale/.initial=1, .unknown/.code={}}% + \pgfkeys{/tikzuml/component/picture/.cd,#1}% + \pgfkeys{/tikzuml/component/picture/.cd, scale/.get=\tikzumlComponentScale}% + \begin{tikzpicture}[#1]% + \filldraw (0,0) rectangle (1ex,1.5ex);% + \filldraw (-0.2ex,0.4ex) rectangle (0.2ex,0.6ex);% + \filldraw (-0.2ex,0.9ex) rectangle (0.2ex,1.1ex);% + \end{tikzpicture}% +}% +% +% define a uml component +% args : name of the component +% content of the component +% optional args : x,y coordinates of the component +% width of the component node +\newenvironment{umlcomponent}[2][]{% + \ifnum\thetikzumlComponentLevel>0% + \let\tikzumlComponent@nameold\tikzumlComponent@fitname% + \let\tikzumlComponent@parentold\tikzumlComponent@parent% + \edef\tikzumlComponent@parent{\tikzumlComponent@parentold @@\tikzumlComponent@nameold}% + \else% + \def\tikzumlComponent@parent{}% + \fi% + % + \stepcounter{tikzumlComponentLevel}% + % + \pgfkeys{/tikzuml/component/.cd, x/.initial=\tikzumlDefaultX, y/.initial=\tikzumlDefaultX, width/.initial=\tikzumlComponentDefaultWidth, name/.initial={},% + draw/.initial=\tikzumlDefaultDrawColor, fill/.initial=\tikzumlComponentDefaultFillColor,% + text/.initial=\tikzumlDefaultTextColor, style/.style={},% + no coords/.is if=tikzumlcomponentWithoutCoords,% + no coords=false,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{above}\OR% + \equal{\keyname}{left}\OR% + \equal{\keyname}{below}\OR% + \equal{\keyname}{right}\OR% + \equal{\keyname}{above left}\OR% + \equal{\keyname}{above right}\OR% + \equal{\keyname}{below left}\OR% + \equal{\keyname}{below right}}{% + \IfSubStr{\keyvalue}{ of }{% + \pgfkeys{/tikzuml/component/.cd, no coords}% + }{}% + }{}% + \ifx\keyvalue\pgfkeysnovalue% + \pgfkeys{/tikzuml/component/.cd, style/.append style/.expand once={\keyname}}% + \else% + \pgfkeys{/tikzuml/component/.cd, style/.append style/.expand twice={\expandafter\keyname\expandafter=\keyvalue}}% + \fi% + %\errmessage{TIKZUML ERROR : in umlcomponent, invalid option \keyname}% + }% + }% + \pgfkeys{/tikzuml/component/.cd, #1}% + \pgfkeys{/tikzuml/component/.cd, x/.get=\tikzumlComponentXShift, y/.get=\tikzumlComponentYShift,% + width/.get=\tikzumlComponentMinimumWidth, name/.get=\tikzumlComponentName,% + draw/.get=\tikzumlComponentDrawColor, fill/.get=\tikzumlComponentFillColor,% + text/.get=\tikzumlComponentTextColor% + }% + % + \ifthenelse{\equal{\tikzumlComponentName}{}}{% + \edef\tikzumlComponent@name{#2}% + }{% + \edef\tikzumlComponent@name{\tikzumlComponentName}% + }% + % + \begingroup% + \def\_{@}\edef\x{\endgroup% + \def\noexpand\tikzumlComponent@fitname{\tikzumlComponent@name}}\x% + % + \let\tikzumlComponent@nodeNameold\tikzumlComponent@nodeName% + \def\tikzumlComponent@caption{#2}% + % + \begingroup% + \def\_{_}\edef\x{\endgroup% + \def\noexpand\tikzumlComponent@nodeName{\tikzumlComponent@name}}\x% + % + \expandafter\gdef\csname tikzumlComponentFit\tikzumlComponent@parent @@\tikzumlComponent@fitname\endcsname{}% + % + \setcounter{tikzumlComponentSubComponentNum}{0}% + % + \begin{scope}[xshift=\tikzumlComponentXShift cm, yshift=\tikzumlComponentYShift cm]% +}{% + \addtocounter{tikzumlComponentLevel}{-1}% + \begin{pgfonlayer}{component\thetikzumlComponentLevel}% + % + % if contains nothing, one define a fictive node to enable the fit option + \ifnum\c@tikzumlComponentSubComponentNum=0% + \iftikzumlcomponentWithoutCoords% + \node[inner ysep=0.5ex, minimum width=\tikzumlComponentMinimumWidth, font=\tikzumlDefaultFont, /tikzuml/component/style] (\tikzumlComponent@nodeName-root) {\phantom{\tikzumlComponent@nodeName}};% + \else% + \node[inner ysep=0.5ex, minimum width=\tikzumlComponentMinimumWidth, font=\tikzumlDefaultFont, /tikzuml/component/style] (\tikzumlComponent@nodeName-root) at (0,0) {\phantom{\tikzumlComponent@nodeName}};% + \fi% + \expandafter\xdef\csname tikzumlComponentFit\tikzumlComponent@parent @@\tikzumlComponent@fitname\endcsname{(\tikzumlComponent@nodeName-root)}% + \fi% + % + \ifnum\c@tikzumlComponentLevel>0% + \def\tikzumlComponentFitTmp{\csname tikzumlComponentFit\tikzumlComponent@parent\endcsname}% + \expandafter\xdef\csname tikzumlComponentFit\tikzumlComponent@parent\endcsname{\tikzumlComponentFitTmp (\tikzumlComponent@nodeName-body) (\tikzumlComponent@nodeName-caption)}% + \stepcounter{tikzumlComponentSubComponentNum}% + \fi% + % + \node[inner sep=2ex, font=\tikzumlDefaultFont, fit = \csname tikzumlComponentFit\tikzumlComponent@parent @@\tikzumlComponent@fitname\endcsname] (\tikzumlComponent@nodeName-body) {};% + \node[above, font=\tikzumlDefaultFont] (\tikzumlComponent@nodeName-captiontmp) at (\tikzumlComponent@nodeName-body.north) {\tikzumlComponent@caption};% + \node (\tikzumlComponent@nodeName-logotmp) at (\tikzumlComponent@nodeName-captiontmp.north -| \tikzumlComponent@nodeName-body.east) {\picturedcomponent{draw=\tikzumlComponentDrawColor, fill=\tikzumlComponentFillColor, font=\tikzumlDefaultFont} };% + \node[draw=\tikzumlComponentDrawColor, fill=\tikzumlComponentFillColor, name=\tikzumlComponent@nodeName, /tikzuml/component/style, fit=(\tikzumlComponent@nodeName-body) (\tikzumlComponent@nodeName-captiontmp)] {};% + \node[font=\tikzumlDefaultFont] (\tikzumlComponent@nodeName-caption) at (\tikzumlComponent@nodeName-captiontmp) {\tikzumlComponent@caption};% + \draw (\tikzumlComponent@nodeName-caption.north -| \tikzumlComponent@nodeName.east) node[font=\tikzumlDefaultFont, xshift=-1ex, below=-1ex, name=\tikzumlComponent@nodeName-logo] {\picturedcomponent{draw=\tikzumlComponentDrawColor, fill=\tikzumlComponentFillColor, font=\tikzumlDefaultFont} };% + \draw (\tikzumlComponent@nodeName-caption.south -| \tikzumlComponent@nodeName.north west) -- (\tikzumlComponent@nodeName-caption.south -| \tikzumlComponent@nodeName.north east);% + \coordinate (\tikzumlComponent@nodeName-west-port) at (\tikzumlComponent@nodeName.west); + \coordinate (\tikzumlComponent@nodeName-east-port) at (\tikzumlComponent@nodeName.east); + \coordinate (\tikzumlComponent@nodeName-south-port) at (\tikzumlComponent@nodeName.south); + \coordinate (\tikzumlComponent@nodeName-north-port) at (\tikzumlComponent@nodeName.north); + \end{pgfonlayer}% + \end{scope}% + % + % add to fit + \ifnum\c@tikzumlPackageLevel>0% + \edef\tikzumlPackageFitOld{\csname tikzumlPackageFit\tikzumlPackage@parent @@\tikzumlPackage@fitname\endcsname}% + \expandafter\xdef\csname tikzumlPackageFit\tikzumlPackage@parent @@\tikzumlPackage@fitname\endcsname{\tikzumlPackageFitOld (\tikzumlComponent@nodeName)}% + \stepcounter{tikzumlPackageClassNum}% + \fi% +}% +% +% shortcut for empty component +\newcommand{\umlbasiccomponent}[2][]{\begin{umlcomponent}[#1]{#2} \end{umlcomponent}}% +% +\newcommand{\umlrequiredinterface}[2][]{% + \def\tikzumlInterfaceWithPort{tikzumlFalse}% + \pgfkeys{/tikzuml/requiredinterfacerelation/.cd, interface/.initial={}, distance/.initial=\tikzumlRequiredInterfaceDefaultDistance,% + name/.initial=tikzumlEmpty, width/.initial=\tikzumlRequiredInterfaceDefaultWidth,% + padding/.initial=\tikzumlRequiredInterfaceDefaultPadding,% + draw/.initial=\tikzumlDefaultDrawColor, fill/.initial=\tikzumlComponentDefaultFillColor,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{with port}}{% + \def\tikzumlInterfaceWithPort{tikzumlTrue}% + }{}% + }% + }% + \pgfkeys{/tikzuml/requiredinterfacerelation/.cd, #1}% + \pgfkeys{/tikzuml/requiredinterfacerelation/.cd, interface/.get=\tikzumlRequiredInterfaceLabel,% + distance/.get=\tikzumlRequiredInterfaceDistance,% + name/.get=\tikzumlRequiredInterfaceName,% + width/.get=\tikzumlRequiredInterfaceWidth,% + padding/.get=\tikzumlRequiredInterfacePadding,% + draw/.get=\tikzumlRequiredInterfaceDrawColor,% + fill/.get=\tikzumlRequiredInterfaceFillColor% + }% + % + \ifthenelse{\equal{\tikzumlRequiredInterfaceName}{tikzumlEmpty}}{% + \edef\tikzumlRequiredInterface@interfacename{#2-east-interface}% + \edef\tikzumlRequiredInterface@portname{#2-east-port}% + \edef\tikzumlRequiredInterface@paddingname{#2-east-padding}% + }{% + \edef\tikzumlRequiredInterface@interfacename{\tikzumlRequiredInterfaceName}% + \edef\tikzumlRequiredInterface@portname{\tikzumlRequiredInterfaceName-port}% + \edef\tikzumlRequiredInterface@paddingname{\tikzumlRequiredInterfaceName-padding}% + }% + % + \edef\tikzumlRequiredInterface@name{#2}% + % + \begingroup% + \def\_{_}\edef\x{\endgroup% + \def\noexpand\tikzumlRequiredInterface@nodeName{\tikzumlRequiredInterface@name}}\x% + % + \begingroup% + \def\_{_}\edef\x{\endgroup% + \def\noexpand\tikzumlRequiredInterface@interfacenodeName{\tikzumlRequiredInterface@interfacename}}\x% + % + \begingroup% + \def\_{_}\edef\x{\endgroup% + \def\noexpand\tikzumlRequiredInterface@portnodeName{\tikzumlRequiredInterface@portname}}\x% + % + \begingroup% + \def\_{_}\edef\x{\endgroup% + \def\noexpand\tikzumlRequiredInterface@paddingnodeName{\tikzumlRequiredInterface@paddingname}}\x% + % + \ifthenelse{\equal{\tikzumlInterfaceWithPort}{tikzumlTrue}}{% + \node[inner sep=0.5*\tikzumlRequiredInterfaceWidth, rectangle, draw=\tikzumlRequiredInterfaceDrawColor, fill=\tikzumlRequiredInterfaceFillColor] (\tikzumlRequiredInterface@portnodeName) at (\tikzumlRequiredInterface@nodeName.east) {};% + }{% + \node[inner sep=0] (\tikzumlRequiredInterface@nodeName-east-port) at (\tikzumlRequiredInterface@nodeName.east) {};% + }% + \begin{scope}% + \draw (\tikzumlRequiredInterface@nodeName)+(\tikzumlRequiredInterfaceDistance,0) node[inner sep=0, text width=\tikzumlRequiredInterfaceWidth, circle, name=\tikzumlRequiredInterface@interfacenodeName-tmp] {};% + \clip (\tikzumlRequiredInterface@interfacenodeName-tmp.north) rectangle (\tikzumlRequiredInterface@interfacenodeName-tmp.south -| \tikzumlRequiredInterface@interfacenodeName-tmp.west);% + \node[inner sep=0, text width=\tikzumlRequiredInterfaceWidth, circle, draw=\tikzumlRequiredInterfaceDrawColor] (\tikzumlRequiredInterface@interfacenodeName) at (\tikzumlRequiredInterface@interfacenodeName-tmp) {};% + \end{scope}% + \node[above] at (\tikzumlRequiredInterface@interfacenodeName.north) {\tikzumlRequiredInterfaceLabel};% + % + \umlrelation[style={tikzuml connector style}, #1]{\tikzumlRequiredInterface@portnodeName}{\tikzumlRequiredInterface@interfacenodeName}% + % + \draw (\tikzumlRequiredInterface@interfacenodeName)+(\tikzumlRequiredInterfacePadding,0) node[name=\tikzumlRequiredInterface@paddingnodeName] {};% + % + % add to fit + \ifnum\c@tikzumlComponentLevel>0% + \def\tikzumlComponentFitTmp{\csname tikzumlComponentFit\tikzumlComponent@parent @@\tikzumlComponent@fitname\endcsname}% + \expandafter\xdef\csname tikzumlComponentFit\tikzumlComponent@parent @@\tikzumlComponent@fitname\endcsname{\tikzumlComponentFitTmp (\tikzumlRequiredInterface@paddingnodeName) (\tikzumlRequiredInterface@portnodeName) }% + \fi% +}% +% +\newcommand{\umlprovidedinterface}[2][]{% + \def\tikzumlInterfaceWithPort{tikzumlFalse}% + \pgfkeys{/tikzuml/providedinterfacerelation/.cd, interface/.initial={}, distance/.initial=\tikzumlProvidedInterfaceDefaultDistance,% + name/.initial=tikzumlEmpty, width/.initial=\tikzumlProvidedInterfaceDefaultWidth,% + padding/.initial=\tikzumlProvidedInterfaceDefaultPadding,% + draw/.initial=\tikzumlDefaultDrawColor, fill/.initial=\tikzumlComponentDefaultFillColor,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{with port}}{% + \def\tikzumlInterfaceWithPort{tikzumlTrue}% + }{}% + }% + }% + \pgfkeys{/tikzuml/providedinterfacerelation/.cd, #1}% + \pgfkeys{/tikzuml/providedinterfacerelation/.cd, interface/.get=\tikzumlProvidedInterfaceLabel,% + distance/.get=\tikzumlProvidedInterfaceDistance,% + name/.get=\tikzumlProvidedInterfaceName,% + width/.get=\tikzumlProvidedInterfaceWidth,% + padding/.get=\tikzumlProvidedInterfacePadding,% + draw/.get=\tikzumlProvidedInterfaceDrawColor,% + fill/.get=\tikzumlProvidedInterfaceFillColor% + }% + % + \ifthenelse{\equal{\tikzumlProvidedInterfaceName}{tikzumlEmpty}}{% + \edef\tikzumlProvidedInterface@interfacename{#2-west-interface}% + \edef\tikzumlProvidedInterface@portname{#2-west-port}% + \edef\tikzumlProvidedInterface@paddingname{#2-west-padding}% + }{% + \edef\tikzumlProvidedInterface@interfacename{\tikzumlProvidedInterfaceName}% + \edef\tikzumlProvidedInterface@portname{\tikzumlProvidedInterfaceName-port}% + \edef\tikzumlProvidedInterface@paddingname{\tikzumlProvidedInterfaceName-padding}% + }% + % + \edef\tikzumlProvidedInterface@name{#2}% + % + \begingroup% + \def\_{_}\edef\x{\endgroup% + \def\noexpand\tikzumlProvidedInterface@nodeName{\tikzumlProvidedInterface@name}}\x% + % + \begingroup% + \def\_{_}\edef\x{\endgroup% + \def\noexpand\tikzumlProvidedInterface@interfacenodeName{\tikzumlProvidedInterface@interfacename}}\x% + % + \begingroup% + \def\_{_}\edef\x{\endgroup% + \def\noexpand\tikzumlProvidedInterface@portnodeName{\tikzumlProvidedInterface@portname}}\x% + % + \begingroup% + \def\_{_}\edef\x{\endgroup% + \def\noexpand\tikzumlProvidedInterface@paddingnodeName{\tikzumlProvidedInterface@paddingname}}\x% + % + \ifthenelse{\equal{\tikzumlInterfaceWithPort}{tikzumlTrue}}{% + \node[inner sep=0.5*\tikzumlProvidedInterfaceWidth, rectangle, draw=\tikzumlProvidedInterfaceDrawColor, fill=\tikzumlProvidedInterfaceFillColor] (\tikzumlProvidedInterface@portnodeName) at (\tikzumlProvidedInterface@nodeName.west) {};% + }{% + \node[inner sep=0] (\tikzumlProvidedInterface@portnodeName) at (\tikzumlProvidedInterface@nodeName.west) {};% + }% + \draw (\tikzumlProvidedInterface@nodeName)+(-\tikzumlProvidedInterfaceDistance,0) node[inner sep=0, text width=\tikzumlProvidedInterfaceWidth, circle, draw=\tikzumlProvidedInterfaceDrawColor, fill=\tikzumlProvidedInterfaceFillColor, name=\tikzumlProvidedInterface@interfacenodeName] {};% + \node[above] at (\tikzumlProvidedInterface@interfacenodeName.north) + {\tikzumlProvidedInterfaceLabel};% + % + \umlrelation[style={tikzuml connector style}, #1]{\tikzumlProvidedInterface@portnodeName}{\tikzumlProvidedInterface@interfacenodeName}% + % + \draw (\tikzumlProvidedInterface@interfacenodeName)+(-\tikzumlProvidedInterfacePadding,0) node[name=\tikzumlProvidedInterface@paddingnodeName] {};% + % + % add to fit + \ifnum\thetikzumlComponentLevel>0% + \def\tikzumlComponentFitTmp{\csname tikzumlComponentFit\tikzumlComponent@parent @@\tikzumlComponent@fitname\endcsname}% + \expandafter\xdef\csname tikzumlComponentFit\tikzumlComponent@parent @@\tikzumlComponent@fitname\endcsname{\tikzumlComponentFitTmp (\tikzumlProvidedInterface@paddingnodeName) (\tikzumlProvidedInterface@portnodeName) }% + \fi% +}% +% +\newlength{\tikzuml@AC@xa}% +\newlength{\tikzuml@AC@ya}% +\newlength{\tikzuml@AC@xb}% +\newlength{\tikzuml@AC@yb}% +\newlength{\tikzuml@AC@xi}% +\newlength{\tikzuml@AC@yi}% +\newlength{\tikzuml@AC@xic}% +\newlength{\tikzuml@AC@yic}% +\newlength{\tikzuml@AC@xio}% +\newlength{\tikzuml@AC@yio}% +\newlength{\tikzuml@AC@AB}% +\newlength{\tikzuml@AC@lambda}% +\newlength{\tikzuml@AC@xtrc}% +\newlength{\tikzuml@AC@ytrc}% +\newlength{\tikzuml@AC@xtlc}% +\newlength{\tikzuml@AC@ytlc}% +\newlength{\tikzuml@AC@xblc}% +\newlength{\tikzuml@AC@yblc}% +\newlength{\tikzuml@AC@xbrc}% +\newlength{\tikzuml@AC@ybrc}% +\newlength{\tikzuml@AC@middleArm}% +% +\newcommand{\umlassemblyconnectorsymbol}[2]{% + \ifthenelse{\NOT\equal{\tikzumlAssemblyConnectorLabel}{}}{% + \edef\tikzuml@ACStart@name{#1}% + \edef\tikzuml@ACEnd@name{#2}% + \edef\tikzuml@AC@width{\tikzumlAssemblyConnectorWidth}% + % + \begingroup% + \def\_{_}\edef\x{\endgroup% + \def\noexpand\tikzuml@ACStart@nodeName{\tikzuml@ACStart@name}}\x% + % + \begingroup% + \def\_{_}\edef\x{\endgroup% + \def\noexpand\tikzuml@ACEnd@nodeName{\tikzuml@ACEnd@name}}\x% + % + \begingroup% + \def\_{_}\edef\x{\endgroup% + \def\noexpand\tikzuml@ACInterface@nodeName{\tikzumlAssemblyConnectorSymbolName}}\x% + % + \pgfextractx{\tikzuml@AC@xa}{\pgfpointanchor{\tikzuml@ACStart@nodeName}{\tikzumlAssemblyConnectorStartAnchor}}% + \pgfextracty{\tikzuml@AC@ya}{\pgfpointanchor{\tikzuml@ACStart@nodeName}{\tikzumlAssemblyConnectorStartAnchor}}% + \pgfextractx{\tikzuml@AC@xb}{\pgfpointanchor{\tikzuml@ACEnd@nodeName}{\tikzumlAssemblyConnectorEndAnchor}}% + \pgfextracty{\tikzuml@AC@yb}{\pgfpointanchor{\tikzuml@ACEnd@nodeName}{\tikzumlAssemblyConnectorEndAnchor}}% + \pgfmathsetlength{\tikzuml@AC@xi}{0.5*\tikzuml@AC@xa+0.5*\tikzuml@AC@xb}% + \pgfmathsetlength{\tikzuml@AC@yi}{0.5*\tikzuml@AC@ya+0.5*\tikzuml@AC@yb}% + \pgfmathsetlength{\tikzuml@AC@AB}{veclen(\tikzuml@AC@xa-\tikzuml@AC@xb,\tikzuml@AC@ya-\tikzuml@AC@yb)}% + \pgfmathsetlength{\tikzuml@AC@lambda}{0.25*\tikzuml@AC@width/\tikzuml@AC@AB}% + \pgfmathsetlength{\tikzuml@AC@xic}{\tikzuml@AC@xi-\tikzuml@AC@lambda*(\tikzuml@AC@xb-\tikzuml@AC@xa)}% + \pgfmathsetlength{\tikzuml@AC@yic}{\tikzuml@AC@yi-\tikzuml@AC@lambda*(\tikzuml@AC@yb-\tikzuml@AC@ya)}% + \pgfmathsetlength{\tikzuml@AC@xio}{\tikzuml@AC@xi+\tikzuml@AC@lambda*(\tikzuml@AC@xb-\tikzuml@AC@xa)}% + \pgfmathsetlength{\tikzuml@AC@yio}{\tikzuml@AC@yi+\tikzuml@AC@lambda*(\tikzuml@AC@yb-\tikzuml@AC@ya)}% + \node[inner sep=0.5*\tikzuml@AC@width] (\tikzuml@ACInterface@nodeName-interface) at (\tikzuml@AC@xi,\tikzuml@AC@yi) {};% + \node[inner sep=0, text width=\tikzuml@AC@width, circle, draw=\tikzumlAssemblyConnectorDrawColor, fill=\tikzumlAssemblyConnectorFillColor] (\tikzuml@ACInterface@nodeName-io) at (\tikzuml@AC@xio,\tikzuml@AC@yio) {};% + \begin{scope}% + \pgfmathsetlength{\tikzuml@AC@xtrc}{\tikzuml@AC@xic-2*\tikzuml@AC@lambda*(\tikzuml@AC@yb-\tikzuml@AC@ya)}% + \pgfmathsetlength{\tikzuml@AC@ytrc}{\tikzuml@AC@yic+2*\tikzuml@AC@lambda*(\tikzuml@AC@xb-\tikzuml@AC@xa)}% + \pgfmathsetlength{\tikzuml@AC@xbrc}{\tikzuml@AC@xic+2*\tikzuml@AC@lambda*(\tikzuml@AC@yb-\tikzuml@AC@ya)}% + \pgfmathsetlength{\tikzuml@AC@ybrc}{\tikzuml@AC@yic-2*\tikzuml@AC@lambda*(\tikzuml@AC@xb-\tikzuml@AC@xa)}% + \pgfmathsetlength{\tikzuml@AC@xtlc}{\tikzuml@AC@xic-3*\tikzuml@AC@lambda*(\tikzuml@AC@yb-\tikzuml@AC@ya+\tikzuml@AC@xb-\tikzuml@AC@xa)}% + \pgfmathsetlength{\tikzuml@AC@ytlc}{\tikzuml@AC@yic+3*\tikzuml@AC@lambda*(\tikzuml@AC@xb-\tikzuml@AC@xa+\tikzuml@AC@ya-\tikzuml@AC@yb)}% + \pgfmathsetlength{\tikzuml@AC@xblc}{\tikzuml@AC@xic+3*\tikzuml@AC@lambda*(\tikzuml@AC@yb-\tikzuml@AC@ya+\tikzuml@AC@xa-\tikzuml@AC@xb)}% + \pgfmathsetlength{\tikzuml@AC@yblc}{\tikzuml@AC@yic-3*\tikzuml@AC@lambda*(\tikzuml@AC@xb-\tikzuml@AC@xa+\tikzuml@AC@yb-\tikzuml@AC@ya)}% + \coordinate (\tikzuml@ACInterface@nodeName-trc) at (\tikzuml@AC@xtrc,\tikzuml@AC@ytrc);% + \coordinate (\tikzuml@ACInterface@nodeName-brc) at (\tikzuml@AC@xbrc,\tikzuml@AC@ybrc);% + \coordinate (\tikzuml@ACInterface@nodeName-tlc) at (\tikzuml@AC@xtlc,\tikzuml@AC@ytlc);% + \coordinate (\tikzuml@ACInterface@nodeName-blc) at (\tikzuml@AC@xblc,\tikzuml@AC@yblc);% + \clip (\tikzuml@ACInterface@nodeName-trc) -- (\tikzuml@ACInterface@nodeName-tlc) -- (\tikzuml@ACInterface@nodeName-blc) -- (\tikzuml@ACInterface@nodeName-brc) -- cycle;% + \node[inner sep=0, text width=\tikzuml@AC@width, circle, draw=\tikzumlAssemblyConnectorDrawColor, fill=\tikzumlAssemblyConnectorFillColor] (\tikzuml@ACInterface@nodeName-ic) at (\tikzuml@AC@xic,\tikzuml@AC@yic) {};% + \end{scope}% + \node[above, font=\tikzumlDefaultFont] at (\tikzuml@ACInterface@nodeName-interface.north) + {\tikzumlAssemblyConnectorLabel};% + }{}% +}% +% +\newcommand{\umlassemblyconnector}[3][]{% + \def\tikzumlAssemblyConnectorWithPort{tikzumlFalse}% + \def\tikzumlAssemblyConnectorFirstArm{tikzumlFalse}% + \def\tikzumlAssemblyConnectorSecondArm{tikzumlFalse}% + \def\tikzumlAssemblyConnectorMiddleArm{tikzumlFalse}% + \def\tikzumlAssemblyConnectorLastArm{tikzumlFalse}% + \pgfkeys{/tikzuml/assemblyconnectorrelation/.cd, geometry/.initial=--, interface/.initial={},% + arm1/.initial={auto}, arm2/.initial={auto},% + name/.initial=connector-\thetikzumlConnectorNum, width/.initial=1em,% + anchor1/.initial={}, anchor2/.initial={},% + draw/.initial=\tikzumlDefaultDrawColor,% + fill assembly connector/.initial=\tikzumlAssemblyConnectorDefaultFillColor,% + fill port/.initial=\tikzumlPortDefaultFillColor,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{with port}}{% + \def\tikzumlAssemblyConnectorWithPort{tikzumlTrue}% + }{% + \ifthenelse{\equal{\keyname}{first arm}}{% + \def\tikzumlAssemblyConnectorFirstArm{tikzumlTrue}% + }{% + \ifthenelse{\equal{\keyname}{second arm}}{% + \def\tikzumlAssemblyConnectorSecondArm{tikzumlTrue}% + }{% + \ifthenelse{\equal{\keyname}{middle arm}}{% + \def\tikzumlAssemblyConnectorMiddleArm{tikzumlTrue}% + }{% + \ifthenelse{\equal{\keyname}{last arm}}{% + \def\tikzumlAssemblyConnectorLastArm{tikzumlTrue}% + }{% + }% + }% + }% + }% + }% + }% + }% + \pgfkeys{/tikzuml/assemblyconnectorrelation/.cd, #1}% + \pgfkeys{/tikzuml/assemblyconnectorrelation/.cd, geometry/.get=\tikzumlAssemblyConnectorGeometry,% + name/.get=\tikzumlAssemblyConnectorName,% + interface/.get=\tikzumlAssemblyConnectorLabel,% + width/.get=\tikzumlAssemblyConnectorWidth,% + arm1/.get=\tikzumlAssemblyConnectorStartArm,% + arm2/.get=\tikzumlAssemblyConnectorEndArm,% + anchor1/.get=\tikzumlAssemblyConnectorStartAnchorTmp,% + anchor2/.get=\tikzumlAssemblyConnectorEndAnchorTmp,% + draw/.get=\tikzumlAssemblyConnectorDrawColor,% + fill assembly connector/.get=\tikzumlAssemblyConnectorFillColor,% + fill port/.get=\tikzumlAssemblyConnectorPortFillColor% + }% + % + \edef\tikzumlAssemblyConnectorStart@name{#2}% + \edef\tikzumlAssemblyConnectorEnd@name{#3}% + % + \begingroup% + \def\_{_}\edef\x{\endgroup% + \def\noexpand\tikzumlAssemblyConnectorStart@nodeName{\tikzumlAssemblyConnectorStart@name}}\x% + % + \begingroup% + \def\_{_}\edef\x{\endgroup% + \def\noexpand\tikzumlAssemblyConnectorEnd@nodeName{\tikzumlAssemblyConnectorEnd@name}}\x% + % + \begingroup% + \def\_{_}\edef\x{\endgroup% + \def\noexpand\tikzumlAssemblyConnectorLabel@nodeName{\tikzumlAssemblyConnectorLabel}}\x% + % + \pgfextractx{\tikzuml@AC@xa}{\pgfpointanchor{\tikzumlAssemblyConnectorStart@nodeName}{center}}% + \pgfextracty{\tikzuml@AC@ya}{\pgfpointanchor{\tikzumlAssemblyConnectorStart@nodeName}{center}}% + \pgfextractx{\tikzuml@AC@xb}{\pgfpointanchor{\tikzumlAssemblyConnectorEnd@nodeName}{center}}% + \pgfextracty{\tikzuml@AC@yb}{\pgfpointanchor{\tikzumlAssemblyConnectorEnd@nodeName}{center}}% + % + \ifthenelse{\equal{\tikzumlAssemblyConnectorGeometry}{--}}{% + \ifthenelse{\tikzuml@AC@xb>\tikzuml@AC@xa}{% + \def\tikzumlAssemblyConnectorStartAnchor{east}% + \def\tikzumlAssemblyConnectorEndAnchor{west}% + }{% + \def\tikzumlAssemblyConnectorStartAnchor{west}% + \def\tikzumlAssemblyConnectorEndAnchor{east}% + } + }{}% + % + \ifthenelse{\equal{\tikzumlAssemblyConnectorGeometry}{-|}}{% + \ifthenelse{\tikzuml@AC@xb>\tikzuml@AC@xa}{% + \def\tikzumlAssemblyConnectorStartAnchor{east}% + }{% + \def\tikzumlAssemblyConnectorStartAnchor{west}% + } + \ifthenelse{\tikzuml@AC@yb>\tikzuml@AC@ya}{% + \def\tikzumlAssemblyConnectorEndAnchor{south}% + }{% + \def\tikzumlAssemblyConnectorEndAnchor{north}% + } + }{}% + % + \ifthenelse{\equal{\tikzumlAssemblyConnectorGeometry}{|-}}{% + \ifthenelse{\tikzuml@AC@xb>\tikzuml@AC@xa}{% + \def\tikzumlAssemblyConnectorEndAnchor{west}% + }{% + \def\tikzumlAssemblyConnectorEndAnchor{east}% + } + \ifthenelse{\tikzuml@AC@yb>\tikzuml@AC@ya}{% + \def\tikzumlAssemblyConnectorStartAnchor{north}% + }{% + \def\tikzumlAssemblyConnectorStartAnchor{south}% + } + }{}% + % + \ifthenelse{\equal{\tikzumlAssemblyConnectorGeometry}{-|-}}{% + \ifthenelse{\equal{\tikzumlAssemblyConnectorStartArm}{auto}}{% + \ifthenelse{\equal{\tikzumlAssemblyConnectorEndArm}{auto}}{% + \pgfmathsetlength{\tikzuml@AC@middleArm}{0.5 * \tikzuml@AC@xa + 0.5 * \tikzuml@AC@xb}% + }{% + \pgfmathsetlength{\tikzuml@AC@middleArm}{\tikzuml@AC@xb+\tikzumlAssemblyConnectorEndArm}% + }% + }{% + \pgfmathsetlength{\tikzuml@AC@middleArm}{\tikzuml@AC@xa+\tikzumlAssemblyConnectorStartArm}% + }% + \pgfmathparse{\tikzuml@AC@middleArm>\tikzuml@AC@xa} + \pgfmathparse{\tikzuml@AC@middleArm>\tikzuml@AC@xb} + \ifthenelse{\lengthtest{\tikzuml@AC@middleArm>\tikzuml@AC@xa}}{% + \def\tikzumlAssemblyConnectorStartAnchor{east}% + }{% + \def\tikzumlAssemblyConnectorStartAnchor{west}% + } + \ifthenelse{\lengthtest{\tikzuml@AC@middleArm>\tikzuml@AC@xb}}{% + \def\tikzumlAssemblyConnectorEndAnchor{east}% + }{% + \def\tikzumlAssemblyConnectorEndAnchor{west}% + } + }{}% + % + \ifthenelse{\equal{\tikzumlAssemblyConnectorGeometry}{|-|}}{% + \ifthenelse{\equal{\tikzumlAssemblyConnectorStartArm}{auto}}{% + \ifthenelse{\equal{\tikzumlAssemblyConnectorEndArm}{auto}}{% + \pgfmathsetlength{\tikzuml@AC@middleArm}{0.5 * \tikzuml@AC@ya + 0.5 * \tikzuml@AC@yb}% + }{% + \pgfmathsetlength{\tikzuml@AC@middleArm}{\tikzuml@AC@yb+\tikzumlAssemblyConnectorEndArm}% + }% + }{% + \pgfmathsetlength{\tikzuml@AC@middleArm}{\tikzuml@AC@ya+\tikzumlAssemblyConnectorStartArm}% + }% + \ifthenelse{\tikzuml@AC@middleArm>\tikzuml@AC@ya}{% + \def\tikzumlAssemblyConnectorStartAnchor{north}% + }{% + \def\tikzumlAssemblyConnectorStartAnchor{south}% + } + \ifthenelse{\tikzuml@AC@middleArm>\tikzuml@AC@yb}{% + \def\tikzumlAssemblyConnectorEndAnchor{north}% + }{% + \def\tikzumlAssemblyConnectorEndAnchor{south}% + } + }{}% + % + \ifthenelse{\equal{\tikzumlAssemblyConnectorStartAnchorTmp}{}}{% + }{% + \def\tikzumlAssemblyConnectorStartAnchor{\tikzumlAssemblyConnectorStartAnchorTmp}% + }% + \ifthenelse{\equal{\tikzumlAssemblyConnectorEndAnchorTmp}{}}{% + }{% + \def\tikzumlAssemblyConnectorEndAnchor{\tikzumlAssemblyConnectorEndAnchorTmp}% + }% + % + \node[inner sep=0] (\tikzumlAssemblyConnectorStart@nodeName-\tikzumlAssemblyConnectorLabel@nodeName-port-tmp) at (\tikzumlAssemblyConnectorStart@nodeName.\tikzumlAssemblyConnectorStartAnchor) {};% + \node[inner sep=0] (\tikzumlAssemblyConnectorEnd@nodeName-\tikzumlAssemblyConnectorLabel@nodeName-port-tmp) at (\tikzumlAssemblyConnectorEnd@nodeName.\tikzumlAssemblyConnectorEndAnchor) {};% + % + \umlrelation[style={tikzuml connector style}, #1]{\tikzumlAssemblyConnectorStart@nodeName-\tikzumlAssemblyConnectorLabel@nodeName-port-tmp}{\tikzumlAssemblyConnectorEnd@nodeName-\tikzumlAssemblyConnectorLabel@nodeName-port-tmp}% + % + \ifthenelse{\equal{\tikzumlAssemblyConnectorWithPort}{tikzumlTrue}}{% + \node[inner sep=0.5*\tikzumlAssemblyConnectorWidth, rectangle, draw=\tikzumlAssemblyConnectorDrawColor, fill=\tikzumlAssemblyConnectorPortFillColor] (\tikzumlAssemblyConnectorStart@nodeName-\tikzumlAssemblyConnectorLabel@nodeName-port) at (\tikzumlAssemblyConnectorStart@nodeName-\tikzumlAssemblyConnectorLabel@nodeName-port-tmp) {};% + \node[inner sep=0.5*\tikzumlAssemblyConnectorWidth, rectangle, draw=\tikzumlAssemblyConnectorDrawColor, fill=\tikzumlAssemblyConnectorPortFillColor] (\tikzumlAssemblyConnectorEnd@nodeName-\tikzumlAssemblyConnectorLabel@nodeName-port) at (\tikzumlAssemblyConnectorEnd@nodeName-\tikzumlAssemblyConnectorLabel@nodeName-port-tmp) {};% + }{% + \node[inner sep=0] (\tikzumlAssemblyConnectorStart@nodeName-\tikzumlAssemblyConnectorLabel@nodeName-port) at (\tikzumlAssemblyConnectorStart@nodeName.\tikzumlAssemblyConnectorStartAnchor) {};% + \node[inner sep=0] (\tikzumlAssemblyConnectorEnd@nodeName-\tikzumlAssemblyConnectorLabel@nodeName-port) at (\tikzumlAssemblyConnectorEnd@nodeName.\tikzumlAssemblyConnectorEndAnchor) {};% + }% + % + \addtocounter{tikzumlRelationNum}{-1}% + \ifthenelse{\equal{\tikzumlAssemblyConnectorName}{connector-\thetikzumlConnectorNum}}{% + \edef\tikzumlAssemblyConnectorName{relation-\thetikzumlRelationNum}% + \edef\tikzumlAssemblyConnectorSymbolName{\tikzumlAssemblyConnectorLabel@nodeName}% + }{% + \edef\tikzumlAssemblyConnectorSymbolName{\tikzumlAssemblyConnectorName}% + }% + % + \stepcounter{tikzumlRelationNum}% + % + \ifthenelse{\equal{\tikzumlAssemblyConnectorGeometry}{--}}{% + \umlassemblyconnectorsymbol{\tikzumlAssemblyConnectorStart@nodeName}{\tikzumlAssemblyConnectorEnd@nodeName}% + }{% + \ifthenelse{\equal{\tikzumlAssemblyConnectorGeometry}{-|}}{% + \ifthenelse{\equal{\tikzumlAssemblyConnectorFirstArm}{tikzumlTrue}}{% + \umlassemblyconnectorsymbol{\tikzumlAssemblyConnectorStart@nodeName}{\tikzumlAssemblyConnectorName-2}% + }{% + \umlassemblyconnectorsymbol{\tikzumlAssemblyConnectorName-2}{\tikzumlAssemblyConnectorEnd@nodeName}% + }% + }{% + \ifthenelse{\equal{\tikzumlAssemblyConnectorGeometry}{|-}}{% + \ifthenelse{\equal{\tikzumlAssemblyConnectorFirstArm}{tikzumlTrue}}{% + \umlassemblyconnectorsymbol{\tikzumlAssemblyConnectorStart@nodeName}{\tikzumlAssemblyConnectorName-2}% + }{% + \umlassemblyconnectorsymbol{\tikzumlAssemblyConnectorName-2}{\tikzumlAssemblyConnectorEnd@nodeName}% + }% + }{% + \ifthenelse{\equal{\tikzumlAssemblyConnectorGeometry}{-|-}}{% + \ifthenelse{\equal{\tikzumlAssemblyConnectorFirstArm}{tikzumlTrue}}{% + \umlassemblyconnectorsymbol{\tikzumlAssemblyConnectorStart@nodeName}{\tikzumlAssemblyConnectorName-2}% + }{% + \ifthenelse{\equal{\tikzumlAssemblyConnectorLastArm}{tikzumlTrue}}{% + \umlassemblyconnectorsymbol{\tikzumlAssemblyConnectorName-4}{\tikzumlAssemblyConnectorEnd@nodeName}% + }{% + \umlassemblyconnectorsymbol{\tikzumlAssemblyConnectorName-2}{\tikzumlAssemblyConnectorName-4}% + }% + }% + }{% + \ifthenelse{\equal{\tikzumlAssemblyConnectorGeometry}{|-|}}{% + \ifthenelse{\equal{\tikzumlAssemblyConnectorFirstArm}{tikzumlTrue}}{% + \umlassemblyconnectorsymbol{\tikzumlAssemblyConnectorStart@nodeName}{\tikzumlAssemblyConnectorName-2}% + }{% + \ifthenelse{\equal{\tikzumlAssemblyConnectorLastArm}{tikzumlTrue}}{% + \umlassemblyconnectorsymbol{\tikzumlAssemblyConnectorName-4}{\tikzumlAssemblyConnectorEnd@nodeName}% + }{% + \umlassemblyconnectorsymbol{\tikzumlAssemblyConnectorName-2}{\tikzumlAssemblyConnectorName-4}% + }% + }% + }{}% + }% + }% + }% + }% + \stepcounter{tikzumlConnectorNum}% +}% +% +% shortcuts of \umlassemblyconnector +\newcommand{\umlHVassemblyconnector}[3][]{% + \pgfkeys{/tikzuml/HVassemblyconnector/.cd,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{geometry}}{% + \errmessage{TIKZUML ERROR : in umlHVassemblyconnector, forbidden option geometry}% + }{}% + }}% + \pgfkeys{/tikzuml/HVassemblyconnector/.cd, #1}% + \umlassemblyconnector[geometry=-|, #1]{#2}{#3}% +}% +% +\newcommand{\umlVHassemblyconnector}[3][]{% + \pgfkeys{/tikzuml/VHassemblyconnector/.cd,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{geometry}}{% + \errmessage{TIKZUML ERROR : in umlVHassemblyconnector, forbidden option geometry}% + }{}% + }% + }% + \pgfkeys{/tikzuml/VHassemblyconnector/.cd, #1}% + \umlassemblyconnector[geometry=|-, #1]{#2}{#3}% +}% +% +\newcommand{\umlHVHassemblyconnector}[3][]{% + \pgfkeys{/tikzuml/HVHassemblyconnector/.cd,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{geometry}}{% + \errmessage{TIKZUML ERROR : in umlHVHassemblyconnector, forbidden option geometry}% + }{}% + }}% + \pgfkeys{/tikzuml/HVHassemblyconnector/.cd, #1}% + \umlassemblyconnector[geometry=-|-, #1]{#2}{#3}% +}% +% +\newcommand{\umlVHVassemblyconnector}[3][]{% + \pgfkeys{/tikzuml/VHVassemblyconnector/.cd,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{geometry}}{% + \errmessage{TIKZUML ERROR : in umlVHVassemblyconnector, forbidden option geometry}% + }{}% + }}% + \pgfkeys{/tikzuml/VHVassemblyconnector/.cd, #1}% + \umlassemblyconnector[geometry=|-|, #1]{#2}{#3}% +}% +% +\newcommand{\umlport}[3][]{% + \pgfkeys{/tikzuml/port/.cd, draw/.initial=\tikzumlDefaultDrawColor, fill/.initial=\tikzumlPortDefaultFillColor,% + width/.initial=\tikzumlPortDefaultWidth,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \errmessage{TIKZUML ERROR : in umlport forbidden option \keyname}% + }% + }% + \pgfkeys{/tikzuml/port/.cd, #1}% + \pgfkeys{/tikzuml/port/.cd, width/.get=\tikzumlPortWidth,% + draw/.get=\tikzumlPortDrawColor, fill/.get=\tikzumlPortFillColor}% + \edef\tikzumlPort@name{#2}% + \edef\tikzumlPort@anchor{#3}% + \begingroup% + \def\_{_}\edef\x{\endgroup% + \def\noexpand\tikzumlPort@nodeName{\tikzumlPort@name}}\x% + % + \node[inner sep=0.5*\tikzumlPortWidth, rectangle, draw=\tikzumlPortDrawColor, fill=\tikzumlPortFillColor] (\tikzumlPort@nodeName-\tikzumlPort@anchor-port) at (\tikzumlPort@nodeName.\tikzumlPort@anchor) {};% +}% +% +\newcommand{\umldelegateconnector}[3][]{% + \def\tikzumlDelegateConnectorWithStartPort{tikzumlFalse}% + \def\tikzumlDelegateConnectorWithEndPort{tikzumlFalse}% + \pgfkeys{/tikzuml/delegateconnector/.cd, + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{stereo}}{% + \errmessage{TIKZUML ERROR : in umldelegateconnector, forbidden option stereo}% + }{}% + }}% + \pgfkeys{/tikzuml/delegateconnector/.cd, #1}% + \umlrelation[style={tikzuml connector style}, stereo=delegate, #1]{#2}{#3}% +}% +% +% shortcuts of \umldelegateconnector +\newcommand{\umlHVdelegateconnector}[3][]{% + \pgfkeys{/tikzuml/HVdelegateconnector/.cd,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{geometry}\OR\equal{\keyname}{stereo}}{% + \errmessage{TIKZUML ERROR : in umlHVdelegateconnector, forbidden option \keyname}% + }{}% + }}% + \pgfkeys{/tikzuml/HVdelegateconnector/.cd, #1}% + \umldelegateconnector[geometry=-|, #1]{#2}{#3}% +}% +% +\newcommand{\umlVHdelegateconnector}[3][]{% + \pgfkeys{/tikzuml/VHdelegateconnector/.cd,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{geometry}\OR\equal{\keyname}{stereo}}{% + \errmessage{TIKZUML ERROR : in umlVHdelegateconnector, forbidden option \keyname}% + }{}% + }}% + \pgfkeys{/tikzuml/VHdelegateconnector/.cd, #1}% + \umldelegateconnector[geometry=|-, #1]{#2}{#3}% +}% +% +\newcommand{\umlHVHdelegateconnector}[3][]{% + \pgfkeys{/tikzuml/HVHdelegateconnector/.cd,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{geometry}\OR\equal{\keyname}{stereo}}{% + \errmessage{TIKZUML ERROR : in umlHVHdelegateconnector, forbidden option \keyname}% + }{}% + }}% + \pgfkeys{/tikzuml/HVHdelegateconnector/.cd, #1}% + \umldelegateconnector[geometry=-|-, #1]{#2}{#3}% +}% +% +\newcommand{\umlVHVdelegateconnector}[3][]{% + \pgfkeys{/tikzuml/VHVdelegateconnector/.cd,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{geometry}\OR\equal{\keyname}{stereo}}{% + \errmessage{TIKZUML ERROR : in umlVHVdelegateconnector, forbidden option \keyname}% + }{}% + }}% + \pgfkeys{/tikzuml/VHVdelegateconnector/.cd, #1}% + \umldelegateconnector[geometry=|-|, #1]{#2}{#3}% +}% +%%% End of tikz-uml.sty +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \ No newline at end of file diff --git a/10-entwurfsheft/titlepage.tex b/10-entwurfsheft/titlepage.tex new file mode 100644 index 0000000..cc1d67b --- /dev/null +++ b/10-entwurfsheft/titlepage.tex @@ -0,0 +1,75 @@ +%% Encoding: UTF-8 %% + +%% titlepage.tex + +\def\usesf{} +\let\usesf\sffamily % diese Zeile auskommentieren für normalen TeX Font + +\begin{titlepage} + +\setlength{\unitlength}{1pt} +\begin{picture}(00,0)(70,770) + \includegraphics[width=\paperwidth]{assets/KIT_Deckblatt.pdf} +\end{picture} + +\thispagestyle{empty} + +\begin{center} +\hbox{} +\vfill +\includegraphics[width=.5\textwidth]{assets/logo.pdf} +\vskip 1cm +{\usesf + {\huge\bfseries PSE\textsuperscript{2} - Podcast Synchronisation \\ + made Efficient\\ + Entwurfsheft \par} +\vskip 1.8cm +{\Large Wintersemester 2022/2023\\} +%von\\[2mm] +\vskip 1.5cm + +% {\large\bfseries Vorname Nachname\\} +% \vskip 1.2cm +Praxis der Softwareentwicklung \\ +Prof. Dr.-Ing. Gregor Snelting \\ +Fakultät für Informatik\\ +Karlsruher Institut für Technologie\\ +\vskip 1.5cm +\begin{tabular}{p{20mm}l} +Autoren: +& Daniel Hönlinger \\ +& Gero Beckmann \\ +& Immanuel Reitz \\ +& Julius Friesen \\ +& Lukas Schmidheissler \\ +\\ +Betreuer: & M.Sc. Hans-Peter Lehmann \\ + & M.Sc. Daniel Seemaier +\end{tabular} +} +\end{center} +\vfill + +%\begin{textblock}{10}[0,0](4,15) +% \includegraphics[width=.3\textwidth]{logos/logo.pdf} +%\end{textblock} + +% \begin{textblock}{8}[0,0](14,14) +% \includegraphics[width=.3\textwidth]{logos/KASTEL_logo.pdf} +% \end{textblock} + +\end{titlepage} + +% \thispagestyle{empty} +% \ \vfill +% \begin{flushleft} +% Copyright $\copyright$ ITI und Verfasser 201?\\ +% \ \\ +% Institut für Theoretische Informatik +% Fakultät für Informatik\\ +% Karlsruher Institut für Technologie\\ +% Am Fasanengarten 5\\ +% 76131 Karlsruhe +% \end{flushleft} +% \newpage + diff --git a/11-entwurfsheft-kolloquium/.gitignore b/11-entwurfsheft-kolloquium/.gitignore new file mode 100644 index 0000000..87ec682 --- /dev/null +++ b/11-entwurfsheft-kolloquium/.gitignore @@ -0,0 +1,302 @@ +## Core latex/pdflatex auxiliary files: +*.aux +*.lof +*.log +*.lot +*.fls +*.out +*.toc +*.fmt +*.fot +*.cb +*.cb2 +.*.lb + +## Intermediate documents: +*.dvi +*.xdv +*-converted-to.* +# these rules might exclude image files for figures etc. +# *.ps +# *.eps +*.pdf +!assets/*.pdf + +## Generated if empty string is given at "Please type another file name for output:" +.pdf + +## Bibliography auxiliary files (bibtex/biblatex/biber): +*.bbl +*.bcf +*.blg +*-blx.aux +*-blx.bib +*.run.xml + +## Build tool auxiliary files: +*.fdb_latexmk +*.synctex +*.synctex(busy) +*.synctex.gz +*.synctex.gz(busy) +*.pdfsync + +## Build tool directories for auxiliary files +# latexrun +latex.out/ + +## Auxiliary and intermediate files from other packages: +# algorithms +*.alg +*.loa + +# achemso +acs-*.bib + +# amsthm +*.thm + +# beamer +*.nav +*.pre +*.snm +*.vrb + +# changes +*.soc + +# comment +*.cut + +# cprotect +*.cpt + +# elsarticle (documentclass of Elsevier journals) +*.spl + +# endnotes +*.ent + +# fixme +*.lox + +# feynmf/feynmp +*.mf +*.mp +*.t[1-9] +*.t[1-9][0-9] +*.tfm + +#(r)(e)ledmac/(r)(e)ledpar +*.end +*.?end +*.[1-9] +*.[1-9][0-9] +*.[1-9][0-9][0-9] +*.[1-9]R +*.[1-9][0-9]R +*.[1-9][0-9][0-9]R +*.eledsec[1-9] +*.eledsec[1-9]R +*.eledsec[1-9][0-9] +*.eledsec[1-9][0-9]R +*.eledsec[1-9][0-9][0-9] +*.eledsec[1-9][0-9][0-9]R + +# glossaries +*.acn +*.acr +*.glg +*.glo +*.gls +*.glsdefs +*.lzo +*.lzs +*.slg +*.slo +*.sls + +# uncomment this for glossaries-extra (will ignore makeindex's style files!) +*.ist + +# gnuplot +*.gnuplot +*.table + +# gnuplottex +*-gnuplottex-* + +# gregoriotex +*.gaux +*.glog +*.gtex + +# htlatex +*.4ct +*.4tc +*.idv +*.lg +*.trc +*.xref + +# hyperref +*.brf + +# knitr +*-concordance.tex +# TODO Uncomment the next line if you use knitr and want to ignore its generated tikz files +# *.tikz +*-tikzDictionary + +# listings +*.lol + +# luatexja-ruby +*.ltjruby + +# makeidx +*.idx +*.ilg +*.ind + +# minitoc +*.maf +*.mlf +*.mlt +*.mtc[0-9]* +*.slf[0-9]* +*.slt[0-9]* +*.stc[0-9]* + +# minted +_minted* +*.pyg + +# morewrites +*.mw + +# newpax +*.newpax + +# nomencl +*.nlg +*.nlo +*.nls + +# pax +*.pax + +# pdfpcnotes +*.pdfpc + +# sagetex +*.sagetex.sage +*.sagetex.py +*.sagetex.scmd + +# scrwfile +*.wrt + +# svg +svg-inkscape/ + +# sympy +*.sout +*.sympy +sympy-plots-for-*.tex/ + +# pdfcomment +*.upa +*.upb + +# pythontex +*.pytxcode +pythontex-files-*/ + +# tcolorbox +*.listing + +# thmtools +*.loe + +# TikZ & PGF +*.dpth +*.md5 +*.auxlock + +# titletoc +*.ptc + +# todonotes +*.tdo + +# vhistory +*.hst +*.ver + +# easy-todo +*.lod + +# xcolor +*.xcp + +# xmpincl +*.xmpi + +# xindy +*.xdy + +# xypic precompiled matrices and outlines +*.xyc +*.xyd + +# endfloat +*.ttt +*.fff + +# Latexian +TSWLatexianTemp* + +## Editors: +# WinEdt +*.bak +*.sav + +# Texpad +.texpadtmp + +# LyX +*.lyx~ + +# Kile +*.backup + +# gummi +.*.swp + +# KBibTeX +*~[0-9]* + +# TeXnicCenter +*.tps + +# auto folder when using emacs and auctex +./auto/* +*.el + +# expex forward references with \gathertags +*-tags.tex + +# standalone packages +*.sta + +# Makeindex log files +*.lpz + +# xwatermark package +*.xwm + +# REVTeX puts footnotes in the bibliography by default, unless the nofootinbib +# option is specified. Footnotes are the stored in a file with suffix Notes.bib. +# Uncomment the next line to have this generated file ignored. +#*Notes.bib diff --git a/11-entwurfsheft-kolloquium/.gitlab-ci.yml b/11-entwurfsheft-kolloquium/.gitlab-ci.yml new file mode 100644 index 0000000..27d0617 --- /dev/null +++ b/11-entwurfsheft-kolloquium/.gitlab-ci.yml @@ -0,0 +1,36 @@ +plantuml: + stage: .pre + image: + name: plantuml/plantuml + entrypoint: [""] + script: + - java -jar plantuml.jar -tpdf assets/diagrams/*.puml + artifacts: + paths: + - assets + +tex: + stage: build + image: texlive/texlive + script: + - mkdir public + - make tex + - mv *.pdf public + artifacts: + paths: + - public + dependencies: + - plantuml + +pages: + stage: deploy + script: + - echo Hello, World! + artifacts: + paths: + - public + rules: + - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH + dependencies: + - tex + diff --git a/11-entwurfsheft-kolloquium/CHANGELOG.md b/11-entwurfsheft-kolloquium/CHANGELOG.md new file mode 100644 index 0000000..7671a41 --- /dev/null +++ b/11-entwurfsheft-kolloquium/CHANGELOG.md @@ -0,0 +1,34 @@ +# Changelog +Alle Änderungen an diesem Projekt werden in dieser Datei dokumentiert. +Die Versionsnummern folgt der Syntax in `sdqbeamer.cls`. + +## [2022-05-03 v3.1.3] +- Die Breite des Gruppennamens in der Fußzeile kann nun über `\groupnamewidth{}` gesteuert werden +- FIX: zweizeilige Fußzeilen haben nun gleichmäßigen vertikalen Abstand (Issue #16 in Gitlab) + +## [2021-08-10 v3.1.2] +- FIX: framesubtitle wird nun angezeigt (Issue #6 in Gitlab) + +## [2020-12-08 v3.1.1] +- FIX: Titelbild (Issue #4 in Gitlab) + +## [2020-12-07 v3.1] +- Umgebung ``contentblock`` (farbloser Block mit fetter Überschrift) hinzugefügt +- Farbboxen (``greenblock``, ``blueblock``, …) hinzugefügt +- Abstufungen der KIT-Farben in 10er-Schritten entsprechend der Gestaltungsrichtlinien eingeführt +- FIX: Navigationspunkte für Subsections in eine Zeile gesetzt, um vertikal Platz zu sparen +- FIX: ``inputenc`` an den Anfang von ``sdqbeamer.cls`` verschoben + +## [2020-11-16 v3.0] +- Seitenformat 16:10 hinzugefügt +- Umstellung auf KIT-Design vom 1. August 2020 + - Anpassung auf neues Farbschema und Maße + - neues Titelbild aus der KIT-Bildwelt +- Neue Optionen: + - durch `smallfoot` und `bigfoot` kann die Schriftgröße der Fußzeile gesteuert werden + - durch `navbarkit` kann eine Fußzeile nach KIT-Vorgaben erzwungen werden +- Deutsch (`de`) ist nun die Standard-Option +- Ordner `templates` wurde gelöscht und die Inhalte in `sdqbeamer.cls` integriert +- Globale Größe auf 10 pt verringert (vorher: 11 pt), da der beschreibbare Bereich im Vergleich zur 2009er Version kleiner geworden ist +- SDQ-spezifische Logos und Titelbilder entfernt. Diese sind ab sofort im Branch »sdq« verfügbar. +- Fix: Zeilenumbruch bei Titel in der Fußzeile repariert \ No newline at end of file diff --git a/11-entwurfsheft-kolloquium/Makefile b/11-entwurfsheft-kolloquium/Makefile new file mode 100644 index 0000000..67f5384 --- /dev/null +++ b/11-entwurfsheft-kolloquium/Makefile @@ -0,0 +1,18 @@ +MAIN = presentation +FLAGS = -pdf + +all: clean compile +compile: diagram tex +clean: clean-diagram clean-tex + +dev: + latexmk $(FLAGS) -pvc $(MAIN) +tex: + latexmk $(FLAGS) $(MAIN) +diagram: + java -jar plantuml.jar -tpdf assets/diagrams/*.puml +clean-tex: + latexmk -C +clean-diagram: + find assets/diagrams -type f -not -name '*.puml' -delete + diff --git a/11-entwurfsheft-kolloquium/README.md b/11-entwurfsheft-kolloquium/README.md new file mode 100644 index 0000000..6a5e9ea --- /dev/null +++ b/11-entwurfsheft-kolloquium/README.md @@ -0,0 +1,118 @@ +LaTeX-Vorlage für Präsentationen +================================ + +Das vorliegende Paket dient als Vorlage für Präsentationen im [Corporate Design des KIT](https://intranet.kit.edu/gestaltungsrichtlinien.php) (Fassung vom 1. August 2020). + +Es wird an der Forschungsgruppe [DSiS](https://dsis.kastel.kit.edu) an der KIT-Fakultät für Informatik entwickelt und basiert auf [LaTeX Beamer](https://ctan.org/pkg/beamer). + +Autor: [Dr.-Ing. Erik Burger](https://dsis.kastel.kit.edu/staff_erik_burger.php) +mit Beiträgen von Christian Hammer, Klaus Krogmann und Maximilian Schambach + +Siehe https://sdqweb.ipd.kit.edu/wiki/Dokumentvorlagen + +Hinweise, Verbesserungsvorschläge +================================= + +Bitte verwenden Sie das [Issue-Tracking-System von Gitlab](https://git.scc.kit.edu/i43/dokumentvorlagen/praesentationen/beamer/-/issues), um auf Probleme mit der Vorlage hinzuweisen oder Erweiterungswünsche zu äußern. Sie können gerne auch eine Änderung per Merge-Request vorschlagen. + +Verwendung +========== + +Optionen der Dokumentklasse `sdqbeamer` +----------------------------------------- +Durch die folgenden Optionen kann das Seitenverhältnis der Folien bestimmt werden: + +| Seitenverhältnis | Option | +| ---------------- | ------------------- | +| 16:9 | `16:9` (Standard) | +| 16:10 | `16:10` | +| 4:3 | `4:3` | + +Die Schriftgröße in der Fußzeile ist standardmäßig größer gewählt, als in den Gestaltungsrichtlinien vorgegeben. Diese Vorgabe kann durch die Option `smallfoot` erzwungen werden. + +| Schriftgröße Fußzeile | Option | +| ----------------------| -------------------- | +| etwas größer (12pt) | `bigfoot` (Standard) | +| KIT-Vorgabe (9pt) | `smallfoot` | + +Die Plazierung der Navigationsleiste kann durch folgende Optionen beeinflußt werden: + +| Position | Option | Bemerkung | +| ------------------------ | ---------------- | ------------------------------------------ | +| oberhalb der Trennlinie | `navbarinline` | Standard | +| unterhalb der Trennlinie | `navbarinfooter` | keine Subsection-Punkte, Größe `smallfoot` | +| Seitenleiste links | `navbarside` | keine Subsection-Punkte | +| keine Navigationsleiste | `navbaroff` | | +| KIT-Vorgabe | `navbarkit` | entspricht `navbaroff` und `smallfoot` | + +Als Sprache sind Deutsch und Englisch verfügbar. Durch die Sprachwahl werden automatisch die passenden Logos und Formate (z.B. Datum) gewählt. + +| Sprache | | +| -------- |---------------- | +| Deutsch | `de` (Standard) | +| Englisch | `en` | + +Beispiel: `\documentclass[de,16:9,navbarinline]{sdqbeamer}` + +Titelbild +--------- + +Das Bild auf der Titelfolie kann mit dem Befehl + +`\titleimage{myimage}` (ohne Dateiendung) + +gesetzt werden. Um ein eigenes Bild zu verwenden, bitte die Datei (z.B. `myimage.jpg`) ins `logos/`-Verzeichnis legen und den Befehl anpassen. Mitgeliefert wird ein generisches Bild aus der KIT-Bildwelt (https://intranet.kit.edu/gestaltungsrichtlinien.php) in der Datei `logos/banner_2020_kit.jpg`. Falls kein Titelbild eingefügt werden soll, bitte `\titleimage{}` setzen. + +Für 16:9-Folien sollte das Verhältnis des Bildes 160:37 betragen, für 4:3-Folien 63:20. Es können auch breitere Bilder verwendet werden, da das Titelbild auf die Höhe des Rahmens skaliert und zentriert wird. + +Logo und Name Abteilung/KIT-Fakultät/Institut +--------------------------------------------- + +Das Logo rechts oben auf der Titelfolie kann mit dem folgenden Befehl gesetzt werden: + +`\grouplogo{mylogo}` (ohne Dateiendung) + +Um ein eigenes Logo zu verwenden, bitte die Datei (z.B. `mylogo.pdf`) in das Verzeichnis `logos/` legen und den Befehl anpassen. Falls kein Logo eingefügt werden soll, bitte `\grouplogo{}` setzen. + +Der Gruppenname kann mit folgendem Befehl gesetzt werden: + +`\groupname{Software Design and Quality}` + +Der Gruppenname erscheint in der Fußzeile rechts unten. Lange Namen werden in zwei Zeilen umgebrochen. Falls der Gruppenname leer gelassen wird (`\groupname{}`), wird die volle Breite der Fußzeile für Autornamen und Titel verwendet. + +Die Standardbreite des Gruppennamens sind 50 mm. Sie kann mit + +`\groupnamewidth{80mm}` + +verändert werden, wodurch sich auch die Breite des Textfeldes mit Autor und Titel entsprechend ändert. Umbrüche sind mit `\\` möglich. Statt zweizeiliger Fußzeilen empfiehlt sich eventuell die Option `smallfoot`. + +LaTeX allgemein +--------------- +Siehe https://sdqweb.ipd.kit.edu/wiki/LaTeX + +Dateistruktur +============ +`presentation.tex` +------------------ +Hauptdatei des LaTeX-Dokuments. + +`presentation.bib` +------------- +Beispieldatei für BibTeX-Referenzen +https://sdqweb.ipd.kit.edu/wiki/BibTeX-Literaturlisten + +`sdqbeamer.cls` +----------------- +Dokumentklasse für Präsentationen im KIT-Design. + +`logos/` +-------- +In diesem Verzeichnis befinden das KIT-Logo als PDF sowie das Hintergrundbild der Titelfolie als JPG. + +`CHANGELOG.md` +-------------- +Dokumentation der Änderungen in den jeweiligen Versionen. + +`README.md` +----------- +Dieser Text. diff --git a/11-entwurfsheft-kolloquium/assets/.gitignore b/11-entwurfsheft-kolloquium/assets/.gitignore new file mode 100644 index 0000000..16252a4 --- /dev/null +++ b/11-entwurfsheft-kolloquium/assets/.gitignore @@ -0,0 +1,3 @@ +diagrams/* +!diagrams/*.puml + diff --git a/11-entwurfsheft-kolloquium/assets/.gitingnore b/11-entwurfsheft-kolloquium/assets/.gitingnore new file mode 100644 index 0000000..16252a4 --- /dev/null +++ b/11-entwurfsheft-kolloquium/assets/.gitingnore @@ -0,0 +1,3 @@ +diagrams/* +!diagrams/*.puml + diff --git a/11-entwurfsheft-kolloquium/assets/KIT_Deckblatt.pdf b/11-entwurfsheft-kolloquium/assets/KIT_Deckblatt.pdf new file mode 100644 index 0000000..7de8ed4 Binary files /dev/null and b/11-entwurfsheft-kolloquium/assets/KIT_Deckblatt.pdf differ diff --git a/11-entwurfsheft-kolloquium/assets/diagrams/backendComponentDiagram.puml b/11-entwurfsheft-kolloquium/assets/diagrams/backendComponentDiagram.puml new file mode 100644 index 0000000..806522c --- /dev/null +++ b/11-entwurfsheft-kolloquium/assets/diagrams/backendComponentDiagram.puml @@ -0,0 +1,61 @@ +@startuml +' skinparam linetype ortho + +'######################################################################### +'SubscriptionsAPI +component SubscriptionsAPI { + + component SubscriptionService + component SubscriptionController + component SubscriptionDataAccessLayer + + portout "Webserver" as wSub + portin "Database" as dSub + } + +dSub --0)- SubscriptionDataAccessLayer +SubscriptionDataAccessLayer --0)- SubscriptionService +SubscriptionService --0)- SubscriptionController +SubscriptionController --0)- wSub + +'######################################################################### + + +'######################################################################### +'EpisodeActionsAPI + +component EpisodeActionsAPI { + component EpisodeActionService + component EpisodeActionController + component EpisodeActionDataAccessLayer + + portout "Webserver" as wEpisode + portin "Database" as dEpisode +} + +dEpisode --0)- EpisodeActionDataAccessLayer +EpisodeActionController --0)- wEpisode +EpisodeActionDataAccessLayer --0)- EpisodeActionService +EpisodeActionService --0)- EpisodeActionController + +'######################################################################### + + +'######################################################################### +'AuthenticationAPI + +component AuthenticationAPI { + component AuthenticationService + component AuthenticationController + component AuthenticationDataAccessLayer + + portout "Webserver" as wAuth + portin "Database" as dAuth +} + +dAuth --0)- AuthenticationDataAccessLayer +AuthenticationController --0)- wAuth +AuthenticationDataAccessLayer --0)- AuthenticationService +AuthenticationService --0)- AuthenticationController + +@enduml diff --git a/11-entwurfsheft-kolloquium/assets/diagrams/classdiagram-authentication.puml b/11-entwurfsheft-kolloquium/assets/diagrams/classdiagram-authentication.puml new file mode 100644 index 0000000..a2b3518 --- /dev/null +++ b/11-entwurfsheft-kolloquium/assets/diagrams/classdiagram-authentication.puml @@ -0,0 +1,112 @@ +@startuml + +package authenticationAPI <> { + package authenticationDataAccessLayer <> { + ' interface AuthenticationDao { + ' String login(String username) + ' int logout(String username) + ' } + + ' class AuthenticationDataAccessService <<@Respository>> { + ' <> AuthenticationDataAccessService(JpaTemplate jpaTemplate) + ' String login(String username) + ' int logout(String username) + ' } + + interface UserDetailsManager { + void createUser(UserDetails userDetails) + void changePassword(String oldPassword, String newPassword) + void deleteUser(String username) + void updateUser(UserDetails user) + boolean userExists(String username) + } + note left + Aus org.springframework.security.provisioning + - liefert Methoden zum Erstellen neuer User + und zum Aktualisieren bestehender. + end note + + class JdbcUserDetailsManager <<@Repository>> { + <> JdbcUserDetailsManager(DataSource dataSource) + void createUser(UserDetails user) + void changePassword(String oldPassword, String newPassword) + void deleteUser(String username) + void updateUser(UserDetails user) + boolean userExists(String username) + } + note right + User Management Service aus dem Paket + org.springframework.security.provisioning + der CRUD Operationen für User bereitstellt. + Hier sind nur die relevanten Methoden modelliert. + end note + } + + package authenticationService <> { + class AuthenticationService <<@Service>> { + -- + <> AuthenticationService(UserDetailsManager userDetailsManager) + List verifyLogin(String username) + int logout(String username) + int forgotPassword(ForgotPasswordRequest forgotPasswordRequest) + .. via JdbcUserDetailsManager .. + int resetPassword(String username, RequestWithPassword requestWithPassword) + int registerUser(UserDetails user) + int changePassword(String username, ChangePasswordRequest changePasswordRequest) + int deleteUser(String username, RequestWithPassword requestWithPassword) + } + + class JavaMailSenderImpl {} + note left + Aus org.springframework.mail.javamail. + Implementierung des JavaMailSender Interfaces, + welches das MailSender Interface durch Unterstützung + von MIME Nachrichten erweitert. + Das MailSender Interface definiert dabei eine + Strategie zum Versenden einfacher Mails. + Unterstützt sowohl JavaMail MimeMessages und + Spring SimpleMailMessages. + end note + } + + package authenticationController <> { + class AuthenticationController <<@Controller>> { + <> AuthenticationController(AuthenticationService authenticationService) + ResponseEntity> verifyLogin(String username) + ResponseEntity logout(String username) + ResponseEntity forgotPassword(ForgotPasswordRequest forgotPasswordRequest) + ResponseEntity resetPassword(String username, RequestWithPassword requestWithPassword) + ResponseEntity registerUser(UserDetails user) + ResponseEntity changePassword(String username, ChangePasswordRequest changePasswordRequest) + ResponseEntity deleteUser(String username, RequestWithPassword requestWithPassword) + } + + class ChangePasswordRequest { + <> ChangePasswordRequest(String oldPassword, String newPassword) + String getOldPassword() + String getNewPassword() + } + + class ForgotPasswordRequest { + <> ForgotPasswordRequest(String email) + String getEmail() + } + + class RequestWithPassword { + <> ResetPasswordRequest(String password) + String getPassword() + } + } +} + +' User <.. AuthenticationDataAccessService: DB +' User <.. JdbcUserDetailsManager: DB +UserDetailsManager <.. AuthenticationService: <> +' AuthenticationDao <.. AuthenticationService: <> +AuthenticationService --o AuthenticationController +' AuthenticationDao <|. AuthenticationDataAccessService: <> +UserDetailsManager <|. JdbcUserDetailsManager: <> +JavaMailSenderImpl <. AuthenticationService: <> + +@enduml + diff --git a/11-entwurfsheft-kolloquium/assets/diagrams/classdiagram-episode-actions.puml b/11-entwurfsheft-kolloquium/assets/diagrams/classdiagram-episode-actions.puml new file mode 100644 index 0000000..7a4530e --- /dev/null +++ b/11-entwurfsheft-kolloquium/assets/diagrams/classdiagram-episode-actions.puml @@ -0,0 +1,84 @@ +@startuml + +package episodeActionsAPI <> { + package episodeActionDataAccessLayer <> { + class EpisodeActionDataAccessService <<@Repository>> { + <> EpisodeActionDataAccessService (JpaTemplate jpaTemplate) + long addEpisodeActions(String username, List episodeActionPosts) + List getEpisodeActions(String username) + List getEpisodeActionsOfPodcast(String username, String podcastURL) + List getEpisodeActionsSince(String username, LocalDateTime since) + List getEpisodeActionsOfPodcastSince(String username, String podcastURL, LocalDateTime since) + } + + interface EpisodeActionDao { + long addEpisodeActions(String username, List episodeActionPosts) + List getEpisodeActions(String username) + List getEpisodeActionsOfPodcast(String username, String podcastURL) + List getEpisodeActionsSince(String username, LocalDateTime since) + List getEpisodeActionsOfPodcastSince(String username, String podcastURL, LocalDateTime since) + } + } + + package episodeActionService <> { + class EpisodeActionService <<@Service>> { + <> EpisodeActionService (EpisodeActionDao episodeActionDao) + LocalDateTime addEpisodeActions(String username, List episodeActionPosts) + List getEpisodeActions(String username) + List getEpisodeActionsOfPodcast(String username, String podcastURL) + List getEpisodeActionsSince(String username, LocalDateTime since) + List getEpisodeActionsOfPodcastSince(String username, String podcastURL, LocalDateTime since) + } + } + + package episodeActionController <> { + class EpisodeActionController <<@Controller>>{ + <> EpisodeActionController (EpisodeActionService episodeActionService) + ResponseEntity addEpisodeActions(String username, EpisodeActionPostRequest episodeActionPostRequest) + ResponseEntity getEpisodeActions(String username, String deviceID, boolean aggregated) + ResponseEntity getEpisodeActionsOfPodcast(String username, String podcastURL, String deviceID, boolean aggregated) + ResponseEntity getEpisodeActionsSince(String username, String deviceID, long since, boolean aggregated) + ResponseEntity getEpisodeActionsOfPodcastSince(String username, String podcastURL, String deviceID, long since, boolean aggregated) + } + + class EpisodeActionPostResponse { + <> EpisodeActionPostResponse(List> updateURLs) + long getTimestamp() + List> getUpdatedURLs() + } + + class EpisodeActionPost { + <> EpisodeActionPost(String podcastURL, String episodeURL, Action action, LocalDateTime timestamp, int started, int position) + String getPodcastURL() + String getEpisodeURL() + int getGUID() + Action getAction() + LocalDateTime getTimestamp() + int getStarted() + int getPosition() + EpisodeAction getEpisodeAction() + } + + class EpisodeActionPostRequest { + <> EpisodeActionPostRequest(List episodeActionPosts) + List getEpisodeActionPosts() + } + + class EpisodeActionGetResponse { + <> EpisodeActionGetResponse(List episodeActionPosts) + List getEpisodeActionPosts() + long getTimestamp() + } + } +} + +EpisodeActionPost -o EpisodeActionGetResponse +EpisodeActionPost -o EpisodeActionPostRequest +' EpisodeAction <.. EpisodeActionDataAccessService: DB +' Episode <.. EpisodeActionDataAccessService: DB +EpisodeActionDao <.. EpisodeActionService: <> +EpisodeActionService --o EpisodeActionController +EpisodeActionDao <|. EpisodeActionDataAccessService: <> + +@enduml + diff --git a/11-entwurfsheft-kolloquium/assets/diagrams/classdiagram-model.puml b/11-entwurfsheft-kolloquium/assets/diagrams/classdiagram-model.puml new file mode 100644 index 0000000..72ad49f --- /dev/null +++ b/11-entwurfsheft-kolloquium/assets/diagrams/classdiagram-model.puml @@ -0,0 +1,109 @@ +@startuml + +package model <> { + class Subscription { + <> Subscription(String url, String title) + int getID() + String getURL() + long getLastActionTimestamp() + String getTitle() + } + + class SubscriptionAction { + <> SubscriptionAction(int userID, int subscriptionID) + int getID() + int getUserID() + int getSubscriptionID() + long getTimestamp() + boolean getAdded() + } + + class Episode { + <> Episode(int subscriptionID, int id, String url, String title, String thumbnailURL, int total) + int getSubscriptionID() + int getID() + int getGUID() + String getURL() + String getTitle() + int getTotal() + } + + enum Action { + Download + Play + Delete + New + Flattr + String getJsonProperty() + } + + class EpisodeAction { + <> EpisodeAction(Action action, LocalDateTime timestamp, int started, int position) + int getEpisodeID() + Action getAction() + long getTimestamp() + int getStarted() + int getPosition() + void setEpisodeID() + EpisodeActionPost getEpisodeActionPost(String podcastURL, String episodeURL) + } + + interface UserDetails { + String getUsername() + String getPassword() + Collection getAuthorities() + boolean isAccountExpired() + boolean isAccountLocked() + boolean isCredentialsNonExpired() + boolean isEnabled() + } + note left + Aus org.springframework.security.core.userdetails. + Wird für die Schnittstelle UserDetailsManager benötigt. + Stellt wichtige Informationen eines Users bereit. + Diese werden nur indirekt von Spring Security + benutzt, indem sie vorher in Authentication Objekten + gekapselt werden. + end note + + class User { + -- + <> User(String username, String password) + int getID() + String getSessionToken() + boolean getEmailIsValidated() + .. interface methods .. + String getUsername() + String getPassword() + Collection getAuthorities() + boolean isAccountExpired() + boolean isAccountLocked() + boolean isCredentialsNonExpired() + boolean isEnabled() + } + + interface GrantedAuthority { + String getAuthority() + } + note right + Aus org.springframework.security.core. + Wird für die Schnittstelle UserDetails benötigt. + Repräsentiert eine Autorisierung, die einem + Authentication Objekt gewährt wird. + end note + + class Authority { + <> Authority() + String getAuthority() + } +} + +Subscription <. SubscriptionAction: ID +Action <-- EpisodeAction +EpisodeAction .> Episode: ID +UserDetails <|.. User: <> +User -> Authority +GrantedAuthority <|.. Authority: <> + +@enduml + diff --git a/11-entwurfsheft-kolloquium/assets/diagrams/classdiagram-subscriptions.puml b/11-entwurfsheft-kolloquium/assets/diagrams/classdiagram-subscriptions.puml new file mode 100644 index 0000000..432f185 --- /dev/null +++ b/11-entwurfsheft-kolloquium/assets/diagrams/classdiagram-subscriptions.puml @@ -0,0 +1,75 @@ +@startuml + +package subscriptionsAPI <> { + package subscriptionDataAccessLayer <> { + class SubscriptionDataAccessService <<@Repository>> { + <> SubscriptionDataAccessService(JpaTemplate jpaTemplate) + int uploadSubscriptions(String username, List subscriptions) + List getSubscriptions(String username) + List getSubscriptionsSince(String username, LocalDateTime time) + int addSubscriptions(String username, List addedSubscriptions) + int removeSubscriptions(String username, List removedSubscriptions) + List getTitles(String username) + } + + interface SubscriptionDao { + int uploadSubscriptions(String username, List subscriptions) + List getSubscriptions(String username) + List getSubscriptionsSince(String username, LocalDateTime time) + int addSubscriptions(String username, List addedSubscriptions) + int removeSubscriptions(String username, List removedSubscriptions) + List getTitles(String username) + } + } + + package subscriptionService <> { + class SubscriptionService <<@Service>> { + <> SubscriptionService(SubscriptionDao subscriptionDao) + int uploadSubscriptions(String username, List subscriptions) + List getSubscriptions(String username) + List getSubscriptionsSince(String username, LocalDateTime time) + int addSubscriptions(String username, List addedSubscriptions) + int removeSubscriptions(String username, List removedSubscriptions) + List getTitles(String username) + } + } + + package subscriptionController <> { + class SubscriptionController <<@Controller>>{ + ' @Autowired + <> SubscriptionController(SubscriptionService subscriptionService) + ' @GetMapping + ResponseEntity> getSubscriptions(String username, String deviceID, String functionJSONP) + ' @PutMapping + ResponseEntity uploadSubscriptions(String username, String deviceID, List subscriptions) + ' @PostMapping + ResponseEntity applySubscriptionDelta(String username, String deviceID, SubscriptionDelta delta) + ' @GetMapping + ResponseEntity getSubscriptionDelta(String username, String deviceID, long since) + ResponseEntity> getTitles(String username, String deviceID) + } + + class SubscriptionTitles { + <> SubscriptionTitles(Subscription subscription, List episodeTitles) + Subscription getSubscription() + List getEpisodesTitles() + } + + class SubscriptionDelta { + <> SubscriptionDelta(List add, List remove) + List getRemove() + LocalDate getTimestamp() + List> getUpdate_urls() + } + } + +} + +' Subscription <.. SubscriptionDataAccessService: DB +' SubscriptionAction <.. SubscriptionDataAccessService: DB +SubscriptionService --o SubscriptionController +SubscriptionDao <.. SubscriptionService: <> +SubscriptionDao <|. SubscriptionDataAccessService: <> + +@enduml + diff --git a/11-entwurfsheft-kolloquium/assets/diagrams/classdiagram-util.puml b/11-entwurfsheft-kolloquium/assets/diagrams/classdiagram-util.puml new file mode 100644 index 0000000..03dfc9a --- /dev/null +++ b/11-entwurfsheft-kolloquium/assets/diagrams/classdiagram-util.puml @@ -0,0 +1,43 @@ +@startuml + +package util <> { + class RSSParser { + <> RSSParser(String subscriptionURL) + String getSubscriptionTitle() + List getEpisodes() + Episode getEpisodeForURL(String episodeURL) + } + note bottom + Verwendet intern Spring um + HTTP-Anfragen zu erstellen. + end note + + class CleanCronJob { + <> CleanCronJob(JdbcUserDetailsManager jdbcUserDetailsManager) + void cleanInvalidUsers() + } + note bottom + Hintergrundservice, der in periodischen Abständen + Nutzer, die ihre E-Mail-Adresse nicht nach 24 Stunden + bestätigt haben, wieder aus der Datenbank löscht. + (Auf die Assoziation zu JdbcUserDetailsManager wird + im Sinne der Übersichtlichkeit verzichtet.) + end note + + class ResponseEntity { + <> ResponseEntity(T body, HttpStatusCode status) + T getBody() + HttpStatusCode getStatusCode() + } + note bottom + Aus org.springframework.http. + Erweitert die Klasse HttpEntity, welche + ein HTTP Anfrage- oder Antwort-Objekt + repräsentiert, durch einen HttpStatusCode. + Wird von den Controller-Methoden als + Rückgabewert verwendet. + end note +} + +@enduml + diff --git a/11-entwurfsheft-kolloquium/assets/diagrams/classdiagram.puml b/11-entwurfsheft-kolloquium/assets/diagrams/classdiagram.puml new file mode 100644 index 0000000..f833aa2 --- /dev/null +++ b/11-entwurfsheft-kolloquium/assets/diagrams/classdiagram.puml @@ -0,0 +1,68 @@ +@startuml +' skinparam linetype ortho +' skinparam groupInheritance 2 +allowmixing + +!include classdiagram-authentication.puml +!include classdiagram-episode-actions.puml +!include classdiagram-model.puml +!include classdiagram-subscriptions.puml +!include classdiagram-util.puml + +class SecurityConfigurationBasicAuth { + <> SecurityConfigurationBasicAuth() + PasswordEncoder encoder() + UserDetailsManager userDetailsService() + SecuryFilterChain fiterChain(HTTPSecurity http) throws Excpetion +} +note top + Erstellt einen Servlet Filter (springSecurityFilterChain) + der für die gesamte Sicherheit zuständig ist (Schutz der URLs, + Validierung von Anmeldedaten, Weiterleitung zur Anmeldung, etc.). +end note + +class PSEApplication { + <> PSEApplication() + void main(String[] args) +} + +database Datenbank +Datenbank <-[hidden]d- subscriptionsAPI +Datenbank <-[hidden]d- episodeActionsAPI +Datenbank <-[hidden]d- authenticationAPI +() SQL as SQLSub +() SQL as SQLAuth +() SQL as SQLEpisode + +Datenbank -- SQLSub +Datenbank -- SQLAuth +Datenbank -- SQLEpisode + +Subscription --o SubscriptionTitles +EpisodeActionPost -o SubscriptionTitles +UserDetailsManager <.. SecurityConfigurationBasicAuth: <> + +SubscriptionController ..o PSEApplication +AuthenticationController ..o PSEApplication +EpisodeActionController ..o PSEApplication +SecurityConfigurationBasicAuth ..o PSEApplication + +PSEApplication --() HTTP + +SQLSub )-- SubscriptionDataAccessService: JPA +' SQLAuth )-- AuthenticationDataAccessService: JPA +SQLAuth )-- JdbcUserDetailsManager: JDBC +SQLEpisode )-- EpisodeActionDataAccessService: JPA + +RSSParser <. SubscriptionDataAccessService: <> +RSSParser <. EpisodeActionDataAccessService: <> +' JdbcUserDetailsManager <-- CleanCronJob + +model .o Datenbank: ORM (User, SubscriptionAction, Subscription, EpisodeAction, Episode) +' Datenbank o.. Subscription: ORM +' Datenbank o.. SubscriptionAction: ORM +' Datenbank o.. Episode: ORM +' Datenbank o.. EpisodeAction: ORM +' Datenbank o.. User: ORM + +@enduml diff --git a/11-entwurfsheft-kolloquium/assets/diagrams/componentdiagram.puml b/11-entwurfsheft-kolloquium/assets/diagrams/componentdiagram.puml new file mode 100644 index 0000000..dea4a1d --- /dev/null +++ b/11-entwurfsheft-kolloquium/assets/diagrams/componentdiagram.puml @@ -0,0 +1,53 @@ +@startuml + +[App] as app +[VueRouter] as router +[NavbarComponent] as navbar +[LoginPage] as login_page +[SubscriptionsPage] as abo_page +[EpisodesPage] as episodes_page +[SettingsPage] as settings_page +[ForgotPasswordPage] as forgot_page +[ResetPasswordPage] as reset_page +note top + Wird in der E-Mail zum Zurücksetzen des Passworts mit dem JWT-Token verlinkt. + Sendet das alte und neue Passwort und den JWT an die API. +end note +[RegistrationPage] as registration_page + +[SubscriptionComponent] as sub +[EpisodeComponent] as episode +[LastUpdateComponent] as last_update +[PasswordValidatorComponent] as password + +app --> router + +app --> navbar +router --> login_page +router --> forgot_page +router --> reset_page +router --> registration_page +router --> abo_page +router --> episodes_page +router --> settings_page + +navbar -[hidden] router + +episodes_page -[hidden] abo_page +login_page -[hidden] forgot_page +registration_page -[hidden] reset_page +abo_page -[hidden] settings_page +forgot_page -[hidden] episodes_page +' forgot_page -[hidden] settings_page + +abo_page --> sub +episodes_page --> episode + +sub --> last_update +episode --> last_update + +settings_page --> password +reset_page --> password +registration_page --> password + +@enduml diff --git a/11-entwurfsheft-kolloquium/assets/diagrams/db.puml b/11-entwurfsheft-kolloquium/assets/diagrams/db.puml new file mode 100644 index 0000000..bdefaea --- /dev/null +++ b/11-entwurfsheft-kolloquium/assets/diagrams/db.puml @@ -0,0 +1,78 @@ +@startuml +' Type Symbol +' Zero or One |o-- +' Exactly One ||-- +' Zero or Many }o-- +' One or Many }|-- + +skinparam linetype ortho + +entity User { + * int id <> + * String email + * String password + * boolean verified + * long created_at +} + +entity SubscriptionAction { + * int id <> + * int user_id + * long timestamp + * int subscription_id + * boolean added +} + +entity Subscription { + * int id <> + * String url + * long timestamp + * String title +} + +entity Episode { + * int id <> + * int guid <> + * String url + * String title + * int total + * int subscription_id +} +note right + Wenn der Client eine GUID aus dem Feed mitsendet, wird + diese statt der URL verwendet um die Episode zu finden. + So wird die Episode auch noch gefunden, nachdem sich + die URL geändert hat. +end note +note bottom of Episode + Wenn für die Episoden-URL einer EpisodeAction noch keine Episode in der Datenbank steht, + dann schreibe dafür ein Dummy-Objekt in Datenbank und lade asynchron die Episoden der Subscription. + Ersetze dann die Dummy-Objekte durch die Episoden und setze den Timestamp der Subscription auf + die aktuelle Zeit. + Um DoS-Angriffe auf den Backend-Server abzuwenden, können die Episoden einer Subscription erst + nach einer Stunde erneut gefetched werden. Bis dahin werden für EpisodeActions, die sich auf noch + nicht geladene Episoden beziehen, nur Dummy-Objekte für die Episoden in die Datenbank geschrieben. + Es sei noch darauf hingewiesen, dass diese Dummy-Episoden bei Anfragen nicht mit ausgegeben werden. +end note + +entity EpisodeAction { + * int id <> + * int user_id + * int episode_id + * long timestamp + * int action + * int started + * int position +} +note right + Speichere für jede Episode + nur letzte Play-Action. +endnote + +User ||--o{ EpisodeAction +User ||--o{ SubscriptionAction +SubscriptionAction }|--|| Subscription +EpisodeAction }|--|| Episode +Subscription ||-right-|{ Episode + +@enduml diff --git a/11-entwurfsheft-kolloquium/assets/diagrams/deployment.puml b/11-entwurfsheft-kolloquium/assets/diagrams/deployment.puml new file mode 100644 index 0000000..26918e2 --- /dev/null +++ b/11-entwurfsheft-kolloquium/assets/diagrams/deployment.puml @@ -0,0 +1,59 @@ +@startuml + +node "<> \nBackend Server" as backendServer{ + database " <> \n MariaDB Server 10.6" as database { + rectangle rectangle1 [ + <> + User + ] + rectangle rectangle2 [ + <> + SubscriptionAction + ] + rectangle rectangle3 [ + <> + EpisodeAction + ] + rectangle rectangle4 [ + <> + Subscription + ] + rectangle rectangle5 [ + <> + Episode + ] + } + + node "<> \nJava Spring" as javaSpring{ + node " <> \n Tomcat Webserver" + } +} + +node "<> \nFrontend" as frontendServer { + +} + +node "<> \nEndgerät" as terminal { + node "<> \nBrowser" as browser + node "<> \nPodcatcher" as podcatcher +} + +backendServer "1" - "*" podcatcher + +node "<> \nFrontend Server" as frontendServer{ + node "<> \nVue.js" as vuejs { + + } +} + +podcatcher -[hidden] browser + +backendServer - "1" frontendServer + +database "1" -- "1" javaSpring + +browser "*" -- frontendServer + + + +@enduml diff --git a/11-entwurfsheft-kolloquium/assets/diagrams/sequencediagram-forgotAndResetPW.puml b/11-entwurfsheft-kolloquium/assets/diagrams/sequencediagram-forgotAndResetPW.puml new file mode 100644 index 0000000..603130c --- /dev/null +++ b/11-entwurfsheft-kolloquium/assets/diagrams/sequencediagram-forgotAndResetPW.puml @@ -0,0 +1,41 @@ +@startuml + +skinparam ParticipantPadding 30 + +participant AuthenticationController << (C, #ADD1B2) @Controller >> +-> AuthenticationController: ""POST /api/2/auth/forgot.json"" \n//@RequestBody ForgotPasswordRequest forgotPasswordRequest// \n\n-> forgotPassword(//forgotPasswordRequest//) +activate AuthenticationController +participant AuthenticationService << (C, #ADD1B2) @Service >> +AuthenticationController -> AuthenticationService: forgotPassword(//forgotPasswordRequest//) +activate AuthenticationService +participant JavaMailSenderImpl << (C, #ADD1B2) >> +AuthenticationService -> JavaMailSenderImpl: create link to reset password with JWT as URL parameter \n-> send(SimpleMailMessage simpleMessage) with link +activate JavaMailSenderImpl +<<- JavaMailSenderImpl: sends email with link containing a JWT to reset password +JavaMailSenderImpl --> AuthenticationService +deactivate JavaMailSenderImpl +AuthenticationService --> AuthenticationController: int indicating status +deactivate AuthenticationService +<-- AuthenticationController: ResponseEntity indicating status \n\n-> ""HTTP status code"" +deactivate AuthenticationController +||60|| +-> AuthenticationController: ""PUT /api/2/auth/{username}/resetpassword.json"" \n//@RequestParam String jwt// \n//@RequestBody ResetPasswordRequest resetPasswordRequest// \n\n-> login user (""username"") via JWT (//jwt//) \n-> resetPassword(""username"", //resetPasswordRequest//) +activate AuthenticationController +AuthenticationController -> AuthenticationService: resetPassword(""username"", //resetPasswordRequest//) +activate AuthenticationService +participant JdbcUserDetailsManager << (C, #ADD1B2) @Repository >> +AuthenticationService -> JdbcUserDetailsManager: String oldPassword = //resetPasswordRequest//.getOldPassword() \nString newPassword = //resetPasswordRequest//.getNewPassword() \n-> changePassword(newPassword, oldPassword) +activate JdbcUserDetailsManager +database Database +JdbcUserDetailsManager -> Database: change password of logged in user +activate Database +Database --> JdbcUserDetailsManager +deactivate Database +JdbcUserDetailsManager --> AuthenticationService: int indicating status +deactivate JdbcUserDetailsManager +AuthenticationService --> AuthenticationController: int indicating status +deactivate AuthenticationService +<-- AuthenticationController: ResponseEntity indicating status \n\n-> ""HTTP status code"" +deactivate AuthenticationController + +@enduml \ No newline at end of file diff --git a/11-entwurfsheft-kolloquium/assets/diagrams/sequencediagram-getEpisodeActions.puml b/11-entwurfsheft-kolloquium/assets/diagrams/sequencediagram-getEpisodeActions.puml new file mode 100644 index 0000000..47497d5 --- /dev/null +++ b/11-entwurfsheft-kolloquium/assets/diagrams/sequencediagram-getEpisodeActions.puml @@ -0,0 +1,38 @@ +@startuml + +' title =**Get All Episode Actions** + +participant EpisodeActionController << (C, #ADD1B2) @Controller >> +-> EpisodeActionController: ""GET /api/2/episodes/{username}.json"" \n//@RequestParam("device") String deviceID// \n//@RequestParam("aggregated") boolean aggregated// \n\n-> getEpisodeActions(""username"", //deviceID//, //aggregated//) +note right + Die Parameter //deviceID// und //aggregated// werden ignoriert, + da nicht zwischen Geräten unterschieden und für jede + Episode sowieso nur die letzte Play-Action gespeichert + wird. Dies gilt für alle GET-Anfragen der Episode Actions API. +end note +activate EpisodeActionController +participant EpisodeActionService << (C, #ADD1B2) @Service >> +EpisodeActionController -> EpisodeActionService: getEpisodeActions(""username"") +activate EpisodeActionService +participant EpisodeActionDataAccessService << (C, #ADD1B2) @Repository >> +EpisodeActionService -> EpisodeActionDataAccessService: getEpisodeActions(""username"") +activate EpisodeActionDataAccessService +EpisodeActionDataAccessService -> EpisodeActionDataAccessService: getEpisodeActionsSince(""username"", \nLocalDateTime.MIN.toEpochSecond(ZoneOffset.UTC)) +database Database +activate EpisodeActionDataAccessService +EpisodeActionDataAccessService -> Database: get all EpisodeActions for all subscribed podcasts +activate Database +Database --> EpisodeActionDataAccessService: List selectedEpisodeActions \n-> then remove all older than LocalDateTime.MIN (none) +EpisodeActionDataAccessService -> Database: join EpisodeActions in selectedEpisodeActions with episodeURL of Episode +Database --> EpisodeActionDataAccessService +deactivate Database +EpisodeActionDataAccessService --> EpisodeActionDataAccessService: List episodeActionPosts +deactivate EpisodeActionDataAccessService +EpisodeActionDataAccessService --> EpisodeActionService: List episodeActionPosts +deactivate EpisodeActionDataAccessService +EpisodeActionService --> EpisodeActionController: List episodeActionPosts +deactivate EpisodeActionService +<-- EpisodeActionController: ResponseEntity response \n\n-> ""HTTP status code"" \n-> ""JSON"" +deactivate EpisodeActionController + +@enduml \ No newline at end of file diff --git a/11-entwurfsheft-kolloquium/assets/diagrams/sequencediagram-getEpisodeActionsOfPodcastSince.puml b/11-entwurfsheft-kolloquium/assets/diagrams/sequencediagram-getEpisodeActionsOfPodcastSince.puml new file mode 100644 index 0000000..d8797d1 --- /dev/null +++ b/11-entwurfsheft-kolloquium/assets/diagrams/sequencediagram-getEpisodeActionsOfPodcastSince.puml @@ -0,0 +1,32 @@ +@startuml + +' title =**Get Episode Actions of Podcast Since** + +participant EpisodeActionController << (C, #ADD1B2) @Controller >> +-> EpisodeActionController: ""GET /api/2/episodes/{username}.json"" \n//@RequestParam("podcast") String podcastURL// \n//@RequestParam("device") String deviceID// \n//@RequestParam("since") long since// \n//@RequestParam("aggregated") boolean aggregated// \n\n-> getEpisodeActionsOfPodcastSince(""username"", //podcastURL//, //deviceID//, //since//, //aggregated//) +note right + Die Parameter //deviceID// und //aggregated// werden ignoriert. + Siehe Notiz in Sequenzdiagramm **Get All Episode Actions**. +end note +activate EpisodeActionController +participant EpisodeActionService << (C, #ADD1B2) @Service >> +EpisodeActionController -> EpisodeActionService: getEpisodeActionsOfPodcastSince(""username"", //podcastURL//, //since//) +activate EpisodeActionService +participant EpisodeActionDataAccessService << (C, #ADD1B2) @Repository >> +EpisodeActionService -> EpisodeActionDataAccessService: getEpisodeActionsOfPodcastSince(""username"", //podcastURL//, //since//) +activate EpisodeActionDataAccessService +database Database +EpisodeActionDataAccessService -> Database: get all EpisodeActions the given podcast (//podcastURL//) +activate Database +Database --> EpisodeActionDataAccessService: List selectedEpisodeActions \n-> then remove all older than //since// +EpisodeActionDataAccessService -> Database: join EpisodeActions in selectedEpisodeActions with episodeURL of Episode +Database --> EpisodeActionDataAccessService +deactivate Database +EpisodeActionDataAccessService --> EpisodeActionService: List episodeActionPosts +deactivate EpisodeActionDataAccessService +EpisodeActionService --> EpisodeActionController: List episodeActionPosts +deactivate EpisodeActionService +<-- EpisodeActionController: ResponseEntity response \n\n-> ""HTTP status code"" \n-> ""JSON"" +deactivate EpisodeActionController + +@enduml \ No newline at end of file diff --git a/11-entwurfsheft-kolloquium/assets/diagrams/sequencediagram-getSubscriptions.puml b/11-entwurfsheft-kolloquium/assets/diagrams/sequencediagram-getSubscriptions.puml new file mode 100644 index 0000000..6e6895f --- /dev/null +++ b/11-entwurfsheft-kolloquium/assets/diagrams/sequencediagram-getSubscriptions.puml @@ -0,0 +1,36 @@ +@startuml + +' title =**Get All Subscriptions** + +participant SubscriptionController << (C, #ADD1B2) @Controller >> +-> SubscriptionController: ""GET /subscriptions/{username}.json"" \n"" /subscriptions/{username}/{deviceid}.json"" \n//@RequestParam("jsonp") String functionJSONP// \n\n-> getSubscriptions(""username"", ""deviceid"", //functionJSONP//) +activate SubscriptionController +note right + Die Parameter ""deviceid"" und + //functionJSONP// werden ignoriert, + da nicht zwischen Geräten unterschieden + und JSONP nicht unterstützt wird. +end note +participant SubscriptionService << (C, #ADD1B2) @Service >> +SubscriptionController -> SubscriptionService: getSubscriptions(""username"") +activate SubscriptionService +participant SubscriptionDataAccessService << (C, #ADD1B2) @Repository >> +SubscriptionService -> SubscriptionDataAccessService: getSubscriptions(""username"") +activate SubscriptionDataAccessService +SubscriptionDataAccessService -> SubscriptionDataAccessService: getSubscriptionsSince(""username"", LocalDateTime.MIN) +database Database +activate SubscriptionDataAccessService +SubscriptionDataAccessService -> Database: get all Podcasts from Subscriptions for ""username"" +activate Database +Database --> SubscriptionDataAccessService: List subscribedPodcasts +deactivate Database +SubscriptionDataAccessService --> SubscriptionDataAccessService: List podcastURLs +deactivate SubscriptionDataAccessService +SubscriptionDataAccessService --> SubscriptionService: List podcastURLs +deactivate SubscriptionDataAccessService +SubscriptionService --> SubscriptionController: List podcastURLs +deactivate SubscriptionService +<-- SubscriptionController: ResponseEntity> podcastURLs \n \n-> ""HTTP status code"" \n-> ""JSON"" +deactivate SubscriptionController + +@enduml \ No newline at end of file diff --git a/11-entwurfsheft-kolloquium/assets/diagrams/sequencediagram-register.puml b/11-entwurfsheft-kolloquium/assets/diagrams/sequencediagram-register.puml new file mode 100644 index 0000000..b7b7aa1 --- /dev/null +++ b/11-entwurfsheft-kolloquium/assets/diagrams/sequencediagram-register.puml @@ -0,0 +1,26 @@ +@startuml + +' title =**Register** + +participant AuthenticationController << (C, #ADD1B2) @Controller >> +-> AuthenticationController: ""POST /api/2/auth/register.json"" \n//@RequestBody UserDetails user// \n\n-> registerUser(//user//) +activate AuthenticationController +participant AuthenticationService << (C, #ADD1B2) @Service >> +AuthenticationController -> AuthenticationService: registerUser(//user//) +activate AuthenticationService +participant JdbcUserDetailsManager << (C, #ADD1B2) @Repository >> +AuthenticationService -> JdbcUserDetailsManager: createUser(//user//) +activate JdbcUserDetailsManager +database Database +JdbcUserDetailsManager -> Database: create new User with given UserDetails (//user//) +activate Database +Database --> JdbcUserDetailsManager +deactivate Database +JdbcUserDetailsManager --> AuthenticationService: int indicating status +deactivate JdbcUserDetailsManager +AuthenticationService --> AuthenticationController: int indicating status +deactivate AuthenticationService +<-- AuthenticationController: ResponseEntity indicating status \n\n-> ""HTTP status code"" +deactivate AuthenticationController + +@enduml \ No newline at end of file diff --git a/11-entwurfsheft-kolloquium/assets/diagrams/sequencediagram-uploadEpisodeActions.puml b/11-entwurfsheft-kolloquium/assets/diagrams/sequencediagram-uploadEpisodeActions.puml new file mode 100644 index 0000000..d3dac57 --- /dev/null +++ b/11-entwurfsheft-kolloquium/assets/diagrams/sequencediagram-uploadEpisodeActions.puml @@ -0,0 +1,38 @@ +@startuml + +' title =**Upload Episode Actions** + +participant EpisodeActionController << (C, #ADD1B2) @Controller >> +-> EpisodeActionController: ""POST /api/2/episodes/{username}.json"" \n//@RequestBody EpisodeActionPostRequest episodeActionPostRequest// \n\n-> addEpisodeActions(""username"", //episodeActionPostRequest//) +activate EpisodeActionController +participant EpisodeActionService << (C, #ADD1B2) @Service >> +EpisodeActionController -> EpisodeActionService: addEpisodeActions(""username"", \nepisodeActionPosts = //episodeActionPostRequest//.getEpisodeActionPosts()) +activate EpisodeActionService +participant EpisodeActionDataAccessService << (C, #ADD1B2) @Repository >> +EpisodeActionService -> EpisodeActionDataAccessService: addEpisodeActions(""username"", episodeActionPosts) +database Database +activate EpisodeActionDataAccessService +loop for each EpisodeActionPost in episodeActionPosts -> episodeAction = episodeActionPost.getEpisodeAction() +opt episodeAction.getAction().equals(Action.PLAY) +EpisodeActionDataAccessService -> Database: set episodeID field of episodeAction for this ""username"" via podcastURL and episodeURL +activate Database +Database --> EpisodeActionDataAccessService +EpisodeActionDataAccessService -> Database: get last EpisodeAction with this episodeID if present +Database --> EpisodeActionDataAccessService: Optional lastEpisodeAction +opt lastEpisodeAction.isPresent() +EpisodeActionDataAccessService -> Database: replace lastEpisodeAction with episodeAction +else else +EpisodeActionDataAccessService -> Database: add episodeAction to DB as new entry +end +Database --> EpisodeActionDataAccessService +deactivate Database +end +end +EpisodeActionDataAccessService --> EpisodeActionService: long latestTimestamp +deactivate EpisodeActionDataAccessService +EpisodeActionService --> EpisodeActionController: LocalDateTime timestamp = LocalDateTime.ofEpochSecond(latestTimestamp, 0, ZoneOffset.UTC) +deactivate EpisodeActionService +<-- EpisodeActionController: ResponseEntity \n(with empty list for updateURLs) \n\n-> ""HTTP status code"" \n-> ""JSON"" +deactivate EpisodeActionController + +@enduml \ No newline at end of file diff --git a/11-entwurfsheft-kolloquium/assets/diagrams/sequencediagram-uploadSubscriptions.puml b/11-entwurfsheft-kolloquium/assets/diagrams/sequencediagram-uploadSubscriptions.puml new file mode 100644 index 0000000..1edc8cf --- /dev/null +++ b/11-entwurfsheft-kolloquium/assets/diagrams/sequencediagram-uploadSubscriptions.puml @@ -0,0 +1,32 @@ +@startuml + +' title =**Upload Subscriptions** + +participant SubscriptionController << (C, #ADD1B2) @Controller >> +-> SubscriptionController: ""PUT /subscriptions/{username}/{deviceid}.json"" \n//@RequestBody List subscriptions// \n\n-> uploadSubscriptions(""username"", ""deviceid"", //subscriptions//) +activate SubscriptionController +participant SubscriptionService << (C, #ADD1B2) @Service >> +SubscriptionController -> SubscriptionService: uploadSubscriptions(""username"", //subscriptions//) +activate SubscriptionService +participant SubscriptionDataAccessService << (C, #ADD1B2) @Repository >> +SubscriptionService -> SubscriptionDataAccessService: uploadSubscriptions(""username"", //subscriptions//) +activate SubscriptionDataAccessService +database Database +SubscriptionDataAccessService -> Database: delete all subsciptions of ""username"" +activate Database +Database --> SubscriptionDataAccessService +SubscriptionDataAccessService -> SubscriptionDataAccessService: addSubscriptions(""username"", //subscriptions//) +activate SubscriptionDataAccessService +SubscriptionDataAccessService -> Database: upload all subscriptions (//subscriptions//) for ""username"" +Database --> SubscriptionDataAccessService +deactivate Database +SubscriptionDataAccessService --> SubscriptionDataAccessService: int indicating status +deactivate SubscriptionDataAccessService +SubscriptionDataAccessService --> SubscriptionService: int indicating status +deactivate SubscriptionDataAccessService +SubscriptionService --> SubscriptionController: int indicating status +deactivate SubscriptionService +<-- SubscriptionController: ResponseEntity with empty String for success \n\n-> ""HTTP status code"" \n-> ""JSON"" +deactivate SubscriptionController + +@enduml \ No newline at end of file diff --git a/11-entwurfsheft-kolloquium/assets/episode.png b/11-entwurfsheft-kolloquium/assets/episode.png new file mode 100644 index 0000000..c0db4a2 Binary files /dev/null and b/11-entwurfsheft-kolloquium/assets/episode.png differ diff --git a/11-entwurfsheft-kolloquium/assets/help.png b/11-entwurfsheft-kolloquium/assets/help.png new file mode 100644 index 0000000..39a1b84 Binary files /dev/null and b/11-entwurfsheft-kolloquium/assets/help.png differ diff --git a/11-entwurfsheft-kolloquium/assets/lastupdate.png b/11-entwurfsheft-kolloquium/assets/lastupdate.png new file mode 100644 index 0000000..e9b7f5c Binary files /dev/null and b/11-entwurfsheft-kolloquium/assets/lastupdate.png differ diff --git a/11-entwurfsheft-kolloquium/assets/logo.pdf b/11-entwurfsheft-kolloquium/assets/logo.pdf new file mode 100644 index 0000000..91fd334 Binary files /dev/null and b/11-entwurfsheft-kolloquium/assets/logo.pdf differ diff --git a/11-entwurfsheft-kolloquium/assets/logo.svg b/11-entwurfsheft-kolloquium/assets/logo.svg new file mode 100644 index 0000000..1609066 --- /dev/null +++ b/11-entwurfsheft-kolloquium/assets/logo.svg @@ -0,0 +1,211 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Podcast Synchronisation made Efficient + + + + diff --git a/11-entwurfsheft-kolloquium/assets/navbar.png b/11-entwurfsheft-kolloquium/assets/navbar.png new file mode 100644 index 0000000..dd9f8e8 Binary files /dev/null and b/11-entwurfsheft-kolloquium/assets/navbar.png differ diff --git a/11-entwurfsheft-kolloquium/assets/password-margin.png b/11-entwurfsheft-kolloquium/assets/password-margin.png new file mode 100644 index 0000000..d9d4fa3 Binary files /dev/null and b/11-entwurfsheft-kolloquium/assets/password-margin.png differ diff --git a/11-entwurfsheft-kolloquium/assets/password.png b/11-entwurfsheft-kolloquium/assets/password.png new file mode 100644 index 0000000..68248a0 Binary files /dev/null and b/11-entwurfsheft-kolloquium/assets/password.png differ diff --git a/11-entwurfsheft-kolloquium/assets/subscription.png b/11-entwurfsheft-kolloquium/assets/subscription.png new file mode 100644 index 0000000..58a84f9 Binary files /dev/null and b/11-entwurfsheft-kolloquium/assets/subscription.png differ diff --git a/11-entwurfsheft-kolloquium/logos/banner_2020_kit.jpg b/11-entwurfsheft-kolloquium/logos/banner_2020_kit.jpg new file mode 100644 index 0000000..70ae1d0 Binary files /dev/null and b/11-entwurfsheft-kolloquium/logos/banner_2020_kit.jpg differ diff --git a/11-entwurfsheft-kolloquium/notizen b/11-entwurfsheft-kolloquium/notizen new file mode 100644 index 0000000..70ac63b --- /dev/null +++ b/11-entwurfsheft-kolloquium/notizen @@ -0,0 +1,40 @@ +Titelseite: +- Begrüßung + +Einführung: +- Podcast: RSS-Feed, Episoden, Audio/Video +- Podcatcher: lokale Verwaltung von Podcasts, API Unterstützung, +Abspielen von Episoden +- Synchronisationsserver (das soll unser Produkt werden): Hörfortschritte, +Abonnements, Discovery (bei AntennaPod z.B. iTunes) + +Zielsetzung: + +Synchronisation (die Art, die wir bei uns anwenden): +- alle Aktionen werden auf den Server und infolge dessen auf alle Podcatcher +übertragen + +Features: +- Synchronisation: Abos, Hörfortschritt +- Weboberfläche: Aboliste, Zuletzt gehört +- Account-Verwaltung: Registrieren, Anmelden, Passwort ändern/zurücksetzen, +Account löschen, Daten importieren/exportieren + +UI-Journey: +- login.html: + - Sprache ändern + - OAuth (kann) + - Registrieren (muss): neues Fenster, E-Mail + 2-mal Passwort (sicher?), + vergeben? -> Fehler / Bestätigungslink per E-Mail (gültig 24h) + - Anmelden (muss): E-Mail + Passwort eingeben, Login merken, + login, Fehlermeldung oder Dashboard + - Passwort vergessen: neues Fenster, E-Mail eingeben, falls Account wird + Link versendet, 24h gültig, nach betätigen PW zweimal eingeben & bestätigen + (Anforderungen) + +- podcasts.html: Abonnements, Eisoden, Hörfortschritte + +- listening.html: Zuletzt gehörte Episoden, Hörfortschritt, Wann gehört + +- settings.html: PW ändern, Gpodder Import, pers. Daten imp./exp., + Account löschen diff --git a/11-entwurfsheft-kolloquium/presentation.tex b/11-entwurfsheft-kolloquium/presentation.tex new file mode 100644 index 0000000..ca05873 --- /dev/null +++ b/11-entwurfsheft-kolloquium/presentation.tex @@ -0,0 +1,52 @@ +%% Beispiel-Präsentation mit LaTeX Beamer im KIT-Design +%% entsprechend den Gestaltungsrichtlinien vom 1. August 2020 +%% +%% Siehe https://sdqweb.ipd.kit.edu/wiki/Dokumentvorlagen + +%% Beispiel-Präsentation +\documentclass[xcolor={dvipsnames}]{sdqbeamer} +\usepackage{multicol} +\usepackage{fontawesome5} +\usepackage{rest-api} +\usepackage{graphicx} +\usepackage{ulem} +\usepackage[dvipsnames]{xcolor} + + +%% Titelbild +\titleimage{banner_2020_kit} + +%% Gruppenlogo +\grouplogo{kitlogo_de_rgb} + +%% Gruppenname und Breite (Standard: 50 mm) +\groupname{Praxis der Softwareentwicklung} +%\groupnamewidth{50mm} + +\title[Kolloquium Pflichtenheft]{ + PSE\textsuperscript{2} - Podcast Synchronisation made Efficient +} +\subtitle{Kolloquium Entwurfsheft} +\author[Immanuel Reitz]{Immanuel Reitz} + +\date[17.\,01.\,2023]{17. Januar 2023} + +\begin{document} + +\KITtitleframe + +\include{slides/apiExtension} + +\include{slides/architecture} + +\include{slides/pattern} + +\include{slides/classdiagram} + +\include{slides/sequence} + +\include{slides/frontend} + +\include{slides/changes} +%comment +\end{document} diff --git a/11-entwurfsheft-kolloquium/presentation.tex.orig b/11-entwurfsheft-kolloquium/presentation.tex.orig new file mode 100644 index 0000000..8dd6019 --- /dev/null +++ b/11-entwurfsheft-kolloquium/presentation.tex.orig @@ -0,0 +1,68 @@ +%% Beispiel-Präsentation mit LaTeX Beamer im KIT-Design +%% entsprechend den Gestaltungsrichtlinien vom 1. August 2020 +%% +%% Siehe https://sdqweb.ipd.kit.edu/wiki/Dokumentvorlagen + +%% Beispiel-Präsentation +\documentclass{sdqbeamer} + +\usepackage{tikz} +\usetikzlibrary{positioning} +\usetikzlibrary{calc} +\usetikzlibrary{arrows} +\usepackage{multicol} +\usepackage{fontawesome5} +\usepackage{tikz} +\usepackage{tikz-uml} +\usepackage{rest-api} +\usetikzlibrary{positioning, arrows, chains} +\usetikzlibrary[calc] + + +%% Titelbild +\titleimage{banner_2020_kit} + +%% Gruppenlogo +\grouplogo{kitlogo_de_rgb} + +%% Gruppenname und Breite (Standard: 50 mm) +\groupname{Praxis der Softwareentwicklung} +%\groupnamewidth{50mm} + +\title[Kolloquium Pflichtenheft]{ + PSE\textsuperscript{2} - Podcast Synchronisation made Efficient +} +\subtitle{Kolloquium Entwurfsheft} +\author[Immanuel Reitz]{Immanuel Reitz} + +\date[17.\,01.\,2023]{17. Januar 2023} + +\begin{document} + +\KITtitleframe + +\include{slides/apiExtension} +% Folie Einführung +\include{slides/einführung} + +% Folie Zielsetzung +\include{slides/zielsetzung} + +\include{slides/architektur} + +\include{slides/classdiagram} + +\include{slides/abläufe} + +<<<<<<< HEAD +% TODO: Folie Features +\include{slides/features} + +\include{slides/frontend} + +% TODO: User-Journey +======= +\include{slides/pattern} +>>>>>>> main + +\end{document} diff --git a/11-entwurfsheft-kolloquium/sdqbeamer.cls b/11-entwurfsheft-kolloquium/sdqbeamer.cls new file mode 100644 index 0000000..1b9c2b8 --- /dev/null +++ b/11-entwurfsheft-kolloquium/sdqbeamer.cls @@ -0,0 +1,975 @@ +%% Vorlage für Präsentationen mit LaTeX Beamer im KIT-Design +%% entsprechend den Gestaltungsrichtlinien vom 1. August 2020 +%% +%% Siehe https://sdqweb.ipd.kit.edu/wiki/Dokumentvorlagen + + +\NeedsTeXFormat{LaTeX2e} +\ProvidesClass{sdqbeamer}[2022-05-03 v3.1.3 SDQ Beamer class] + +\RequirePackage[utf8]{inputenc} +\RequirePackage[T1]{fontenc} + +\newif\ifsectionnavigation +\newif\ifnavbarinfoot +\newif\ifnavbarinline +\newif\ifnavbarside +\newif\iffourtothree +\newif\ifsixteentonine +\newif\ifsixteentoten +\newif\ifgerman +\newif\ifsmallfooterfont + +\def\kitslogan#1{\def\@kitslogan{#1}} +\def\kitlogo#1{\def\@kitlogo{#1}} +\def\groupname#1{\def\@groupname{#1}} +\def\groupnamewidth#1{\def\@groupnamewidth{#1}} + +% siehe README.md +\DeclareOption{de}{ + \kitslogan{KIT -- Die Forschungsuniversität in der Helmholtz-Gemeinschaft} + \kitlogo{pse_logo} + \germantrue + \PassOptionsToPackage{autostyle}{csquotes} + } +\DeclareOption{en}{ + \kitslogan{KIT -- The Research University in the Helmholtz Association} + \kitlogo{kitlogo_en_rgb} + \germanfalse + } +\DeclareOption{4:3}{ + \fourtothreetrue + \sixteentoninefalse + \sixteentotenfalse +} +\DeclareOption{16:9}{ + \fourtothreefalse + \sixteentoninetrue + \sixteentotenfalse +} +\DeclareOption{16:10}{ + \fourtothreefalse + \sixteentoninefalse + \sixteentotentrue +} +\DeclareOption{navbarside}{ + \sectionnavigationtrue + \navbarsidetrue + \navbarinlinefalse + \navbarinfootfalse +} +\DeclareOption{navbarinline}{ + \sectionnavigationtrue + \navbarsidefalse + \navbarinlinetrue + \navbarinfootfalse +} +\DeclareOption{navbarinfooter}{ + \sectionnavigationtrue + \navbarsidefalse + \navbarinlinefalse + \navbarinfoottrue +} +\DeclareOption{navbaroff}{ + \sectionnavigationfalse +} +\DeclareOption{navbarkit}{ + \sectionnavigationfalse + \smallfooterfonttrue +} +\DeclareOption{smallfoot}{ + \smallfooterfonttrue +} +\DeclareOption{bigfoot}{ + \smallfooterfontfalse +} + +\ExecuteOptions{de,16:9,navbarinline,bigfoot} + +\DeclareOption*{\PassOptionsToClass{\CurrentOption}{beamer}} + +\ProcessOptions\relax + +\LoadClass[10pt,utf8]{beamer} + +% Babel-Paket wird nur bei deutscher Sprache benötigt +\ifgerman + \RequirePackage[ngerman]{babel} +\fi +\RequirePackage{csquotes} +\RequirePackage{hyperref} +\RequirePackage[absolute,overlay]{textpos} + +%% --------------- +%% | Typographie | +%% --------------- + +\RequirePackage{microtype} + +\RequirePackage[scaled=.92]{helvet} +\RequirePackage[scaled=.78]{beramono} +\RequirePackage{libertineRoman} + +\setbeamerfont{title}{series=\bfseries,size=\Large} +\setbeamerfont{frametitle}{series=\bfseries,size=\Large} +\setbeamerfont{framesubtitle}{series=\bfseries,size=\normalsize} + +%% ----------------- +%% | Folien-Layout | +%% ----------------- + +% Seitenverhältnis +% +% Die Folien sind auf die Standardhöhe in LaTeX Beamer (9,6 cm) normiert. +% Die Maße der KIT-Gestaltungsrichtlinien (Folienhöhe 14,3 cm) wurden durch +% den Quotienten 1,5 geteilt. + +\RequirePackage{geometry} +\iffourtothree + \geometry{papersize={12.8cm,9.6cm}} +\fi +\ifsixteentoten + \geometry{papersize={15.36cm,9.6cm}} +\fi +\ifsixteentonine + \geometry{papersize={17.07cm,9.6cm}} +\fi + +% Ränder laut Gestaltungsrichtlinen; 3 mm -> 2 mm, 11 mm -> 7,3 mm +\newlength{\kitoutermargin} +\setlength{\kitoutermargin}{2mm} +\newlength{\kitinnermargin} +\setlength{\kitinnermargin}{7.3mm} +\newlength{\kitbottommargin} +\setlength{\kitbottommargin}{\kitinnermargin} + +% Ränder außen +\setbeamersize{text margin left=\kitinnermargin,text margin right=\kitinnermargin} + +% keine Navigationssymbole +\setbeamertemplate{navigation symbols}{} +\setbeamercovered{invisible} +\useinnertheme{rounded} +\beamer@compresstrue % Miniframes (Navigations-Punkte) für Subsections immer in einer Zeile, ohne Umbrüche + +% Folientitel +\setbeamertemplate{frametitle}{ + \ifbeamer@plainframe\else% + % Unterkante Titeltext: 22,5 mm -> 15 mm von Seitenkopf + \begin{textblock*}{\dimexpr\paperwidth-30mm-2\kitinnermargin}[0,1](\kitinnermargin,15mm)% + \usebeamerfont{frametitle}\insertframetitle% + \ifx\insertframesubtitle\@empty\else\\[.1em]\fi + \usebeamerfont{framesubtitle}\insertframesubtitle% + \end{textblock*}% + \begin{textblock*}{20mm}[1,1](\dimexpr\paperwidth-\kitinnermargin\relax,15mm)%j + \includegraphics[width=20mm]{logos/\@kitlogo}% + \end{textblock*}% + \fi +% Rand oben (ergibt Beginn des Textes bei 34 mm -> 22,7 mm) + \vspace {18mm} +} + +%% Fußzeile +\newlength{\kitbottom} + % Navbar in Footer: Schmale Fußzeile +\ifnavbarinfoot + % Bei Navbar in Footer immer kleiner Font in der Fußzeile + \setbeamerfont{footer}{size=\fontsize{6pt}{7.2pt}\selectfont} + \setlength{\kitbottom}{4mm} +\else + % Ansonsten kleiner Text nur, wenn "smallfoot" gewählt + \ifsmallfooterfont + \setbeamerfont{footer}{size=\fontsize{6pt}{7.2pt}\selectfont} + \else + \setbeamerfont{footer}{size=\scriptsize} + \fi + \setlength{\kitbottom}{\kitbottommargin} +\fi +\setbeamerfont{page number in head/foot}{series=\bfseries} + +\newlength{\kitbottomdepth} +\newlength{\kitbottomheight} +\newlength{\kitfootergroupwidth} + +\setbeamertemplate{footline}{% +\setlength{\kitbottomdepth}{\dimexpr.5\kitbottom-.5em\relax}% +\setlength{\kitbottomheight}{\dimexpr.5\kitbottom+.5em\relax}% + %% die "%" am Ende sind nötig, damit keine Abstände eingefügt werden + % + % Falls kein Gruppenname angegeben, die ganze Breite der Fußzeile für den Titel nutzen. + \ifdefined\@groupname% + \ifx\@groupname\empty% + \setlength{\kitfootergroupwidth}{0mm}% + \else% + % Falls die Breite des Gruppenlogos definiert ist, diese nehmen, sonst 50 mm + \ifdefined\@groupnamewidth% + \setlength{\kitfootergroupwidth}{\@groupnamewidth}% + \else% + \setlength{\kitfootergroupwidth}{50mm}% + \fi% + \fi% + \else + \setlength{\kitfootergroupwidth}{0mm}% + \fi + \usebeamerfont{footer}% + \ifsectionnavigation% + % Option "navbarinline" + \ifnavbarinline% + \begin{beamercolorbox}[wd=\paperwidth, leftskip=2mm, rightskip=2mm]{} + \insertnavigation{\dimexpr\paperwidth-4mm\relax} + \vspace{1mm} + \end{beamercolorbox}% + \fi% + % Option "navbarinfooter" + \ifnavbarinfoot% + % Punkte für Subsections deaktivieren + \setbeamertemplate{mini frames}{}% + \begin{beamercolorbox}[wd=\paperwidth, leftskip=1mm, rightskip=1mm]{}% + \insertsectionnavigationhorizontal{\dimexpr\paperwidth-\kitoutermargin\relax}{}{} + \end{beamercolorbox}% + \fi% + \fi% + \leavevmode% + \begin{beamercolorbox}[wd=13mm, ht=\kitbottomheight, dp=\kitbottomdepth, leftskip=4mm]{} + \usebeamerfont{page number in head/foot}% + \strut\insertframenumber{}/\inserttotalframenumber% + \end{beamercolorbox}% + \begin{beamercolorbox}[wd=20mm, ht=\kitbottomheight, dp=\kitbottomdepth]{} + \usebeamerfont{date in head/foot}% + \strut\insertshortdate% + \end{beamercolorbox}% + % Die Boxen mit dem Titel und dem Gruppennamen sind vertikal zentriert, damit auch zweizeilige Texte schön aussehen + % Daher müssen sie um \kitbottomdepth nach unten verschoben werden + \raisebox{-\kitbottomdepth}{ + % Die Box hat daher auch Höhe \kitbottom und Tiefe 0mm + \begin{beamercolorbox}[wd=\dimexpr\paperwidth-37mm-\kitfootergroupwidth, ht=\kitbottom, dp=0mm]{}% + % Inhalt vertikal zentrieren; Anpassung um 1.5 pt, damit bei einzeiligem Inhalt genau die Baseline der Blöcke mit Seitenzahl und Datum getroffen wird + \vbox to\kitbottom{\vfill\vskip1.5pt% + \beamer@shortauthor\ifx\beamer@shortauthor\empty\else: \fi\beamer@shorttitle% + \vfill}% + \end{beamercolorbox}% + \ifdefined\@groupname% + \begin{beamercolorbox}[wd=\kitfootergroupwidth, ht=\kitbottom, dp=0mm, rightskip=\kitinnermargin]{} + \vbox to\kitbottom{\vfill\vskip1.5pt% + \raggedleft\@groupname% + \vfill}% + \end{beamercolorbox}% + \fi% + }% +} + +%% Option "navbarside" +\ifnavbarside +\useoutertheme[height=0cm,width=3.5cm,left]{sidebar} + +\setbeamerfont{title in sidebar}{family=\sffamily,series=\mdseries,size={\fontsize{10pt}{11pt}}} +\setbeamerfont{section in sidebar}{family=\sffamily,series=\mdseries,size={\fontsize{9pt}{9.9pt}}} +\setbeamerfont{subsection in sidebar}{family=\sffamily,series=\mdseries,size={\fontsize{8pt}{8.8pt}}} + +\setbeamertemplate{sidebar \beamer@sidebarside} + {\vskip1.5cm% + \hskip6.5mm% + \advance\beamer@sidebarwidth by -5mm% + \insertverticalnavigation{\beamer@sidebarwidth}% + }% +\fi + +%% Hintergrund +\usebackgroundtemplate{ + % Trennlinie nicht bei "plain"-Frames + \ifbeamer@plainframe\else\kitseparationline\fi +} + +% Trennlinie +\newcommand{\kitseparationline}{ + \begin{pgfpicture}{0mm}{0mm}{\paperwidth}{\paperheight} + \pgfsetstrokecolor{black!15} + \pgfsetlinewidth{.5pt} + \pgfpathmoveto{\pgfpoint{\kitoutermargin}{\kitinnermargin}} + \pgfpathlineto{\pgfpoint{\paperwidth-\kitoutermargin}{\kitinnermargin}} + \pgfusepath{stroke} + \end{pgfpicture}% +} + + +%% -------------- +%% | Titelseite | +%% -------------- + +\def\titleimage#1{\def\@titleimage{#1}} +\def\grouplogo#1{\def\@grouplogo{#1}} + +\newcommand{\KITtitleframe}{ + \begin{frame}[plain] + \titlepage + \end{frame} +} + +\newlength{\kittitleimageheight} +\setbeamertemplate{title page}{ + % From textpos documentation (https://ctan.org/pkg/textpos) + % + % \begin{textblock}{}[,](,) + % The coordinates and are fractions of the width and height of the text + % box, respectively, and state that the box is to be placed so that the reference point + % (,) within the box is to be placed at the point (,) on the page. + + % KIT-Logo + \begin{textblock*}{30mm}(\kitinnermargin,6.7mm) + \includegraphics[width=30mm]{logos/\@kitlogo} + \end{textblock*} + + % Gruppenlogo + \ifdefined\@grouplogo + \ifx\@grouplogo\empty \else + \begin{textblock*}{20mm}(\dimexpr\paperwidth-24mm\relax,6.7mm) + \includegraphics[width=20mm,height=20mm,keepaspectratio]{logos/\@grouplogo} + \end{textblock*} + \fi % falls \grouplogo{} aufgerufen wird, kein Gruppenlogo einbinden + \else + \begin{textblock*}{20mm}(\dimexpr\paperwidth-24mm\relax,6.7mm) + \colorbox{kit-purple100!20}{\parbox[t][12mm][c]{19mm}{\color{kit-purple100}\scriptsize\centering + \ifgerman + Bitte Logo über \texttt{\textbackslash grouplogo\{\}} festlegen. + \else + Please set a logo using \texttt{\textbackslash grouplogo\{\}}. + \fi + }} + \end{textblock*} + \fi + + % Titel + \begin{textblock*}{\dimexpr\paperwidth-8mm\relax}[0,.5](\kitinnermargin,28mm) + \usebeamerfont*{title}\inserttitle + \end{textblock*} + + % Untertitel + \begin{textblock*}{\dimexpr\paperwidth-8mm\relax}(\kitinnermargin,36mm) + \small\textbf{\insertsubtitle} + \end{textblock*} + + % Autor + \begin{textblock*}{\dimexpr\paperwidth-8mm\relax}(\kitinnermargin,41mm) + \small\insertauthor~\textbar~\insertdate + \end{textblock*} + + % Titelbild + \setlength{\kittitleimageheight}{40mm} + \begin{textblock*}{\paperwidth}(\kitoutermargin,\dimexpr\paperheight-\kitbottommargin-\kittitleimageheight) + \begin{pgfpicture}{0mm}{0mm}{\paperwidth}{\kittitleimageheight} + % Clipping-Pfad um titelbild + \pgfsetstrokecolor{black!15} + \pgfsetlinewidth{1pt} + \pgfsetcornersarced{\pgfpoint{3mm}{3mm}} + \pgfpathmoveto{\pgfpoint{\paperwidth-2\kitoutermargin}{0mm}} + \pgfpathlineto{\pgfpoint{\paperwidth-2\kitoutermargin}{\kittitleimageheight}} + \pgfsetcornersarced{\pgfpointorigin} + \pgfpathlineto{\pgfpoint{0mm}{\kittitleimageheight}} + \pgfsetcornersarced{\pgfpoint{3mm}{3mm}} + \pgfpathlineto{\pgfpointorigin} + \pgfsetcornersarced{\pgfpointorigin} + \pgfpathclose + \pgfusepath{stroke,clip} + \pgfsetstrokecolor{black} + + % Titelbild + \ifdefined\@titleimage + \ifx\@titleimage\empty \else% + \pgftext[at=\pgfpoint{.5\paperwidth}{0mm},center,bottom]{% + \includegraphics[height=40mm]{logos/\@titleimage} + } + \fi % Bei Aufruf von \titleimage{} leeren Rahmen anzeigen. + \else + \pgftext[at=\pgfpoint{.5\paperwidth}{.5\kittitleimageheight},center,base]{% + \colorbox{kit-purple100!20}{\parbox[c][\kittitleimageheight][c]{\paperwidth}{\color{kit-purple100}\centering Bitte Titelbild über \texttt{\textbackslash titleimage\{\}} festlegen. + }}% + } + \fi + + + \end{pgfpicture}% + \end{textblock*} + + + % KIT slogan + \begin{textblock*}{80mm}[0,.5](\kitoutermargin,\dimexpr\paperheight-.5\kitbottommargin) + \fontsize{5.5pt}{5.5pt}\selectfont\@kitslogan + \end{textblock*} + + \begin{textblock*}{30mm}[1,.5](\dimexpr\paperwidth-\kitoutermargin\relax,\dimexpr\paperheight-.5\kitbottommargin) + \fontsize{11pt}{11pt}\selectfont\bfseries\raggedleft% + {\href{https://www.kit.edu}{www.kit.edu}} + \end{textblock*} + +} +%% --------------- +%% | /Titelseite | +%% --------------- + +%% ---------- +%% | Farben | +%% ---------- +%% KIT-Farbschema + +% KIT color green : +\definecolor{kit-green}{RGB}{0, 150, 130} +\definecolor{kit-green100}{RGB}{0, 150, 130} +\definecolor{kit-green90}{rgb}{0.1, 0.6294, 0.5588} +\definecolor{kit-green80}{rgb}{0.2, 0.6706, 0.6078} +\definecolor{kit-green75}{rgb}{0.25, 0.6912, 0.6324} +\definecolor{kit-green70}{rgb}{0.3, 0.7118, 0.6569} +\definecolor{kit-green60}{rgb}{0.4, 0.7529, 0.7059} +\definecolor{kit-green50}{rgb}{0.5, 0.7941, 0.7549} +\definecolor{kit-green40}{rgb}{0.6, 0.8353, 0.8039} +\definecolor{kit-green30}{rgb}{0.7, 0.8765, 0.8529} +\definecolor{kit-green25}{rgb}{0.75, 0.8971, 0.8775} +\definecolor{kit-green20}{rgb}{0.8, 0.9176, 0.902} +\definecolor{kit-green15}{rgb}{0.85, 0.9382, 0.9265} +\definecolor{kit-green10}{rgb}{0.9, 0.9588, 0.951} +\definecolor{kit-green5}{rgb}{0.95, 0.9794, 0.9755} + +% KIT color blue: +\definecolor{kit-blue}{RGB}{70, 100, 170} +\definecolor{kit-blue100}{RGB}{70, 100, 170} +\definecolor{kit-blue90}{rgb}{0.3471, 0.4529, 0.7} +\definecolor{kit-blue80}{rgb}{0.4196, 0.5137, 0.7333} +\definecolor{kit-blue75}{rgb}{0.4559, 0.5441, 0.75} +\definecolor{kit-blue70}{rgb}{0.4922, 0.5745, 0.7667} +\definecolor{kit-blue60}{rgb}{0.5647, 0.6353, 0.8} +\definecolor{kit-blue50}{rgb}{0.6373, 0.6961, 0.8333} +\definecolor{kit-blue40}{rgb}{0.7098, 0.7569, 0.8667} +\definecolor{kit-blue30}{rgb}{0.7824, 0.8176, 0.9} +\definecolor{kit-blue25}{rgb}{0.8186, 0.848, 0.9167} +\definecolor{kit-blue20}{rgb}{0.8549, 0.8784, 0.9333} +\definecolor{kit-blue15}{rgb}{0.8912, 0.9088, 0.95} +\definecolor{kit-blue10}{rgb}{0.9275, 0.9392, 0.9667} +\definecolor{kit-blue5}{rgb}{0.9637, 0.9696, 0.9833} + +% KIT color red : +\definecolor{kit-red}{RGB}{162, 34, 35} +\definecolor{kit-red100}{RGB}{162, 34, 35} +\definecolor{kit-red90}{rgb}{0.6718, 0.22, 0.2235} +\definecolor{kit-red80}{rgb}{0.7082, 0.3067, 0.3098} +\definecolor{kit-red75}{rgb}{0.7265, 0.35, 0.3529} +\definecolor{kit-red70}{rgb}{0.7447, 0.3933, 0.3961} +\definecolor{kit-red60}{rgb}{0.7812, 0.48, 0.4824} +\definecolor{kit-red50}{rgb}{0.8176, 0.5667, 0.5686} +\definecolor{kit-red40}{rgb}{0.8541, 0.6533, 0.6549} +\definecolor{kit-red30}{rgb}{0.8906, 0.74, 0.7412} +\definecolor{kit-red25}{rgb}{0.9088, 0.7833, 0.7843} +\definecolor{kit-red20}{rgb}{0.9271, 0.8267, 0.8275} +\definecolor{kit-red15}{rgb}{0.9453, 0.87, 0.8706} +\definecolor{kit-red10}{rgb}{0.9635, 0.9133, 0.9137} +\definecolor{kit-red5}{rgb}{0.9818, 0.9567, 0.9569} + +% KIT color yellow : +\definecolor{kit-yellow}{RGB}{252, 229, 0} +\definecolor{kit-yellow100}{RGB}{252, 229, 0} +\definecolor{kit-yellow90}{rgb}{0.9894, 0.9082, 0.1} +\definecolor{kit-yellow80}{rgb}{0.9906, 0.9184, 0.2} +\definecolor{kit-yellow75}{rgb}{0.9912, 0.9235, 0.25} +\definecolor{kit-yellow70}{rgb}{0.9918, 0.9286, 0.3} +\definecolor{kit-yellow60}{rgb}{0.9929, 0.9388, 0.4} +\definecolor{kit-yellow50}{rgb}{0.9941, 0.949, 0.5} +\definecolor{kit-yellow40}{rgb}{0.9953, 0.9592, 0.6} +\definecolor{kit-yellow30}{rgb}{0.9965, 0.9694, 0.7} +\definecolor{kit-yellow25}{rgb}{0.9971, 0.9745, 0.75} +\definecolor{kit-yellow20}{rgb}{0.9976, 0.9796, 0.8} +\definecolor{kit-yellow15}{rgb}{0.9982, 0.9847, 0.85} +\definecolor{kit-yellow10}{rgb}{0.9988, 0.9898, 0.9} +\definecolor{kit-yellow5}{rgb}{0.9994, 0.9949, 0.95} + +% KIT color orange : +\definecolor{kit-orange}{RGB}{223, 155, 27} +\definecolor{kit-orange100}{RGB}{223, 155, 27} +\definecolor{kit-orange90}{rgb}{0.8871, 0.6471, 0.1953} +\definecolor{kit-orange80}{rgb}{0.8996, 0.6863, 0.2847} +\definecolor{kit-orange75}{rgb}{0.9059, 0.7059, 0.3294} +\definecolor{kit-orange70}{rgb}{0.9122, 0.7255, 0.3741} +\definecolor{kit-orange60}{rgb}{0.9247, 0.7647, 0.4635} +\definecolor{kit-orange50}{rgb}{0.9373, 0.8039, 0.5529} +\definecolor{kit-orange40}{rgb}{0.9498, 0.8431, 0.6424} +\definecolor{kit-orange30}{rgb}{0.9624, 0.8824, 0.7318} +\definecolor{kit-orange25}{rgb}{0.9686, 0.902, 0.7765} +\definecolor{kit-orange20}{rgb}{0.9749, 0.9216, 0.8212} +\definecolor{kit-orange15}{rgb}{0.9812, 0.9412, 0.8659} +\definecolor{kit-orange10}{rgb}{0.9875, 0.9608, 0.9106} +\definecolor{kit-orange5}{rgb}{0.9937, 0.9804, 0.9553} + +% KIT color lightgreen : +\definecolor{kit-lightgreen}{RGB}{140, 182, 60} +\definecolor{kit-lightgreen100}{RGB}{140, 182, 60} +\definecolor{kit-lightgreen90}{rgb}{0.5941, 0.7424, 0.3118} +\definecolor{kit-lightgreen80}{rgb}{0.6392, 0.771, 0.3882} +\definecolor{kit-lightgreen75}{rgb}{0.6618, 0.7853, 0.4265} +\definecolor{kit-lightgreen70}{rgb}{0.6843, 0.7996, 0.4647} +\definecolor{kit-lightgreen60}{rgb}{0.7294, 0.8282, 0.5412} +\definecolor{kit-lightgreen50}{rgb}{0.7745, 0.8569, 0.6176} +\definecolor{kit-lightgreen40}{rgb}{0.8196, 0.8855, 0.6941} +\definecolor{kit-lightgreen30}{rgb}{0.8647, 0.9141, 0.7706} +\definecolor{kit-lightgreen25}{rgb}{0.8873, 0.9284, 0.8088} +\definecolor{kit-lightgreen20}{rgb}{0.9098, 0.9427, 0.8471} +\definecolor{kit-lightgreen15}{rgb}{0.9324, 0.9571, 0.8853} +\definecolor{kit-lightgreen10}{rgb}{0.9549, 0.9714, 0.9235} +\definecolor{kit-lightgreen5}{rgb}{0.9775, 0.9857, 0.9618} + + +% KIT color purple : +\definecolor{kit-purple}{RGB}{163, 16, 124} +\definecolor{kit-purple100}{RGB}{163, 16, 124} +\definecolor{kit-purple90}{rgb}{0.6753, 0.1565, 0.5376} +\definecolor{kit-purple80}{rgb}{0.7114, 0.2502, 0.589} +\definecolor{kit-purple75}{rgb}{0.7294, 0.2971, 0.6147} +\definecolor{kit-purple70}{rgb}{0.7475, 0.3439, 0.6404} +\definecolor{kit-purple60}{rgb}{0.7835, 0.4376, 0.6918} +\definecolor{kit-purple50}{rgb}{0.8196, 0.5314, 0.7431} +\definecolor{kit-purple40}{rgb}{0.8557, 0.6251, 0.7945} +\definecolor{kit-purple30}{rgb}{0.8918, 0.7188, 0.8459} +\definecolor{kit-purple25}{rgb}{0.9098, 0.7657, 0.8716} +\definecolor{kit-purple20}{rgb}{0.9278, 0.8125, 0.8973} +\definecolor{kit-purple15}{rgb}{0.9459, 0.8594, 0.9229} +\definecolor{kit-purple10}{rgb}{0.9639, 0.9063, 0.9486} +\definecolor{kit-purple5}{rgb}{0.982, 0.9531, 0.9743} + +% KIT color brown : +\definecolor{kit-brown}{RGB}{167, 130, 46} +\definecolor{kit-brown100}{RGB}{167, 130, 46} +\definecolor{kit-brown90}{rgb}{0.6894, 0.5588, 0.2624} +\definecolor{kit-brown80}{rgb}{0.7239, 0.6078, 0.3443} +\definecolor{kit-brown75}{rgb}{0.7412, 0.6324, 0.3853} +\definecolor{kit-brown70}{rgb}{0.7584, 0.6569, 0.4263} +\definecolor{kit-brown60}{rgb}{0.7929, 0.7059, 0.5082} +\definecolor{kit-brown50}{rgb}{0.8275, 0.7549, 0.5902} +\definecolor{kit-brown40}{rgb}{0.862, 0.8039, 0.6722} +\definecolor{kit-brown30}{rgb}{0.8965, 0.8529, 0.7541} +\definecolor{kit-brown25}{rgb}{0.9137, 0.8775, 0.7951} +\definecolor{kit-brown20}{rgb}{0.931, 0.902, 0.8361} +\definecolor{kit-brown15}{rgb}{0.9482, 0.9265, 0.8771} +\definecolor{kit-brown10}{rgb}{0.9655, 0.951, 0.918} +\definecolor{kit-brown5}{rgb}{0.9827, 0.9755, 0.959} + +% KIT color cyan : +\definecolor{kit-cyan}{RGB}{35, 161, 224} +\definecolor{kit-cyan100}{RGB}{35, 161, 224} +\definecolor{kit-cyan90}{rgb}{0.2235, 0.6682, 0.8906} +\definecolor{kit-cyan80}{rgb}{0.3098, 0.7051, 0.9027} +\definecolor{kit-cyan75}{rgb}{0.3529, 0.7235, 0.9088} +\definecolor{kit-cyan70}{rgb}{0.3961, 0.742, 0.9149} +\definecolor{kit-cyan60}{rgb}{0.4824, 0.7788, 0.9271} +\definecolor{kit-cyan50}{rgb}{0.5686, 0.8157, 0.9392} +\definecolor{kit-cyan40}{rgb}{0.6549, 0.8525, 0.9514} +\definecolor{kit-cyan30}{rgb}{0.7412, 0.8894, 0.9635} +\definecolor{kit-cyan25}{rgb}{0.7843, 0.9078, 0.9696} +\definecolor{kit-cyan20}{rgb}{0.8275, 0.9263, 0.9757} +\definecolor{kit-cyan15}{rgb}{0.8706, 0.9447, 0.9818} +\definecolor{kit-cyan10}{rgb}{0.9137, 0.9631, 0.9878} +\definecolor{kit-cyan5}{rgb}{0.9569, 0.9816, 0.9939} + +% KIT color gray : +\definecolor{kit-gray}{RGB}{0, 0, 0} +\definecolor{kit-gray100}{RGB}{0, 0, 0} +\definecolor{kit-gray90}{rgb}{0.1, 0.1, 0.1} +\definecolor{kit-gray80}{rgb}{0.2, 0.2, 0.2} +\definecolor{kit-gray75}{rgb}{0.25, 0.25, 0.25} +\definecolor{kit-gray70}{rgb}{0.3, 0.3, 0.3} +\definecolor{kit-gray60}{rgb}{0.4, 0.4, 0.4} +\definecolor{kit-gray50}{rgb}{0.5, 0.5, 0.5} +\definecolor{kit-gray40}{rgb}{0.6, 0.6, 0.6} +\definecolor{kit-gray30}{rgb}{0.7, 0.7, 0.7} +\definecolor{kit-gray25}{rgb}{0.75, 0.75, 0.75} +\definecolor{kit-gray20}{rgb}{0.8, 0.8, 0.8} +\definecolor{kit-gray15}{rgb}{0.85, 0.85, 0.85} +\definecolor{kit-gray10}{rgb}{0.9, 0.9, 0.9} +\definecolor{kit-gray5}{rgb}{0.95, 0.95, 0.95} + + + +\setbeamercolor*{normal text}{fg=black} +\setbeamercolor*{alerted text}{fg=kit-red100} +\setbeamercolor*{example text}{fg=black} +\setbeamercolor*{structure}{fg=black} + +\setbeamercolor*{palette primary}{fg=black,bg=black!15} +\setbeamercolor*{palette secondary}{fg=black,bg=black!15} +\setbeamercolor*{palette tertiary}{fg=black,bg=black!15} +\setbeamercolor*{palette quaternary}{fg=black,bg=black!15} + +\setbeamercolor*{palette sidebar primary}{fg=black!75} +\setbeamercolor*{palette sidebar secondary}{fg=black!75} +\setbeamercolor*{palette sidebar tertiary}{fg=black!75} +\setbeamercolor*{palette sidebar quaternary}{fg=black!75} + +\setbeamercolor*{item projected}{fg=white,bg=kit-green100} + +\setbeamercolor*{block title}{fg=white,bg=kit-green100} +\setbeamercolor*{block title alerted}{use=alerted text,fg=white,bg=alerted text.fg!75!black} +\setbeamercolor*{block title example}{fg=white,bg=kit-blue100} + +\setbeamercolor*{block body}{fg=black,bg=kit-green15} +\setbeamercolor*{block body alerted}{parent=normal text,use=block title alerted,bg=block title alerted.bg!10!bg} +\setbeamercolor*{block body example}{fg=black,bg=kit-blue15} + +\setbeamercolor*{separation line}{} +\setbeamercolor*{fine separation line}{} + +\setbeamercolor*{background canvas}{bg=white} + +%% ----------- +%% | /Farben | +%% ----------- + +%% ----------------------------------- +%% | halbgerundete Aufzählungspunkte | +%% ----------------------------------- + +\newcommand{\itemizeColor}{kit-green100} + +% KIT-Aufzählungszeichen +\newcommand{\KITmark}{% + \begin{pgfpicture}{0mm}{0mm}{1ex}{1ex} + {\pgfsetcornersarced{\pgfpoint{.3ex}{.3ex}} + \pgfpathmoveto{\pgfpoint{0cm}{1ex}} + \pgfpathlineto{\pgfpoint{1ex}{1ex}} + \pgfpathlineto{\pgfpoint{1ex}{0cm}}} + {\pgfsetcornersarced{\pgfpoint{.3ex}{.3ex}} + \pgfpathmoveto{\pgfpoint{1ex}{0cm}} + \pgfpathlineto{\pgfpointorigin} + \pgfpathlineto{\pgfpoint{0cm}{1ex}}} + \color{\itemizeColor} + \pgfusepath{fill} + \end{pgfpicture}% +} + +% \setbeamertemplate{itemize items}{\raisebox{.2ex}{\KITmark}} +\setbeamertemplate{itemize items}[square] +\setbeamercolor{itemize item}{fg=kit-green100} + +%% ---------------------- +%% | Inhaltsverzeichnis | +%% ---------------------- + +\setbeamertemplate{section in toc}{\normalsize\textbf{\textcolor{kit-blue}{\inserttocsectionnumber.~\inserttocsection}}\par} +\setbeamertemplate{subsection in toc}{\small\hspace{1.2em}\raisebox{.2ex}{\KITmark}\hspace{\labelsep}\inserttocsubsection\par} + +%% ------------------------------ +%% | halbgerundete Beamer-Boxen | +%% ------------------------------ + +\renewcommand\beamerboxesrounded[2][]{% + \global\let\beamer@firstlineitemizeunskip=\relax% + \vbox\bgroup% + \setkeys{beamerboxes}{upper=block title,lower=block body,width=\textwidth,shadow=false}% + \setkeys{beamerboxes}{#1}% + {% + \usebeamercolor{\bmb@lower}% + \globalcolorstrue% + \colorlet{lower.bg}{bg}% + }% + {% + \usebeamercolor{\bmb@upper}% + \globalcolorstrue% + \colorlet{upper.bg}{bg}% + }% + % + % Typeset head + % + \vskip4bp + \setbox\bmb@box=\hbox{% + \begin{minipage}[b]{\bmb@width}% + \usebeamercolor[fg]{\bmb@upper}% + #2% + \end{minipage}}% + \ifdim\wd\bmb@box=0pt% + \setbox\bmb@box=\hbox{}% + \ht\bmb@box=1.5pt% + \bmb@prevheight=-4.5pt% + \else% + \wd\bmb@box=\bmb@width% + \bmb@temp=\dp\bmb@box% + \ifdim\bmb@temp<1.5pt% + \bmb@temp=1.5pt% + \fi% + \setbox\bmb@box=\hbox{\raise\bmb@temp\hbox{\box\bmb@box}}% + \dp\bmb@box=0pt% + \bmb@prevheight=\ht\bmb@box% + \fi% + \bmb@temp=\bmb@width% + \bmb@dima=\bmb@temp\advance\bmb@dima by2.2bp% + \bmb@dimb=\bmb@temp\advance\bmb@dimb by4bp% + \hbox{% + \begin{pgfpicture}{0bp}{+-\ht\bmb@box}{0bp}{+-\ht\bmb@box} + \ifdim\wd\bmb@box=0pt% + \color{lower.bg}% + \else% + \color{upper.bg}% + \fi% + \pgfpathqmoveto{-4bp}{-1bp} + % Adaption for "KIT-Design" + \pgfpathlineto{\pgfpoint{-4bp}{3bp}} + %\pgfpathqcurveto{-4bp}{1.2bp}{-2.2bp}{3bp}{0bp}{3bp} + \pgfpathlineto{\pgfpoint{\bmb@temp}{3bp}} + \pgfpathcurveto% + {\pgfpoint{\bmb@dima}{3bp}}% + {\pgfpoint{\bmb@dimb}{1.2bp}}% + {\pgfpoint{\bmb@dimb}{-1bp}}% + \bmb@dima=-\ht\bmb@box% + \advance\bmb@dima by-2pt% + \pgfpathlineto{\pgfpoint{\bmb@dimb}{\bmb@dima}} + \pgfpathlineto{\pgfpoint{-4bp}{\bmb@dima}} + \pgfusepath{fill} + \end{pgfpicture}% + \copy\bmb@box% + }% + \nointerlineskip% + \vskip-1pt% + \ifdim\wd\bmb@box=0pt% + \else% + \hbox{% + \begin{pgfpicture}{0pt}{0pt}{\bmb@width}{6pt} + \bmb@dima=\bmb@width% + \advance\bmb@dima by8bp% + \pgfpathrectangle{\pgfpoint{-4bp}{-1bp}}{\pgfpoint{\bmb@dima}{8bp}} + \pgfusepath{clip} + {\pgftransformshift{\pgfpoint{-4bp}{0bp}}\pgftext[left,base]{\pgfuseshading{bmb@transition}}}% + \end{pgfpicture}% + }% + \nointerlineskip% + \vskip-0.5pt% + \fi% + \ifbmb@shadow% + \setbox\bmb@boxshadow=\hbox{\pgfuseshading{bmb@shadow}}% + \setbox\bmb@boxshadowball=\hbox{\pgfuseshading{bmb@shadowball}}% + \setbox\bmb@boxshadowballlarge=\hbox{\pgfuseshading{bmb@shadowballlarge}}% + \fi% + \setbox\bmb@colorbox=\hbox{{\pgfpicturetrue\pgfsetcolor{lower.bg}}}% + \setbox\bmb@box=\hbox\bgroup\begin{minipage}[b]{\bmb@width}% + \vskip2pt% + \usebeamercolor[fg]{\bmb@lower}% + \colorlet{beamerstructure}{upper.bg}% + \colorlet{structure}{upper.bg}% + %\color{.}% + } + +\def\endbeamerboxesrounded{% + \end{minipage}\egroup% + \wd\bmb@box=\bmb@width% + \bmb@temp=\dp\bmb@box% + \advance\bmb@temp by.5pt% + \setbox\bmb@box=\hbox{\raise\bmb@temp\hbox{\box\bmb@box}}% + \dp\bmb@box=0pt% + \bmb@temp=\wd\bmb@box% + \bmb@dima=\bmb@temp\advance\bmb@dima by2.2bp% + \bmb@dimb=\bmb@temp\advance\bmb@dimb by4bp% + \hbox{% + \begin{pgfpicture}{0bp}{0bp}{0bp}{0bp} + \ifbmb@shadow% + {\pgftransformshift{\pgfpoint{4bp}{-3bp}}\pgftext{\copy\bmb@boxshadowball}} + \begin{pgfscope} + {% + \advance\bmb@temp by-1bp% + \pgfpathrectangle{\pgfpoint{\bmb@temp}{-7bp}}{\pgfpoint{9bp}{9bp}}% + \pgfusepath{clip} + }% + {\pgftransformshift{\pgfpoint{\bmb@temp}{1bp}}\pgftext{\box\bmb@boxshadowballlarge}} + \end{pgfscope} + \begin{pgfscope} + \advance\bmb@temp by-4bp% + \pgfpathrectangle{\pgfpoint{4bp}{-7bp}}{\pgfpoint{\bmb@temp}{5bp}} + \pgfusepath{clip} + {\pgftransformshift{\pgfpoint{4bp}{-7bp}}\pgftext[left,base]{\copy\bmb@boxshadow}}% + \end{pgfscope} + \begin{pgfscope} + \advance\bmb@temp by 4bp% + \bmb@dima=\ht\bmb@box% + \advance\bmb@dima by\bmb@prevheight% + \advance\bmb@dima by 4bp% + \pgfpathrectangle{\pgfpoint{\bmb@temp}{1bp}}{\pgfpoint{4bp}{\bmb@dima}} + \pgfusepath{clip} + \advance\bmb@dima by-4bp% + {\pgftransformshift{\pgfpoint{\bmb@temp}{\bmb@dima}}\pgftext{\box\bmb@boxshadowball}} + \advance\bmb@dima by-1bp% + \pgfpathrectangle{\pgfpoint{\bmb@temp}{1bp}}{\pgfpoint{4bp}{\bmb@dima}} + \pgfusepath{clip} + \advance\bmb@temp by4bp% + {\pgftransformshift{\pgfpoint{\bmb@temp}{-3bp}}% + \pgftransformrotate{90}% + \pgftext[left,base]{\box\bmb@boxshadow}}% + \end{pgfscope} + \fi% + \unhbox\bmb@colorbox% + \pgfpathqmoveto{-4bp}{1bp} + \pgfpathqcurveto{-4bp}{-1.2bp}{-2.2bp}{-3bp}{0bp}{-3bp} + \pgfpathlineto{\pgfpoint{\the\bmb@dimb}{-3bp}} + { + \bmb@dima=\ht\bmb@box% + \pgfpathlineto{\pgfpoint{\bmb@dimb}{\bmb@dima}} + \pgfpathlineto{\pgfpoint{-4bp}{\bmb@dima}} + \pgfusepath{fill} + } + \ifbmb@shadow% + { + \color{black!50!bg} + \pgfsetlinewidth{0pt} + \pgfpathmoveto{\pgfpoint{\bmb@dimb}{-.5bp}} + \bmb@dima=\ht\bmb@box% + \advance\bmb@dima by\bmb@prevheight% + \advance\bmb@dima by 1bp% + \pgfpathlineto{\pgfpoint{\bmb@dimb}{\bmb@dima}} + \pgfusepath{stroke} + \bmb@temp=\bmb@dima + \advance\bmb@dima by 1bp% + \color{black!31!bg} + \pgfpathmoveto{\pgfpoint{\bmb@dimb}{\bmb@temp}} + \pgfpathlineto{\pgfpoint{\bmb@dimb}{\bmb@dima}} + \pgfusepath{stroke} + \advance\bmb@dima by 1bp% + \advance\bmb@temp by 1bp% + \color{black!19!bg} + \pgfpathmoveto{\pgfpoint{\bmb@dimb}{\bmb@temp}} + \pgfpathlineto{\pgfpoint{\bmb@dimb}{\bmb@dima}} + \pgfusepath{stroke} + \advance\bmb@dima by 1bp% + \advance\bmb@temp by 1bp% + \color{black!6!bg} + \pgfpathmoveto{\pgfpoint{\bmb@dimb}{\bmb@temp}} + \pgfpathlineto{\pgfpoint{\bmb@dimb}{\bmb@dima}} + \pgfusepath{stroke} + \advance\bmb@dima by 1.5bp% + \advance\bmb@temp by 1bp% + \color{bg} + \pgfpathmoveto{\pgfpoint{\bmb@dimb}{\bmb@temp}} + \pgfpathlineto{\pgfpoint{\bmb@dimb}{\bmb@dima}} + \pgfusepath{stroke} + } + \fi + \end{pgfpicture}% + \box\bmb@box% + }% + \ifbmb@shadow% + \vskip4bp minus 2bp% + \else% + \vskip2bp% + \fi% + \egroup% of \vbox\bgroup +} + + +%% ------------------------------- +%% | /halbgerundete Beamer-Boxen | +%% ------------------------------- + + +%% ---------------------- +%% | Block-Definitionen | +%% ---------------------- + +% Content environment for structuring. Basically a headline followed by text +\newenvironment<>{contentblock}[1]{\begingroup% + \setbeamertemplate{blocks}[default] + \setbeamercolor{block body}{fg=black,bg=}% + \setbeamercolor{block title}{fg=black,bg=}% + \setbeamerfont*{block title}{family=\sffamily,series=\bfseries,size=\large} + \begin{block}#2{#1}% + }{\end{block}% +\endgroup} + + +\newenvironment<>{greenblock}[1]{\begingroup% + \setbeamercolor{block body}{fg=black,bg=kit-green15}% + \setbeamercolor{block title}{fg=white,bg=kit-green100}% + \begin{block}#2{#1}% + }{\end{block} +\endgroup} + +\newenvironment<>{blueblock}[1]{\begingroup% + \setbeamercolor{block body}{fg=black,bg=kit-blue15}% + \setbeamercolor{block title}{fg=white,bg=kit-blue100}% + \setbeamercolor{itemize item}{fg=kit-blue100} + \begin{block}#2{#1}% + }{\end{block} +\endgroup} + +\newenvironment<>{redblock}[1]{\begingroup% + \setbeamercolor{block body}{fg=black,bg=kit-red15}% + \setbeamercolor{block title}{fg=white,bg=kit-red100}% + \setbeamercolor{itemize item}{fg=kit-red100}% + \begin{block}#2{#1}% + }{\end{block}% +\endgroup} + +\newenvironment<>{brownblock}[1]{\begingroup% + \setbeamercolor{block body}{fg=black,bg=kit-brown15}% + \setbeamercolor{block title}{fg=white,bg=kit-brown100}% + \setbeamercolor{itemize item}{fg=kit-brown100}% + \begin{block}#2{#1}% + }{\end{block}% +\endgroup} + +\newenvironment<>{purpleblock}[1]{\begingroup% + \setbeamercolor{block body}{fg=black,bg=kit-purple15}% + \setbeamercolor{block title}{fg=white,bg=kit-purple100}% + \setbeamercolor{itemize item}{fg=kit-purple100}% + \begin{block}#2{#1}% + }{\end{block}% +\endgroup} + +\newenvironment<>{grayblock}[1]{\begingroup% + \setbeamercolor{block body}{fg=black,bg=kit-gray15}% + \setbeamercolor{block title}{fg=white,bg=kit-gray70}% + \setbeamercolor{itemize item}{fg=kit-gray70}% + \begin{block}#2{#1}% + }{\end{block}% +\endgroup} + +\newenvironment<>{yellowblock}[1]{\begingroup% + \setbeamercolor{block body}{fg=black,bg=kit-yellow30}% + \setbeamercolor{block title}{fg=black,bg=kit-yellow100}% + \setbeamercolor{itemize item}{fg=kit-yellow100}% + \begin{block}#2{#1}% + }{\end{block}% +\endgroup} + +\newenvironment<>{lightgreenblock}[1]{\begingroup% + \setbeamercolor{block body}{fg=black,bg=kit-lightgreen15}% + \setbeamercolor{block title}{fg=white,bg=kit-lightgreen100}% + \setbeamercolor{itemize item}{fg=kit-lightgreen100}% + \begin{block}#2{#1}% + }{\end{block}% +\endgroup} + +\newenvironment<>{orangeblock}[1]{\begingroup% + \setbeamercolor{block body}{fg=black,bg=kit-orange15}% + \setbeamercolor{block title}{fg=white,bg=kit-orange100}% + \setbeamercolor{itemize item}{fg=kit-orange100}% + \begin{block}#2{#1}% + }{\end{block}% +\endgroup} + +\newenvironment<>{cyanblock}[1]{\begingroup% + \setbeamercolor{block body}{fg=black,bg=kit-cyan15}% + \setbeamercolor{block title}{fg=white,bg=kit-cyan100}% + \setbeamercolor{itemize item}{fg=kit-cyan100}% + \begin{block}#2{#1}% + }{\end{block}% +\endgroup} + +\newenvironment<>{maroonblock}[1]{\begingroup% +\setbeamercolor{block body}{fg=black,bg=Maroon!15}% +\setbeamercolor{block title}{fg=white,bg=Maroon}% + \setbeamercolor{itemize item}{fg=Maroon}% +\begin{block}#2{#1}% +}{\end{block}% +\endgroup} +%% ------------------------------ +%% | /Block-Definitionen | +%% ------------------------------ + +%% use this for setting the total page number +\newcommand{\beginbackup}{ + \newcounter{framenumbervorappendix} + \setcounter{framenumbervorappendix}{\value{framenumber}} +} +\newcommand{\backupend}{ + \addtocounter{framenumbervorappendix}{-\value{framenumber}} + \addtocounter{framenumber}{\value{framenumbervorappendix}} +} diff --git a/11-entwurfsheft-kolloquium/slides/apiExtension.tex b/11-entwurfsheft-kolloquium/slides/apiExtension.tex new file mode 100644 index 0000000..df1c027 --- /dev/null +++ b/11-entwurfsheft-kolloquium/slides/apiExtension.tex @@ -0,0 +1,39 @@ +\begin{frame}{API Erweiterung} + + \begin{columns}[t] + \begin{column}{.3\textwidth} + \centering{\fontsize{30pt}{36pt}\selectfont\faLock} + \vspace{.2cm} + \begin{orangeblock}{Authentication} + \begin{itemize} + \item Registrierung + \item Passwort ändern + \item Passwort vergessen + \item Passwort zurücksetzen + \item Account löschen + \end{itemize} + \end{orangeblock} + \end{column} + \begin{column}{.3\textwidth} + \centering{\fontsize{30pt}{36pt}\selectfont\faDownload} + \vspace{.2cm} + \begin{greenblock}{Subscriptions} + \begin{itemize} + \item Abrufen der Informationen aller Abonnements + \end{itemize} + \end{greenblock} + \end{column} + \begin{column}{.3\textwidth} + \centering{\fontsize{30pt}{36pt}\selectfont\faSpinner} + \vspace{.2cm} + \begin{blueblock}{Episode Actions} + \begin{itemize} + \item Episode Actions hochladen + \item Episode Actions abrufen + \end{itemize} + \end{blueblock} + \end{column} + \end{columns} + +\end{frame} + diff --git a/11-entwurfsheft-kolloquium/slides/architecture.tex b/11-entwurfsheft-kolloquium/slides/architecture.tex new file mode 100644 index 0000000..edd9f1b --- /dev/null +++ b/11-entwurfsheft-kolloquium/slides/architecture.tex @@ -0,0 +1,7 @@ +\begin{frame}{Architektur} + + \begin{figure}[h] + \includegraphics[width=0.73\textwidth]{assets/diagrams/backendComponentDiagram} + \end{figure} + +\end{frame} \ No newline at end of file diff --git a/11-entwurfsheft-kolloquium/slides/changes.tex b/11-entwurfsheft-kolloquium/slides/changes.tex new file mode 100644 index 0000000..b3ba6de --- /dev/null +++ b/11-entwurfsheft-kolloquium/slides/changes.tex @@ -0,0 +1,22 @@ +\begin{frame}[t]{Änderungen Pflichtenheft} + + \begin{block}{Neue Kriterien} + \begin{itemize} + \item \textlangle RS11\textrangle \, Cooldown Fetch-Vorgang + \item \textlangle RC13\textrangle \, Sprache von Browserinformationen + \end{itemize} + \end{block} + + \begin{blueblock}{Abgeänderte Kriterien} + \begin{itemize} + \item \textlangle RS10\textrangle \, Sessions mit JSON Web Token + \end{itemize} + \end{blueblock} + + \begin{maroonblock}{Entfernte Kriterien} + \begin{itemize} + \item \textlangle RC5\textrangle \, Frontend kompatibel mit beliebigen Gpodder APIs + \end{itemize} + \end{maroonblock} + +\end{frame} diff --git a/11-entwurfsheft-kolloquium/slides/classdiagram.tex b/11-entwurfsheft-kolloquium/slides/classdiagram.tex new file mode 100644 index 0000000..aeecec0 --- /dev/null +++ b/11-entwurfsheft-kolloquium/slides/classdiagram.tex @@ -0,0 +1,57 @@ +\begin{frame}[t]{Überblick: Klassendiagramm} + \centering + \includegraphics[width=\textwidth]{assets/diagrams/classdiagram} + \vspace{.3cm} + \begin{greenblock}{Abhängigkeiten} + \vspace{-.2cm} + \begin{columns} + \begin{column}{.4\textwidth} + \begin{itemize} + \item Spring Web + \item Spring Security + \item Spring Mail Sender + \end{itemize} + \end{column} + \begin{column}{.4\textwidth} + \begin{itemize} + \item Spring Data JPA + \item Lombok + \item Rome (RSS parsing/fetching) + \end{itemize} + \end{column} + \end{columns} + + \end{greenblock} +\end{frame} + +% sub +% episode +% auth +% model +% util + +\begin{frame}[t]{Subscriptions-API} + \centering + \includegraphics[width=\textwidth]{assets/diagrams/classdiagram-subscriptions} +\end{frame} + +\begin{frame}[t]{EpisodeActions-API} + \centering + \includegraphics[width=\textwidth]{assets/diagrams/classdiagram-episode-actions} +\end{frame} + +\begin{frame}[t]{Authentication-API} + \centering + \includegraphics[width=.73\textwidth]{assets/diagrams/classdiagram-authentication} +\end{frame} + +\begin{frame}[t]{Model-Paket} + \centering + \includegraphics[width=\textwidth]{assets/diagrams/classdiagram-model} +\end{frame} + +\begin{frame}[t]{Util-Paket} + \centering + \includegraphics[width=\textwidth]{assets/diagrams/classdiagram-util} +\end{frame} + diff --git a/11-entwurfsheft-kolloquium/slides/frontend.tex b/11-entwurfsheft-kolloquium/slides/frontend.tex new file mode 100644 index 0000000..c49d94f --- /dev/null +++ b/11-entwurfsheft-kolloquium/slides/frontend.tex @@ -0,0 +1,21 @@ +\begin{frame}{Weboberfläche} + \begin{minipage}{.77\textwidth} + \centering + \includegraphics[width=\textwidth]{assets/diagrams/componentdiagram} + \end{minipage} + \hspace{.3cm} + \begin{minipage}{.18\textwidth} + \begin{greenblock}{Abhängigkeiten} + \begin{itemize} + \item vite + \item vue + \item vue-router + \item Pinia + \item bootstrap + \item fontawesome + \item vue-i18n + \end{itemize} + \end{greenblock} + \end{minipage} +\end{frame} + diff --git a/11-entwurfsheft-kolloquium/slides/pattern.tex b/11-entwurfsheft-kolloquium/slides/pattern.tex new file mode 100644 index 0000000..1f6366e --- /dev/null +++ b/11-entwurfsheft-kolloquium/slides/pattern.tex @@ -0,0 +1,26 @@ +\begin{frame}{Entwurfsmuster} + \begin{columns}[t] + \column{.5\textwidth} + \centering{\fontsize{30pt}{36pt}\selectfont\faSyringe} + \begin{greenblock}{Dependency Injection} + \begin{itemize} + \item Bestimmt Abhängigkeiten in Klassen + \item Speichert Instanzen dieser Abhängigkeiten an zentralem Ort + % Wichtig für Interface Abhängigkeiten: + \item Instanz in Konfiguration festlegbar + \item Setzt Instanzen während der Laufzeit an benötigter Stelle ein + \end{itemize} + \end{greenblock} + \column{.5\textwidth} + \centering{\fontsize{30pt}{36pt}\selectfont\faDatabase} + \begin{blueblock}{Data Access Object (DAO)} + \color{kit-blue100} + \begin{itemize} + \item DAO-Interface als Schnittstelle zwischen Geschäftslogik und Datenzugriffslogik + \item DAO-Implementierungen als Varianten der Datenzugriffslogik (MariaDB, MySQL, etc.) + \item[$\Rightarrow$] Austauschbare Implementierungen + \end{itemize} + \end{blueblock} + \end{columns} +\end{frame} + diff --git a/11-entwurfsheft-kolloquium/slides/sequence.tex b/11-entwurfsheft-kolloquium/slides/sequence.tex new file mode 100644 index 0000000..393e502 --- /dev/null +++ b/11-entwurfsheft-kolloquium/slides/sequence.tex @@ -0,0 +1,7 @@ +\begin{frame}{Abläufe: Abrufen aller Abonnements} + + \begin{figure}[h] + \includegraphics[width=0.83\textwidth]{assets/diagrams/sequencediagram-getSubscriptions} + \end{figure} + +\end{frame} \ No newline at end of file diff --git a/20-implementierungsheft/.gitignore b/20-implementierungsheft/.gitignore new file mode 100644 index 0000000..b1b3dda --- /dev/null +++ b/20-implementierungsheft/.gitignore @@ -0,0 +1,305 @@ +## Core latex/pdflatex auxiliary files: +*.aux +*.lof +*.log +*.lot +*.fls +*.out +*.toc +*.fmt +*.fot +*.cb +*.cb2 +.*.lb + +## Intermediate documents: +*.dvi +*.xdv +*-converted-to.* +# these rules might exclude image files for figures etc. +# *.ps +# *.eps +*.pdf +!assets/*.pdf + +## Generated if empty string is given at "Please type another file name for output:" +.pdf + +## Bibliography auxiliary files (bibtex/biblatex/biber): +*.bbl +*.bcf +*.blg +*-blx.aux +*-blx.bib +*.run.xml + +## Build tool auxiliary files: +*.fdb_latexmk +*.synctex +*.synctex(busy) +*.synctex.gz +*.synctex.gz(busy) +*.pdfsync + +## Build tool directories for auxiliary files +# latexrun +latex.out/ + +## Auxiliary and intermediate files from other packages: +# algorithms +*.alg +*.loa + +# achemso +acs-*.bib + +# amsthm +*.thm + +# beamer +*.nav +*.pre +*.snm +*.vrb + +# changes +*.soc + +# comment +*.cut + +# cprotect +*.cpt + +# elsarticle (documentclass of Elsevier journals) +*.spl + +# endnotes +*.ent + +# fixme +*.lox + +# feynmf/feynmp +*.mf +*.mp +*.t[1-9] +*.t[1-9][0-9] +*.tfm + +#(r)(e)ledmac/(r)(e)ledpar +*.end +*.?end +*.[1-9] +*.[1-9][0-9] +*.[1-9][0-9][0-9] +*.[1-9]R +*.[1-9][0-9]R +*.[1-9][0-9][0-9]R +*.eledsec[1-9] +*.eledsec[1-9]R +*.eledsec[1-9][0-9] +*.eledsec[1-9][0-9]R +*.eledsec[1-9][0-9][0-9] +*.eledsec[1-9][0-9][0-9]R + +# glossaries +*.acn +*.acr +*.glg +*.glo +*.gls +*.glsdefs +*.lzo +*.lzs +*.slg +*.slo +*.sls + +# uncomment this for glossaries-extra (will ignore makeindex's style files!) +*.ist + +# gnuplot +*.gnuplot +*.table + +# gnuplottex +*-gnuplottex-* + +# gregoriotex +*.gaux +*.glog +*.gtex + +# htlatex +*.4ct +*.4tc +*.idv +*.lg +*.trc +*.xref + +# hyperref +*.brf + +# knitr +*-concordance.tex +# TODO Uncomment the next line if you use knitr and want to ignore its generated tikz files +# *.tikz +*-tikzDictionary + +# listings +*.lol + +# luatexja-ruby +*.ltjruby + +# makeidx +*.idx +*.ilg +*.ind + +# minitoc +*.maf +*.mlf +*.mlt +*.mtc[0-9]* +*.slf[0-9]* +*.slt[0-9]* +*.stc[0-9]* + +# minted +_minted* +*.pyg + +# morewrites +*.mw + +# newpax +*.newpax + +# nomencl +*.nlg +*.nlo +*.nls + +# pax +*.pax + +# pdfpcnotes +*.pdfpc + +# sagetex +*.sagetex.sage +*.sagetex.py +*.sagetex.scmd + +# scrwfile +*.wrt + +# svg +svg-inkscape/ + +# sympy +*.sout +*.sympy +sympy-plots-for-*.tex/ + +# pdfcomment +*.upa +*.upb + +# pythontex +*.pytxcode +pythontex-files-*/ + +# tcolorbox +*.listing + +# thmtools +*.loe + +# TikZ & PGF +*.dpth +*.md5 +*.auxlock + +# titletoc +*.ptc + +# todonotes +*.tdo + +# vhistory +*.hst +*.ver + +# easy-todo +*.lod + +# xcolor +*.xcp + +# xmpincl +*.xmpi + +# xindy +*.xdy + +# xypic precompiled matrices and outlines +*.xyc +*.xyd + +# endfloat +*.ttt +*.fff + +# Latexian +TSWLatexianTemp* + +## Editors: +# WinEdt +*.bak +*.sav + +# Texpad +.texpadtmp + +# LyX +*.lyx~ + +# Kile +*.backup + +# gummi +.*.swp + +# KBibTeX +*~[0-9]* + +# TeXnicCenter +*.tps + +# auto folder when using emacs and auctex +./auto/* +*.el + +# expex forward references with \gathertags +*-tags.tex + +# standalone packages +*.sta + +# Makeindex log files +*.lpz + +# xwatermark package +*.xwm + +# REVTeX puts footnotes in the bibliography by default, unless the nofootinbib +# option is specified. Footnotes are the stored in a file with suffix Notes.bib. +# Uncomment the next line to have this generated file ignored. +#*Notes.bib + +# Gannt Diagramm als EPS ignorieren +assets/gantt.eps \ No newline at end of file diff --git a/20-implementierungsheft/.gitlab-ci.yml b/20-implementierungsheft/.gitlab-ci.yml new file mode 100644 index 0000000..27d0617 --- /dev/null +++ b/20-implementierungsheft/.gitlab-ci.yml @@ -0,0 +1,36 @@ +plantuml: + stage: .pre + image: + name: plantuml/plantuml + entrypoint: [""] + script: + - java -jar plantuml.jar -tpdf assets/diagrams/*.puml + artifacts: + paths: + - assets + +tex: + stage: build + image: texlive/texlive + script: + - mkdir public + - make tex + - mv *.pdf public + artifacts: + paths: + - public + dependencies: + - plantuml + +pages: + stage: deploy + script: + - echo Hello, World! + artifacts: + paths: + - public + rules: + - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH + dependencies: + - tex + diff --git a/20-implementierungsheft/.latexmkrc b/20-implementierungsheft/.latexmkrc new file mode 100644 index 0000000..bec2b38 --- /dev/null +++ b/20-implementierungsheft/.latexmkrc @@ -0,0 +1,7 @@ +# https://tex.stackexchange.com/questions/1226/how-to-make-latexmk-use-makeglossaries +add_cus_dep('glo', 'gls', 0, 'makeglo2gls'); +add_cus_dep('acn', 'acr', 0, 'makeglo2gls'); +sub makeglo2gls { + system("makeglossaries $_[0]"); +} + diff --git a/20-implementierungsheft/Makefile b/20-implementierungsheft/Makefile new file mode 100644 index 0000000..335b123 --- /dev/null +++ b/20-implementierungsheft/Makefile @@ -0,0 +1,18 @@ +MAIN = implementierungsheft +FLAGS = -pdf + +all: clean compile +compile: diagram tex +clean: clean-diagram clean-tex + +dev: + latexmk $(FLAGS) -pvc $(MAIN) +tex: + latexmk $(FLAGS) $(MAIN) +diagram: + java -jar plantuml.jar -tpdf assets/diagrams/*.puml +clean-tex: + latexmk -C +clean-diagram: + find assets/diagrams -type f -not -name '*.puml' -delete + diff --git a/20-implementierungsheft/README.md b/20-implementierungsheft/README.md new file mode 100644 index 0000000..2c08f29 --- /dev/null +++ b/20-implementierungsheft/README.md @@ -0,0 +1,31 @@ +# Entwurfsheft + +> Systemdesign und -spezifikation + +## Diagramme + +Installiere [PlantUML](https://plantuml.com/starting) (oder über einen Paketmanager). + +Arbeiten an Diagrammen mit Echtzeit-Vorschau (Anzeige wird beim Speichern der +puml-Datei aktualisiert): +```sh +java -jar plantuml.jar -gui assets/classdiagram.puml + +# bzw (wenn zu PATH hinzugefügt oder Linux) +plantuml -gui assets/classdiagram.puml +``` + +Bauen der Diagramme: +```sh +java -jar plantuml.jar -teps assets/*.puml + +# bzw (wenn zu PATH hinzugefügt oder Linux) +plantuml -teps assets/*.puml + +# bzw über Makefile (Linux) +make diagram + +# oder zum Erstellen von Diagrammen und LaTeX: +make +``` + diff --git a/20-implementierungsheft/assets/.gitignore b/20-implementierungsheft/assets/.gitignore new file mode 100644 index 0000000..16252a4 --- /dev/null +++ b/20-implementierungsheft/assets/.gitignore @@ -0,0 +1,3 @@ +diagrams/* +!diagrams/*.puml + diff --git a/20-implementierungsheft/assets/KIT_Deckblatt.pdf b/20-implementierungsheft/assets/KIT_Deckblatt.pdf new file mode 100644 index 0000000..7de8ed4 Binary files /dev/null and b/20-implementierungsheft/assets/KIT_Deckblatt.pdf differ diff --git a/20-implementierungsheft/assets/diagrams/backendComponentDiagram.puml b/20-implementierungsheft/assets/diagrams/backendComponentDiagram.puml new file mode 100644 index 0000000..806522c --- /dev/null +++ b/20-implementierungsheft/assets/diagrams/backendComponentDiagram.puml @@ -0,0 +1,61 @@ +@startuml +' skinparam linetype ortho + +'######################################################################### +'SubscriptionsAPI +component SubscriptionsAPI { + + component SubscriptionService + component SubscriptionController + component SubscriptionDataAccessLayer + + portout "Webserver" as wSub + portin "Database" as dSub + } + +dSub --0)- SubscriptionDataAccessLayer +SubscriptionDataAccessLayer --0)- SubscriptionService +SubscriptionService --0)- SubscriptionController +SubscriptionController --0)- wSub + +'######################################################################### + + +'######################################################################### +'EpisodeActionsAPI + +component EpisodeActionsAPI { + component EpisodeActionService + component EpisodeActionController + component EpisodeActionDataAccessLayer + + portout "Webserver" as wEpisode + portin "Database" as dEpisode +} + +dEpisode --0)- EpisodeActionDataAccessLayer +EpisodeActionController --0)- wEpisode +EpisodeActionDataAccessLayer --0)- EpisodeActionService +EpisodeActionService --0)- EpisodeActionController + +'######################################################################### + + +'######################################################################### +'AuthenticationAPI + +component AuthenticationAPI { + component AuthenticationService + component AuthenticationController + component AuthenticationDataAccessLayer + + portout "Webserver" as wAuth + portin "Database" as dAuth +} + +dAuth --0)- AuthenticationDataAccessLayer +AuthenticationController --0)- wAuth +AuthenticationDataAccessLayer --0)- AuthenticationService +AuthenticationService --0)- AuthenticationController + +@enduml diff --git a/20-implementierungsheft/assets/diagrams/class_after.puml b/20-implementierungsheft/assets/diagrams/class_after.puml new file mode 100644 index 0000000..0a8f475 --- /dev/null +++ b/20-implementierungsheft/assets/diagrams/class_after.puml @@ -0,0 +1,574 @@ +@startuml +' skinparam linetype ortho +' skinparam groupInheritance 2 +allowmixing + +package authentication_api <> { + + package controller as auth.controller <> { + + class AuthenticationController <<@RestController>> { + + <> AuthenticationController(AuthenticationService, EMailConfigProperties): + + logout(String, HttpServletResponse): HttpStatus + + registerUser(UserInfoRequest): HttpStatus + + forgotPassword(String): HttpStatus + + getDeviceList(String, HttpServletResponse): ResponseEntity> + + login(String, HttpServletResponse): HttpStatus + + deleteUser(String, PasswordRequest): HttpStatus + + changePassword(String, ChangePasswordRequest): HttpStatus + + verifyRegistration(String, String, HttpServletResponse): HttpStatus + + resetPassword(String, String, PasswordRequest): HttpStatus + } + + entity AuthenticationResponse << record >> { + + <> AuthenticationResponse(String): + + token(): String + } + + entity ChangePasswordRequest << record >> { + + <> ChangePasswordRequest(String, String): + + oldPassword(): String + + newPassword(): String + } + + entity DeviceWrapper << record >> { + + <> DeviceWrapper(): + + <> DeviceWrapper(String, String, String, int): + + id(): String + + caption(): String + + type(): String + + subscriptions(): int + } + + entity ForgotPasswordRequest << record >> { + + <> ForgotPasswordRequest(String): + + email(): String + } + + entity PasswordRequest << record >> { + + <> PasswordRequest(String): + + password(): String + } + + entity UserInfoRequest << record >> { + + <> UserInfoRequest(String, String, String): + + password(): String + + email(): String + + username(): String + } + } + + package data_access as auth.dao <> { + + interface AuthenticationDao <<@Repository>> { + + findByEmail(String): Optional + + deleteAllByEnabledFalseAndCreatedAtLessThan(long): void + + existsByUsername(String): boolean + + findByUsername(String): Optional + } + } + + package service as auth.service <> { + + class AuthenticationService <<@Service>> { + + <> AuthenticationService(AuthenticationDao, PasswordEncoder, JwtService, EMailServiceImpl, EncryptionService, InputCheckService): + + verifyRegistration(String, String): HttpStatus + + logout(String, HttpServletResponse): HttpStatus + + changePassword(String, ChangePasswordRequest): HttpStatus + + registerUser(UserInfoRequest): HttpStatus + + deleteUser(String, PasswordRequest): HttpStatus + + forgotPassword(String): HttpStatus + + resetPassword(String, String, PasswordRequest): HttpStatus + + deleteInvalidUsersOlderThan(long): void + + login(String, HttpServletResponse): HttpStatus + } + + class EMailServiceImpl <<@Service>> { + + <> EMailServiceImpl(JavaMailSender, EMailConfigProperties, JwtService): + - substitutePlaceholders(String, UserDetails, String): String + + sendVerification(String, UserDetails): void + + sendPasswordReset(String, UserDetails): void + - generatePasswordResetURLString(UserDetails): String + - sendMail(String, String, String): void + - generateVerificationURLString(UserDetails): String + } + + class EncryptionService <<@Service>> { + + <> EncryptionService(SecurityConfigProperties): + + saltAndHashEmail(String): String + - getSalt(): byte[] + } + + class InputCheckService <<@Service>> { + + <> InputCheckService(): + + validateEmail(String): boolean + + validateUsername(String): boolean + + validatePassword(String): boolean + } + + class ResourceReader { + + <> ResourceReader(): + + readFileToString(String): String + } + } +} + +package config <> { + + class ApplicationConfig <<@Configuration>> { + + <> ApplicationConfig(AuthenticationDao): + + userDetailsService(): UserDetailsService + + addInterceptors(InterceptorRegistry): void + + authenticationManager(AuthenticationConfiguration): AuthenticationManager + + passwordEncoder(): PasswordEncoder + + corsConfigurer(): WebMvcConfigurer + + authenticationProvider(): AuthenticationProvider + } + + class AuthenticationValidatorInterceptor { + + <> AuthenticationValidatorInterceptor(): + - extractUsernamePathVariable(HttpServletRequest): String? + + preHandle(HttpServletRequest, HttpServletResponse, Object): boolean + } + + entity EMailConfigProperties << record >> { + + <> EMailConfigProperties(String, String, String): + + resetUrlPath(): String + + verificationUrl(): String + + dashboardBaseUrl(): String + } + + class JwtAuthenticationFilter <<@Component>> { + + <> JwtAuthenticationFilter(JwtService, UserDetailsService): + # doFilterInternal(HttpServletRequest, HttpServletResponse, FilterChain): void + - authenticateIfValid(Cookie, HttpServletRequest): void + } + + class JwtService <<@Service>> { + + <> JwtService(SecurityConfigProperties): + + generateUrlTokenString(UserDetails): String + + isTokenValid(String, UserDetails): boolean + + generateAccessTokenString(UserDetails): String + + extractUsername(String): String + + extractClaim(String, Function): T + - isTokenExpired(String): boolean + + generateTokenString(Map, UserDetails, long): String + - extractExpiration(String): Date + - extractAllClaims(String): Claims + - getSigningKey(): Key + } + + class SecurityConfig <<@Configuration>> { + + <> SecurityConfig(JwtAuthenticationFilter, AuthenticationProvider): + ~ corsConfigurationSource(): CorsConfigurationSource + + securityFilterChain(HttpSecurity): SecurityFilterChain + } + + entity SecurityConfigProperties << record >> { + + <> SecurityConfigProperties(String, String): + + jwtSigningKey(): String + + emailSigningKey(): String + } +} + +package episode_actions_api <> { + + package controller as episode.controller <> { + + class EpisodeActionController <<@RestController>> { + + <> EpisodeActionController(EpisodeActionService): + + addEpisodeActions(String, List): ResponseEntity + + getEpisodeActionsOfPodcast(String, String): ResponseEntity + + getEpisodeActions(String): ResponseEntity + + getEpisodeActionsSince(String, long): ResponseEntity + + getEpisodeActionsOfPodcastSince(String, String, long): ResponseEntity + } + + class EpisodeActionGetResponse { + + <> EpisodeActionGetResponse(List): + + getTimestamp(): long + + getActions(): List + } + + class EpisodeActionPost { + + <> EpisodeActionPost(): + + <> EpisodeActionPost(String, String, String, String, int, EpisodeAction): + + equals(Object): boolean + + hashCode(): int + + builder(): EpisodeActionPostBuilder + + getPodcastURL(): String + + getEpisodeURL(): String + + getTitle(): String + + getGuid(): String + + setGuid(String): void + + setEpisodeAction(EpisodeAction): void + + setTotal(int): void + + getTotal(): int + + setTitle(String): void + # canEqual(Object): boolean + + getEpisodeAction(): EpisodeAction + + toString(): String + + setPodcastURL(String): void + + setEpisodeURL(String): void + } + } + + package data_access as episode.dao <> { + + interface EpisodeActionDao <<@Repository>> { + + findByUserUsernameAndEpisodeSubscriptionUrl(String, String): List + + delete(EpisodeAction): void + + findByUserUsernameAndTimestampGreaterThanEqual(String, LocalDateTime): List + + findByUserUsername(String): List + + findByUserAndEpisodeUrlAndAction(User, String, Action): Optional + + findByUserUsernameAndTimestampGreaterThanEqualAndEpisodeSubscriptionUrl(String, LocalDateTime, String): List + + deleteByUserUsernameAndEpisodeSubscriptionUrl(String, String): void + + existsByUserAndEpisodeUrlAndAction(User, String, Action): boolean + } + + interface EpisodeDao <<@Repository>> { + + existsByGuid(String): boolean + + findByUrl(String): Optional + + existsByUrl(String): boolean + + findByGuid(String): Optional + } + } + + package service as episode.service <> { + + class EpisodeActionService <<@Service>> { + + <> EpisodeActionService(EpisodeActionDao, EpisodeDao, AuthenticationDao, SubscriptionDao, SubscriptionActionDao, RSSParser): + - episodeActionPostsToEpisodeActions(User, List): List + - createEpisode(EpisodeActionPost): Episode + + getEpisodeActionsOfPodcastSince(String, String, long): List + - getEpisodeFromDatabase(EpisodeActionPost): Episode + - addEpisodeActionsToDatabase(User, List): void + - addNewestEpisodeActionToDatabase(User, EpisodeAction): void + - episodeActionsToEpisodeActionPosts(List): List + - addEpisodeActionToDatabase(User, EpisodeAction): void + + getEpisodeActionsSince(String, long): List + - episodeActionPostToEpisodeAction(User, EpisodeActionPost): EpisodeAction + + getEpisodeActions(String): List + + getEpisodeActionsOfPodcast(String, String): List + + addEpisodeActions(String, List): void + } + } +} + +package model <> { + + enum Action << enumeration >> { + + <> Action(): + + valueOf(String): Action + + getJsonProperty(): String + + values(): Action[] + } + + class Episode <<@Entity>> { + + <> Episode(): + + <> Episode(Long, String, String, String, int, Subscription, List): + + getId(): Long + # canEqual(Object): boolean + + getGuid(): String + + setTitle(String): void + + builder(): EpisodeBuilder + + setTotal(int): void + + setGuid(String): void + + equals(Object): boolean + + setSubscription(Subscription): void + + getUrl(): String + + getTitle(): String + + toString(): String + + getTotal(): int + + setUrl(String): void + + setEpisodeActions(List): void + + getSubscription(): Subscription + + getEpisodeActions(): List + + setId(Long): void + + hashCode(): int + } + + class EpisodeAction <<@Entity>> { + + <> EpisodeAction(Long, User, Episode, LocalDateTime, Action, int, int): + + <> EpisodeAction(): + + getEpisode(): Episode + + setEpisode(Episode): void + + setPosition(int): void + # canEqual(Object): boolean + + setUser(User): void + + setStarted(int): void + + getId(): Long + + getUser(): User + + getTimestamp(): LocalDateTime + + getAction(): Action + + getStarted(): int + + hashCode(): int + + setTimestamp(LocalDateTime): void + + equals(Object): boolean + + getPosition(): int + + builder(): EpisodeActionBuilder + + setId(Long): void + + setAction(Action): void + + toString(): String + + toEpisodeActionPost(): EpisodeActionPost + } + + enum Role << enumeration >> { + + <> Role(): + + valueOf(String): Role + + toString(): String + + values(): Role[] + } + + class Subscription <<@Entity>> { + + <> Subscription(): + + <> Subscription(Long, String, String, long, List, List): + + setId(Long): void + + getId(): Long + + equals(Object): boolean + + getUrl(): String + + hashCode(): int + + builder(): SubscriptionBuilder + # canEqual(Object): boolean + + toString(): String + + getTitle(): String + + getTimestamp(): long + + setEpisodes(List): void + + getSubscriptionActions(): List + + getEpisodes(): List + + setSubscriptionActions(List): void + + setUrl(String): void + + setTitle(String): void + + setTimestamp(long): void + + addEpisode(Episode): void + } + + class SubscriptionAction <<@Entity>> { + + <> SubscriptionAction(): + + <> SubscriptionAction(int, User, long, Subscription, boolean): + + equals(Object): boolean + + getId(): int + + getUser(): User + + getTimestamp(): long + + getSubscription(): Subscription + + isAdded(): boolean + # canEqual(Object): boolean + + setId(int): void + + hashCode(): int + + setUser(User): void + + toString(): String + + builder(): SubscriptionActionBuilder + + setTimestamp(long): void + + setSubscription(Subscription): void + + setAdded(boolean): void + } + + class User <<@Entity>> { + + <> User(Long, String, String, String, boolean, long, Role, List, List): + + <> User(): + + getId(): Long + + setCreatedAt(long): void + + getUsername(): String + + builder(): UserBuilder + + toString(): String + + getEmail(): String + + setPassword(String): void + + setSubscriptionActions(List): void + + equals(Object): boolean + + getPassword(): String + + setEmail(String): void + + setRole(Role): void + + isEnabled(): boolean + + setUsername(String): void + + getCreatedAt(): long + + getRole(): Role + + getSubscriptionActions(): List + # canEqual(Object): boolean + + hashCode(): int + + setEnabled(boolean): void + + setEpisodeActions(List): void + + getEpisodeActions(): List + + setId(Long): void + + getAuthorities(): Collection + + isCredentialsNonExpired(): boolean + + isAccountNonLocked(): boolean + + isAccountNonExpired(): boolean + } +} + +package subscriptions_api <> { + + package controller as subscription.controller <> { + + class SubscriptionController <<@RestController>> { + + <> SubscriptionController(SubscriptionService): + + applySubscriptionDelta(String, String, SubscriptionDelta): ResponseEntity + + getSubscriptions(String, String, String): ResponseEntity> + + getSubscriptionDelta(String, String, long): ResponseEntity + + getTitles(String): ResponseEntity> + + uploadSubscriptions(String, String, List): ResponseEntity + } + + class SubscriptionDelta { + + <> SubscriptionDelta(List, List): + + getTimestamp(): long + + getRemove(): List + + getAdd(): List + } + + entity SubscriptionTitles << record >> { + + <> SubscriptionTitles(Subscription, List): + + episodes(): List + + subscription(): Subscription + } + } + + package data_access as subscription.dao <> { + + interface SubscriptionActionDao <<@Repository>> { + + findByUserUsernameAndAddedTrue(String): List + + existsByUserAndSubscription(User, Subscription): boolean + + findByUserAndSubscription(User, Subscription): Optional + + findByUserUsernameAndTimestampGreaterThanEqual(String, long): List + + findByUserUsernameAndAddedTrueAndTimestampGreaterThanEqual(String, long): List + } + + interface SubscriptionDao <<@Repository>> { + + findByUrl(String): Optional + + existsByUrl(String): boolean + } + } + + package service as subscription.service <> { + + class SubscriptionService <<@Service>> { + + <> SubscriptionService(RSSParser, AuthenticationDao, SubscriptionDao, SubscriptionActionDao, EpisodeActionDao, EpisodeActionService): + + getTitles(String): List + + getSubscriptions(String): List + + applySubscriptionDelta(String, SubscriptionDelta): int + + getSubscriptionDelta(String, long): SubscriptionDelta + + uploadSubscriptions(String, List): int + } + } +} + +package util <> { + + class RSSParser <<@Component>> { + + <> RSSParser(EpisodeDao, SubscriptionDao): + + validate(Subscription): void + - parseTimeToSeconds(String): int + - parseEpisode(SyndEntry, Subscription): Episode + - saveEpisodes(List): void + - fetchSubscriptionFeed(Subscription): Map? + - saveSubscription(Subscription): void + - deleteSubscription(Subscription): void + - getFetchedEpisodeForURL(String, Map): Episode + - deleteEpisodes(List): void + } + + class Scheduler <<@Component>> { + + <> Scheduler(): + + clean(): void + } + + class UpdateURLsWrapper { + + <> UpdateURLsWrapper(): + + getTimestamp(): long + + getUpdateURLs(): List> + } +} + +class ServerApplication <<@SpringBootApplication>> { + + <> ServerApplication(): + + main(String[]): void +} + +database Datenbank +Datenbank <-[hidden]d- subscriptions_api +Datenbank <-[hidden]d- episode_actions_api +Datenbank <-[hidden]d- authentication_api +() SQL as SQLSub +() SQL as SQLAuth +() SQL as SQLEpisode + +Datenbank -- SQLSub +Datenbank -- SQLAuth +Datenbank -- SQLEpisode + +SubscriptionController ..o ServerApplication +AuthenticationController ..o ServerApplication +EpisodeActionController ..o ServerApplication + +ServerApplication --() HTTP + +SQLSub )-- SubscriptionActionDao: JPA +SQLSub )-- SubscriptionDao: JPA +SQLAuth )-- AuthenticationDao: JPA +SQLEpisode )-- EpisodeActionDao: JPA +SQLEpisode )-- EpisodeDao: JPA + +model .o Datenbank: ORM (User, SubscriptionAction, Subscription, EpisodeAction, Episode) +' Datenbank o.. Subscription: ORM +' Datenbank o.. SubscriptionAction: ORM +' Datenbank o.. Episode: ORM +' Datenbank o.. EpisodeAction: ORM +' Datenbank o.. User: ORM + +ApplicationConfig "1" *-[#595959,plain]-> "authenticationDao\n1" AuthenticationDao +ApplicationConfig -[#595959,dashed]-> AuthenticationValidatorInterceptor : "«create»" +AuthenticationController "1" *-[#595959,plain]-> "authenticationService\n1" AuthenticationService +AuthenticationController -[#595959,dashed]-> DeviceWrapper : "«create»" +AuthenticationController "1" *-[#595959,plain]-> "eMailConfigProperties\n1" EMailConfigProperties +AuthenticationService "1" *-[#595959,plain]-> "authenticationDao\n1" AuthenticationDao +AuthenticationService "1" *-[#595959,plain]-> "eMailService\n1" EMailServiceImpl +AuthenticationService "1" *-[#595959,plain]-> "encryptionService\n1" EncryptionService +AuthenticationService "1" *-[#595959,plain]-> "inputCheckService\n1" InputCheckService +AuthenticationService "1" *-[#595959,plain]-> "jwtService\n1" JwtService +AuthenticationService "1" *-[#595959,plain]-> "DEFAULT_USER\n1" Role +EMailServiceImpl "1" *-[#595959,plain]-> "eMailConfigProperties\n1" EMailConfigProperties +EMailServiceImpl "1" *-[#595959,plain]-> "jwtService\n1" JwtService +EncryptionService "1" *-[#595959,plain]-> "securityConfigProperties\n1" SecurityConfigProperties +Episode "1" *-[#595959,plain]-> "episodeActions\n*" EpisodeAction +Episode "1" *-[#595959,plain]-> "subscription\n1" Subscription +EpisodeAction "1" *-[#595959,plain]-> "action\n1" Action +EpisodeAction "1" *-[#595959,plain]-> "episode\n1" Episode +EpisodeAction -[#595959,dashed]-> EpisodeActionPost : "«create»" +EpisodeAction "1" *-[#595959,plain]-> "user\n1" User +EpisodeActionController -[#595959,dashed]-> EpisodeActionGetResponse : "«create»" +EpisodeActionController "1" *-[#595959,plain]-> "episodeActionService\n1" EpisodeActionService +EpisodeActionController -[#595959,dashed]-> UpdateURLsWrapper : "«create»" +EpisodeActionGetResponse "1" *-[#595959,plain]-> "actions\n*" EpisodeActionPost +EpisodeActionPost "1" *-[#595959,plain]-> "episodeAction\n1" EpisodeAction +EpisodeActionService "1" *-[#595959,plain]-> "authenticationDao\n1" AuthenticationDao +EpisodeActionService "1" *-[#595959,plain]-> "episodeActionDao\n1" EpisodeActionDao +EpisodeActionService "1" *-[#595959,plain]-> "episodeDao\n1" EpisodeDao +EpisodeActionService "1" *-[#595959,plain]-> "rssParser\n1" RSSParser +EpisodeActionService -[#595959,dashed]-> Subscription : "«create»" +EpisodeActionService "1" *-[#595959,plain]-> "subscriptionActionDao\n1" SubscriptionActionDao +EpisodeActionService "1" *-[#595959,plain]-> "subscriptionDao\n1" SubscriptionDao +JwtAuthenticationFilter "1" *-[#595959,plain]-> "jwtService\n1" JwtService +JwtService "1" *-[#595959,plain]-> "securityConfigProperties\n1" SecurityConfigProperties +RSSParser "1" *-[#595959,plain]-> "episodeDao\n1" EpisodeDao +RSSParser "1" *-[#595959,plain]-> "subscriptionDao\n1" SubscriptionDao +Scheduler "1" *-[#595959,plain]-> "authenticationService\n1" AuthenticationService +SecurityConfig "1" *-[#595959,plain]-> "jwtAuthFilter\n1" JwtAuthenticationFilter +Subscription "1" *-[#595959,plain]-> "episodes\n*" Episode +Subscription "1" *-[#595959,plain]-> "subscriptionActions\n*" SubscriptionAction +SubscriptionAction "1" *-[#595959,plain]-> "subscription\n1" Subscription +SubscriptionAction "1" *-[#595959,plain]-> "user\n1" User +SubscriptionController "1" *-[#595959,plain]-> "subscriptionService\n1" SubscriptionService +SubscriptionController -[#595959,dashed]-> UpdateURLsWrapper : "«create»" +SubscriptionService "1" *-[#595959,plain]-> "authenticationDao\n1" AuthenticationDao +SubscriptionService "1" *-[#595959,plain]-> "episodeActionDao\n1" EpisodeActionDao +SubscriptionService "1" *-[#595959,plain]-> "episodeActionService\n1" EpisodeActionService +SubscriptionService "1" *-[#595959,plain]-> "rssParser\n1" RSSParser +SubscriptionService "1" *-[#595959,plain]-> "subscriptionActionDao\n1" SubscriptionActionDao +SubscriptionService "1" *-[#595959,plain]-> "subscriptionDao\n1" SubscriptionDao +SubscriptionService -[#595959,dashed]-> SubscriptionDelta : "«create»" +SubscriptionService -[#595959,dashed]-> SubscriptionTitles : "«create»" +SubscriptionTitles "1" *-[#595959,plain]-> "subscription\n1" Subscription +User "1" *-[#595959,plain]-> "episodeActions\n*" EpisodeAction +User "1" *-[#595959,plain]-> "role\n1" Role +User "1" *-[#595959,plain]-> "subscriptionActions\n*" SubscriptionAction +@enduml diff --git a/20-implementierungsheft/assets/diagrams/classdiagram.puml b/20-implementierungsheft/assets/diagrams/classdiagram.puml new file mode 100644 index 0000000..4b1970a --- /dev/null +++ b/20-implementierungsheft/assets/diagrams/classdiagram.puml @@ -0,0 +1,463 @@ +@startuml +' skinparam linetype ortho +' skinparam groupInheritance 2 +allowmixing + +package subscriptionsAPI <> { + package subscriptionDataAccessLayer <> { + class SubscriptionDataAccessService <<@Repository>> { + <> SubscriptionDataAccessService(JpaTemplate jpaTemplate) + int uploadSubscriptions(String username, List subscriptions) + List getSubscriptions(String username) + List getSubscriptionsSince(String username, LocalDateTime time) + int addSubscriptions(String username, List addedSubscriptions) + int removeSubscriptions(String username, List removedSubscriptions) + List getTitles(String username) + } + + interface SubscriptionDao { + int uploadSubscriptions(String username, List subscriptions) + List getSubscriptions(String username) + List getSubscriptionsSince(String username, LocalDateTime time) + int addSubscriptions(String username, List addedSubscriptions) + int removeSubscriptions(String username, List removedSubscriptions) + List getTitles(String username) + } + } + + package subscriptionService <> { + class SubscriptionService <<@Service>> { + <> SubscriptionService(SubscriptionDao subscriptionDao) + int uploadSubscriptions(String username, List subscriptions) + List getSubscriptions(String username) + List getSubscriptionsSince(String username, LocalDateTime time) + int addSubscriptions(String username, List addedSubscriptions) + int removeSubscriptions(String username, List removedSubscriptions) + List getTitles(String username) + } + } + + package subscriptionController <> { + class SubscriptionController <<@Controller>>{ + ' @Autowired + <> SubscriptionController(SubscriptionService subscriptionService) + ' @GetMapping + ResponseEntity> getSubscriptions(String username, String deviceID, String functionJSONP) + ' @PutMapping + ResponseEntity uploadSubscriptions(String username, String deviceID, List subscriptions) + ' @PostMapping + ResponseEntity applySubscriptionDelta(String username, String deviceID, SubscriptionDelta delta) + ' @GetMapping + ResponseEntity getSubscriptionDelta(String username, String deviceID, long since) + ResponseEntity> getTitles(String username, String deviceID) + } + + class SubscriptionTitles { + <> SubscriptionTitles(Subscription subscription, List episodeTitles) + Subscription getSubscription() + List getEpisodesTitles() + } + + class SubscriptionDelta { + <> SubscriptionDelta(List add, List remove) + List getRemove() + LocalDate getTimestamp() + List> getUpdate_urls() + } + } + +} + +package episodeActionsAPI <> { + package episodeActionDataAccessLayer <> { + class EpisodeActionDataAccessService <<@Repository>> { + <> EpisodeActionDataAccessService (JpaTemplate jpaTemplate) + long addEpisodeActions(String username, List episodeActionPosts) + List getEpisodeActions(String username) + List getEpisodeActionsOfPodcast(String username, String podcastURL) + List getEpisodeActionsSince(String username, LocalDateTime since) + List getEpisodeActionsOfPodcastSince(String username, String podcastURL, LocalDateTime since) + } + + interface EpisodeActionDao { + long addEpisodeActions(String username, List episodeActionPosts) + List getEpisodeActions(String username) + List getEpisodeActionsOfPodcast(String username, String podcastURL) + List getEpisodeActionsSince(String username, LocalDateTime since) + List getEpisodeActionsOfPodcastSince(String username, String podcastURL, LocalDateTime since) + } + } + + package episodeActionService <> { + class EpisodeActionService <<@Service>> { + <> EpisodeActionService (EpisodeActionDao episodeActionDao) + LocalDateTime addEpisodeActions(String username, List episodeActionPosts) + List getEpisodeActions(String username) + List getEpisodeActionsOfPodcast(String username, String podcastURL) + List getEpisodeActionsSince(String username, LocalDateTime since) + List getEpisodeActionsOfPodcastSince(String username, String podcastURL, LocalDateTime since) + } + } + + package episodeActionController <> { + class EpisodeActionController <<@Controller>>{ + <> EpisodeActionController (EpisodeActionService episodeActionService) + ResponseEntity addEpisodeActions(String username, EpisodeActionPostRequest episodeActionPostRequest) + ResponseEntity getEpisodeActions(String username, String deviceID, boolean aggregated) + ResponseEntity getEpisodeActionsOfPodcast(String username, String podcastURL, String deviceID, boolean aggregated) + ResponseEntity getEpisodeActionsSince(String username, String deviceID, long since, boolean aggregated) + ResponseEntity getEpisodeActionsOfPodcastSince(String username, String podcastURL, String deviceID, long since, boolean aggregated) + } + + class EpisodeActionPostResponse { + <> EpisodeActionPostResponse(List> updateURLs) + long getTimestamp() + List> getUpdatedURLs() + } + + class EpisodeActionPost { + <> EpisodeActionPost(String podcastURL, String episodeURL, Action action, LocalDateTime timestamp, int started, int position) + String getPodcastURL() + String getEpisodeURL() + int getGUID() + Action getAction() + LocalDateTime getTimestamp() + int getStarted() + int getPosition() + EpisodeAction getEpisodeAction() + } + + class EpisodeActionPostRequest { + <> EpisodeActionPostRequest(List episodeActionPosts) + List getEpisodeActionPosts() + } + + class EpisodeActionGetResponse { + <> EpisodeActionGetResponse(List episodeActionPosts) + List getEpisodeActionPosts() + long getTimestamp() + } + } +} + +package authenticationAPI <> { + package authenticationDataAccessLayer <> { + ' interface AuthenticationDao { + ' String login(String username) + ' int logout(String username) + ' } + + ' class AuthenticationDataAccessService <<@Respository>> { + ' <> AuthenticationDataAccessService(JpaTemplate jpaTemplate) + ' String login(String username) + ' int logout(String username) + ' } + + interface UserDetailsManager { + void createUser(UserDetails userDetails) + void changePassword(String oldPassword, String newPassword) + void deleteUser(String username) + void updateUser(UserDetails user) + boolean userExists(String username) + } + note left + Aus org.springframework.security.provisioning + - liefert Methoden zum Erstellen neuer User + und zum Aktualisieren bestehender. + end note + + class JdbcUserDetailsManager <<@Repository>> { + <> JdbcUserDetailsManager(DataSource dataSource) + void createUser(UserDetails user) + void changePassword(String oldPassword, String newPassword) + void deleteUser(String username) + void updateUser(UserDetails user) + boolean userExists(String username) + } + note right + User Management Service aus dem Paket + org.springframework.security.provisioning + der CRUD Operationen für User bereitstellt. + Hier sind nur die relevanten Methoden modelliert. + end note + } + + package authenticationService <> { + class AuthenticationService <<@Service>> { + -- + <> AuthenticationService(UserDetailsManager userDetailsManager) + List verifyLogin(String username) + int logout(String username) + int forgotPassword(ForgotPasswordRequest forgotPasswordRequest) + .. via JdbcUserDetailsManager .. + int resetPassword(String username, RequestWithPassword requestWithPassword) + int registerUser(UserDetails user) + int changePassword(String username, ChangePasswordRequest changePasswordRequest) + int deleteUser(String username, RequestWithPassword requestWithPassword) + } + + class JavaMailSenderImpl {} + note left + Aus org.springframework.mail.javamail. + Implementierung des JavaMailSender Interfaces, + welches das MailSender Interface durch Unterstützung + von MIME Nachrichten erweitert. + Das MailSender Interface definiert dabei eine + Strategie zum Versenden einfacher Mails. + Unterstützt sowohl JavaMail MimeMessages und + Spring SimpleMailMessages. + end note + } + + package authenticationController <> { + class AuthenticationController <<@Controller>> { + <> AuthenticationController(AuthenticationService authenticationService) + ResponseEntity> verifyLogin(String username) + ResponseEntity logout(String username) + ResponseEntity forgotPassword(ForgotPasswordRequest forgotPasswordRequest) + ResponseEntity resetPassword(String username, RequestWithPassword requestWithPassword) + ResponseEntity registerUser(UserDetails user) + ResponseEntity changePassword(String username, ChangePasswordRequest changePasswordRequest) + ResponseEntity deleteUser(String username, RequestWithPassword requestWithPassword) + } + + class ChangePasswordRequest { + <> ChangePasswordRequest(String oldPassword, String newPassword) + String getOldPassword() + String getNewPassword() + } + + class ForgotPasswordRequest { + <> ForgotPasswordRequest(String email) + String getEmail() + } + + class RequestWithPassword { + <> ResetPasswordRequest(String password) + String getPassword() + } + } +} + +package model <> { + class Subscription { + <> Subscription(String url, String title) + int getID() + String getURL() + long getLastActionTimestamp() + String getTitle() + } + + class SubscriptionAction { + <> SubscriptionAction(int userID, int subscriptionID) + int getID() + int getUserID() + int getSubscriptionID() + long getTimestamp() + boolean getAdded() + } + + class Episode { + <> Episode(int subscriptionID, int id, String url, String title, String thumbnailURL, int total) + int getSubscriptionID() + int getID() + int getGUID() + String getURL() + String getTitle() + int getTotal() + } + + enum Action { + Download + Play + Delete + New + Flattr + String getJsonProperty() + } + + class EpisodeAction { + <> EpisodeAction(Action action, LocalDateTime timestamp, int started, int position) + int getEpisodeID() + Action getAction() + long getTimestamp() + int getStarted() + int getPosition() + void setEpisodeID() + EpisodeActionPost getEpisodeActionPost(String podcastURL, String episodeURL) + } + + interface UserDetails { + String getUsername() + String getPassword() + Collection getAuthorities() + boolean isAccountExpired() + boolean isAccountLocked() + boolean isCredentialsNonExpired() + boolean isEnabled() + } + note left + Aus org.springframework.security.core.userdetails. + Wird für die Schnittstelle UserDetailsManager benötigt. + Stellt wichtige Informationen eines Users bereit. + Diese werden nur indirekt von Spring Security + benutzt, indem sie vorher in Authentication Objekten + gekapselt werden. + end note + + class User { + -- + <> User(String username, String password) + int getID() + String getSessionToken() + boolean getEmailIsValidated() + .. interface methods .. + String getUsername() + String getPassword() + Collection getAuthorities() + boolean isAccountExpired() + boolean isAccountLocked() + boolean isCredentialsNonExpired() + boolean isEnabled() + } + + interface GrantedAuthority { + String getAuthority() + } + note right + Aus org.springframework.security.core. + Wird für die Schnittstelle UserDetails benötigt. + Repräsentiert eine Autorisierung, die einem + Authentication Objekt gewährt wird. + end note + + class Authority { + <> Authority() + String getAuthority() + } +} + +package util <> { + class RSSParser { + <> RSSParser(String subscriptionURL) + String getSubscriptionTitle() + List getEpisodes() + Episode getEpisodeForURL(String episodeURL) + } + note bottom + Verwendet intern Spring um + HTTP-Anfragen zu erstellen. + end note + + class CleanCronJob { + <> CleanCronJob(JdbcUserDetailsManager jdbcUserDetailsManager) + void cleanInvalidUsers() + } + note bottom + Hintergrundservice, der in periodischen Abständen + Nutzer, die ihre E-Mail-Adresse nicht nach 24 Stunden + bestätigt haben, wieder aus der Datenbank löscht. + (Auf die Assoziation zu JdbcUserDetailsManager wird + im Sinne der Übersichtlichkeit verzichtet.) + end note + + class ResponseEntity { + <> ResponseEntity(T body, HttpStatusCode status) + T getBody() + HttpStatusCode getStatusCode() + } + note bottom + Aus org.springframework.http. + Erweitert die Klasse HttpEntity, welche + ein HTTP Anfrage- oder Antwort-Objekt + repräsentiert, durch einen HttpStatusCode. + Wird von den Controller-Methoden als + Rückgabewert verwendet. + end note +} + +class SecurityConfigurationBasicAuth { + <> SecurityConfigurationBasicAuth() + PasswordEncoder encoder() + UserDetailsManager userDetailsService() + SecuryFilterChain fiterChain(HTTPSecurity http) throws Excpetion +} +note top + Erstellt einen Servlet Filter (springSecurityFilterChain) + der für die gesamte Sicherheit zuständig ist (Schutz der URLs, + Validierung von Anmeldedaten, Weiterleitung zur Anmeldung, etc.). +end note + +class PSEApplication { + <> PSEApplication() + void main(String[] args) +} + +database Datenbank +Datenbank <-[hidden]d- subscriptionsAPI +Datenbank <-[hidden]d- episodeActionsAPI +Datenbank <-[hidden]d- authenticationAPI +() SQL as SQLSub +() SQL as SQLAuth +() SQL as SQLEpisode + +Datenbank -- SQLSub +Datenbank -- SQLAuth +Datenbank -- SQLEpisode + +SubscriptionController ..o PSEApplication +AuthenticationController ..o PSEApplication +EpisodeActionController ..o PSEApplication +SecurityConfigurationBasicAuth ..o PSEApplication + +PSEApplication --() HTTP + +SQLSub )-- SubscriptionDataAccessService: JPA +' SQLAuth )-- AuthenticationDataAccessService: JPA +SQLAuth )-- JdbcUserDetailsManager: JDBC +SQLEpisode )-- EpisodeActionDataAccessService: JPA + +Subscription <. SubscriptionAction: ID +' Subscription <.. SubscriptionDataAccessService: DB +' SubscriptionAction <.. SubscriptionDataAccessService: DB +SubscriptionService --o SubscriptionController +SubscriptionDao <.. SubscriptionService: <> +Subscription --o SubscriptionTitles +EpisodeActionPost -o SubscriptionTitles +SubscriptionDao <|. SubscriptionDataAccessService: <> + +' User <.. AuthenticationDataAccessService: DB +' User <.. JdbcUserDetailsManager: DB +UserDetailsManager <.. AuthenticationService: <> +' AuthenticationDao <.. AuthenticationService: <> +AuthenticationService --o AuthenticationController +' AuthenticationDao <|. AuthenticationDataAccessService: <> +UserDetailsManager <|. JdbcUserDetailsManager: <> +UserDetailsManager <.. SecurityConfigurationBasicAuth: <> +UserDetails <|.. User: <> +User -> Authority +GrantedAuthority <|.. Authority: <> +JavaMailSenderImpl <. AuthenticationService: <> + +Action <-- EpisodeAction +EpisodeActionPost -o EpisodeActionGetResponse +EpisodeActionPost -o EpisodeActionPostRequest +EpisodeAction .> Episode: ID +' EpisodeAction <.. EpisodeActionDataAccessService: DB +' Episode <.. EpisodeActionDataAccessService: DB +EpisodeActionDao <.. EpisodeActionService: <> +EpisodeActionService --o EpisodeActionController +EpisodeActionDao <|. EpisodeActionDataAccessService: <> + +RSSParser <. SubscriptionDataAccessService: <> +RSSParser <. EpisodeActionDataAccessService: <> +' JdbcUserDetailsManager <-- CleanCronJob + +model .o Datenbank: ORM (User, SubscriptionAction, Subscription, EpisodeAction, Episode) +' Datenbank o.. Subscription: ORM +' Datenbank o.. SubscriptionAction: ORM +' Datenbank o.. Episode: ORM +' Datenbank o.. EpisodeAction: ORM +' Datenbank o.. User: ORM + +@enduml diff --git a/20-implementierungsheft/assets/diagrams/componentdiagram.puml b/20-implementierungsheft/assets/diagrams/componentdiagram.puml new file mode 100644 index 0000000..7e23754 --- /dev/null +++ b/20-implementierungsheft/assets/diagrams/componentdiagram.puml @@ -0,0 +1,79 @@ +@startuml + +[App] as app +[VueRouter] as router + +[EpisodesViewComponent] as episodes_view +[ForgotPasswordViewComponent] as forgot_password_view +[LoginViewComponent] as login_view +[RegistrationViewComponent] as registration_view +[ResetPasswordViewComponent] as reset_password_view +[settingsViewComponent] as settings_view +[SubscriptionsViewComponent] as subscriptions_view + +[DashboardLayoutComponent] as dashboard_layout +[EpisodeComponent] as episode +[ErrorLogComponent] as error_log +[FloatingLabelInputComponent] as floating_label_input +[FormLayoutComponent] as form_layout +[HelpComponent] as help +[LastUpdateComponent] as last_update +[LoadingComponent] as loading +[NavbarComponent] as navbar +[PasswordInputComponent] as password_input +[PasswordValidatorComponent] as password_validator +[ProgressTimeComponent] as progress_time +[SubscriptionComponent] as subscription + +app --> router +app --> navbar +app --> help +app --> error_log + +password_validator --> password_input +password_input --> floating_label_input + +router --> registration_view +router --> login_view +router --> reset_password_view +router --> forgot_password_view +router --> episodes_view +router --> subscriptions_view +router --> settings_view + + +login_view --> form_layout +login_view --> floating_label_input +login_view --> password_input + +forgot_password_view --> form_layout +forgot_password_view --> floating_label_input + +registration_view --> form_layout +registration_view --> password_validator +registration_view --> floating_label_input + +reset_password_view --> form_layout +reset_password_view --> password_validator + +settings_view --> dashboard_layout +settings_view --> floating_label_input +settings_view --> password_input +settings_view --> password_validator + +episodes_view --> dashboard_layout +episodes_view --> episode +episodes_view --> loading + +episode --> last_update +episode --> progress_time + +subscriptions_view --> dashboard_layout +subscriptions_view --> floating_label_input +subscriptions_view --> loading +subscriptions_view --> subscription + +subscription --> last_update +subscription --> progress_time + +@enduml diff --git a/20-implementierungsheft/assets/diagrams/db.puml b/20-implementierungsheft/assets/diagrams/db.puml new file mode 100644 index 0000000..bdefaea --- /dev/null +++ b/20-implementierungsheft/assets/diagrams/db.puml @@ -0,0 +1,78 @@ +@startuml +' Type Symbol +' Zero or One |o-- +' Exactly One ||-- +' Zero or Many }o-- +' One or Many }|-- + +skinparam linetype ortho + +entity User { + * int id <> + * String email + * String password + * boolean verified + * long created_at +} + +entity SubscriptionAction { + * int id <> + * int user_id + * long timestamp + * int subscription_id + * boolean added +} + +entity Subscription { + * int id <> + * String url + * long timestamp + * String title +} + +entity Episode { + * int id <> + * int guid <> + * String url + * String title + * int total + * int subscription_id +} +note right + Wenn der Client eine GUID aus dem Feed mitsendet, wird + diese statt der URL verwendet um die Episode zu finden. + So wird die Episode auch noch gefunden, nachdem sich + die URL geändert hat. +end note +note bottom of Episode + Wenn für die Episoden-URL einer EpisodeAction noch keine Episode in der Datenbank steht, + dann schreibe dafür ein Dummy-Objekt in Datenbank und lade asynchron die Episoden der Subscription. + Ersetze dann die Dummy-Objekte durch die Episoden und setze den Timestamp der Subscription auf + die aktuelle Zeit. + Um DoS-Angriffe auf den Backend-Server abzuwenden, können die Episoden einer Subscription erst + nach einer Stunde erneut gefetched werden. Bis dahin werden für EpisodeActions, die sich auf noch + nicht geladene Episoden beziehen, nur Dummy-Objekte für die Episoden in die Datenbank geschrieben. + Es sei noch darauf hingewiesen, dass diese Dummy-Episoden bei Anfragen nicht mit ausgegeben werden. +end note + +entity EpisodeAction { + * int id <> + * int user_id + * int episode_id + * long timestamp + * int action + * int started + * int position +} +note right + Speichere für jede Episode + nur letzte Play-Action. +endnote + +User ||--o{ EpisodeAction +User ||--o{ SubscriptionAction +SubscriptionAction }|--|| Subscription +EpisodeAction }|--|| Episode +Subscription ||-right-|{ Episode + +@enduml diff --git a/20-implementierungsheft/assets/diagrams/deployment.puml b/20-implementierungsheft/assets/diagrams/deployment.puml new file mode 100644 index 0000000..26918e2 --- /dev/null +++ b/20-implementierungsheft/assets/diagrams/deployment.puml @@ -0,0 +1,59 @@ +@startuml + +node "<> \nBackend Server" as backendServer{ + database " <> \n MariaDB Server 10.6" as database { + rectangle rectangle1 [ + <> + User + ] + rectangle rectangle2 [ + <> + SubscriptionAction + ] + rectangle rectangle3 [ + <> + EpisodeAction + ] + rectangle rectangle4 [ + <> + Subscription + ] + rectangle rectangle5 [ + <> + Episode + ] + } + + node "<> \nJava Spring" as javaSpring{ + node " <> \n Tomcat Webserver" + } +} + +node "<> \nFrontend" as frontendServer { + +} + +node "<> \nEndgerät" as terminal { + node "<> \nBrowser" as browser + node "<> \nPodcatcher" as podcatcher +} + +backendServer "1" - "*" podcatcher + +node "<> \nFrontend Server" as frontendServer{ + node "<> \nVue.js" as vuejs { + + } +} + +podcatcher -[hidden] browser + +backendServer - "1" frontendServer + +database "1" -- "1" javaSpring + +browser "*" -- frontendServer + + + +@enduml diff --git a/20-implementierungsheft/assets/diagrams/gantt-plan.puml b/20-implementierungsheft/assets/diagrams/gantt-plan.puml new file mode 100644 index 0000000..0e90aa2 --- /dev/null +++ b/20-implementierungsheft/assets/diagrams/gantt-plan.puml @@ -0,0 +1,31 @@ +@startgantt + +printscale daily zoom 5 +project starts on 2023-01-30 + +-- Backend -- +[Controller-Schicht] on {Immanuel} lasts 2 days +[Service-Schicht (Daten durchreichen)] on {Daniel} lasts 2 days +[Authentifizierung] on {Gero} lasts 4 days +[Model-Paket] on {Daniel} lasts 1 days +[Datenbank aufsetzen] on {Immanuel} lasts 4 days +[Util-Paket (RSSParser)] on {Daniel} {Lukas} lasts 6 days +[DataAccess-Schicht] on {Immanuel} {Julius} lasts 8 days +[Service-Schicht (Geschäftslogik)] on {Daniel} {Immanuel} lasts 8 days +[Util-Paket (CleanCronJob)] on {Julius} lasts 2 days +-- Frontend -- +[Komponenten] on {Gero} {Julius} {Lukas} lasts 15 days +[API-Anbindung] on {Gero} {Lukas} lasts 4 days + +'Backend +[Service-Schicht (Daten durchreichen)] starts at [Controller-Schicht]'s end +[Datenbank aufsetzen] starts at [Model-Paket]'s end +[Authentifizierung] starts at [Controller-Schicht]'s end +[DataAccess-Schicht] starts at [Datenbank aufsetzen]'s end +[Util-Paket (RSSParser)] starts at [Datenbank aufsetzen]'s end +[Service-Schicht (Geschäftslogik)] starts at [DataAccess-Schicht]'s end +[Util-Paket (CleanCronJob)] starts at [DataAccess-Schicht]'s end +'Frontend +[API-Anbindung] starts at [DataAccess-Schicht]'s end + +@endgantt \ No newline at end of file diff --git a/20-implementierungsheft/assets/diagrams/gantt-reality.puml b/20-implementierungsheft/assets/diagrams/gantt-reality.puml new file mode 100644 index 0000000..f726c56 --- /dev/null +++ b/20-implementierungsheft/assets/diagrams/gantt-reality.puml @@ -0,0 +1,39 @@ +@startgantt + +printscale daily zoom 5 +project starts on 2023-01-30 + +-- Backend -- +[Controller-Schicht] on {Immanuel} lasts 3 days +[Model-Paket] on {Daniel} lasts 3 days +[Datenbank aufsetzen] on {Immanuel} lasts 6 days +[Util-Paket (RSSParser)] on {Daniel} {Lukas} lasts 32 days +[DAO-Interfaces] on {Julius} {Immanuel} lasts 6 days +[Authentifizierung] on {Immanuel} lasts 13 days +[Service-Schicht mit Datenzugriff] on {Julius} lasts 14 days +[Util-Paket (CleanCronJob)] on {Daniel} lasts 2 days +[Docker] on {Daniel} lasts 12 days +[EMailService] on {Gero} lasts 1 days +-- Frontend -- +[Komponenten] on {Gero} {Julius} lasts 15 days +[Mehrsprachigkeit] on {Lukas} lasts 5 days +[Router] on {Gero} lasts 1 days +[API-Anbindung] on {Gero} {Lukas} lasts 28 days +[Error-Handling] on {Gero} lasts 5 days + +'Backend +[Datenbank aufsetzen] starts at [Model-Paket]'s end +[Util-Paket (RSSParser)] starts at [Datenbank aufsetzen]'s end +[DAO-Interfaces] starts at [Datenbank aufsetzen]'s end +[Authentifizierung] starts at [DAO-Interfaces]'s end +[Service-Schicht mit Datenzugriff] starts at [DAO-Interfaces]'s end +[Util-Paket (CleanCronJob)] starts at [DAO-Interfaces]'s end +[Docker] starts at [Util-Paket (CleanCronJob)]'s end +[EMailService] starts 2023-02-14 +'Frontend +[Mehrsprachigkeit] starts 2023-02-01 +[Router] starts at [Mehrsprachigkeit]'s end +[API-Anbindung] starts at [Router]'s end +[Error-Handling] starts 2023-02-05 + +@endgantt \ No newline at end of file diff --git a/20-implementierungsheft/assets/diagrams/sequencediagram-forgotAndResetPW.puml b/20-implementierungsheft/assets/diagrams/sequencediagram-forgotAndResetPW.puml new file mode 100644 index 0000000..603130c --- /dev/null +++ b/20-implementierungsheft/assets/diagrams/sequencediagram-forgotAndResetPW.puml @@ -0,0 +1,41 @@ +@startuml + +skinparam ParticipantPadding 30 + +participant AuthenticationController << (C, #ADD1B2) @Controller >> +-> AuthenticationController: ""POST /api/2/auth/forgot.json"" \n//@RequestBody ForgotPasswordRequest forgotPasswordRequest// \n\n-> forgotPassword(//forgotPasswordRequest//) +activate AuthenticationController +participant AuthenticationService << (C, #ADD1B2) @Service >> +AuthenticationController -> AuthenticationService: forgotPassword(//forgotPasswordRequest//) +activate AuthenticationService +participant JavaMailSenderImpl << (C, #ADD1B2) >> +AuthenticationService -> JavaMailSenderImpl: create link to reset password with JWT as URL parameter \n-> send(SimpleMailMessage simpleMessage) with link +activate JavaMailSenderImpl +<<- JavaMailSenderImpl: sends email with link containing a JWT to reset password +JavaMailSenderImpl --> AuthenticationService +deactivate JavaMailSenderImpl +AuthenticationService --> AuthenticationController: int indicating status +deactivate AuthenticationService +<-- AuthenticationController: ResponseEntity indicating status \n\n-> ""HTTP status code"" +deactivate AuthenticationController +||60|| +-> AuthenticationController: ""PUT /api/2/auth/{username}/resetpassword.json"" \n//@RequestParam String jwt// \n//@RequestBody ResetPasswordRequest resetPasswordRequest// \n\n-> login user (""username"") via JWT (//jwt//) \n-> resetPassword(""username"", //resetPasswordRequest//) +activate AuthenticationController +AuthenticationController -> AuthenticationService: resetPassword(""username"", //resetPasswordRequest//) +activate AuthenticationService +participant JdbcUserDetailsManager << (C, #ADD1B2) @Repository >> +AuthenticationService -> JdbcUserDetailsManager: String oldPassword = //resetPasswordRequest//.getOldPassword() \nString newPassword = //resetPasswordRequest//.getNewPassword() \n-> changePassword(newPassword, oldPassword) +activate JdbcUserDetailsManager +database Database +JdbcUserDetailsManager -> Database: change password of logged in user +activate Database +Database --> JdbcUserDetailsManager +deactivate Database +JdbcUserDetailsManager --> AuthenticationService: int indicating status +deactivate JdbcUserDetailsManager +AuthenticationService --> AuthenticationController: int indicating status +deactivate AuthenticationService +<-- AuthenticationController: ResponseEntity indicating status \n\n-> ""HTTP status code"" +deactivate AuthenticationController + +@enduml \ No newline at end of file diff --git a/20-implementierungsheft/assets/diagrams/sequencediagram-getEpisodeActions.puml b/20-implementierungsheft/assets/diagrams/sequencediagram-getEpisodeActions.puml new file mode 100644 index 0000000..47497d5 --- /dev/null +++ b/20-implementierungsheft/assets/diagrams/sequencediagram-getEpisodeActions.puml @@ -0,0 +1,38 @@ +@startuml + +' title =**Get All Episode Actions** + +participant EpisodeActionController << (C, #ADD1B2) @Controller >> +-> EpisodeActionController: ""GET /api/2/episodes/{username}.json"" \n//@RequestParam("device") String deviceID// \n//@RequestParam("aggregated") boolean aggregated// \n\n-> getEpisodeActions(""username"", //deviceID//, //aggregated//) +note right + Die Parameter //deviceID// und //aggregated// werden ignoriert, + da nicht zwischen Geräten unterschieden und für jede + Episode sowieso nur die letzte Play-Action gespeichert + wird. Dies gilt für alle GET-Anfragen der Episode Actions API. +end note +activate EpisodeActionController +participant EpisodeActionService << (C, #ADD1B2) @Service >> +EpisodeActionController -> EpisodeActionService: getEpisodeActions(""username"") +activate EpisodeActionService +participant EpisodeActionDataAccessService << (C, #ADD1B2) @Repository >> +EpisodeActionService -> EpisodeActionDataAccessService: getEpisodeActions(""username"") +activate EpisodeActionDataAccessService +EpisodeActionDataAccessService -> EpisodeActionDataAccessService: getEpisodeActionsSince(""username"", \nLocalDateTime.MIN.toEpochSecond(ZoneOffset.UTC)) +database Database +activate EpisodeActionDataAccessService +EpisodeActionDataAccessService -> Database: get all EpisodeActions for all subscribed podcasts +activate Database +Database --> EpisodeActionDataAccessService: List selectedEpisodeActions \n-> then remove all older than LocalDateTime.MIN (none) +EpisodeActionDataAccessService -> Database: join EpisodeActions in selectedEpisodeActions with episodeURL of Episode +Database --> EpisodeActionDataAccessService +deactivate Database +EpisodeActionDataAccessService --> EpisodeActionDataAccessService: List episodeActionPosts +deactivate EpisodeActionDataAccessService +EpisodeActionDataAccessService --> EpisodeActionService: List episodeActionPosts +deactivate EpisodeActionDataAccessService +EpisodeActionService --> EpisodeActionController: List episodeActionPosts +deactivate EpisodeActionService +<-- EpisodeActionController: ResponseEntity response \n\n-> ""HTTP status code"" \n-> ""JSON"" +deactivate EpisodeActionController + +@enduml \ No newline at end of file diff --git a/20-implementierungsheft/assets/diagrams/sequencediagram-getEpisodeActionsOfPodcastSince.puml b/20-implementierungsheft/assets/diagrams/sequencediagram-getEpisodeActionsOfPodcastSince.puml new file mode 100644 index 0000000..d8797d1 --- /dev/null +++ b/20-implementierungsheft/assets/diagrams/sequencediagram-getEpisodeActionsOfPodcastSince.puml @@ -0,0 +1,32 @@ +@startuml + +' title =**Get Episode Actions of Podcast Since** + +participant EpisodeActionController << (C, #ADD1B2) @Controller >> +-> EpisodeActionController: ""GET /api/2/episodes/{username}.json"" \n//@RequestParam("podcast") String podcastURL// \n//@RequestParam("device") String deviceID// \n//@RequestParam("since") long since// \n//@RequestParam("aggregated") boolean aggregated// \n\n-> getEpisodeActionsOfPodcastSince(""username"", //podcastURL//, //deviceID//, //since//, //aggregated//) +note right + Die Parameter //deviceID// und //aggregated// werden ignoriert. + Siehe Notiz in Sequenzdiagramm **Get All Episode Actions**. +end note +activate EpisodeActionController +participant EpisodeActionService << (C, #ADD1B2) @Service >> +EpisodeActionController -> EpisodeActionService: getEpisodeActionsOfPodcastSince(""username"", //podcastURL//, //since//) +activate EpisodeActionService +participant EpisodeActionDataAccessService << (C, #ADD1B2) @Repository >> +EpisodeActionService -> EpisodeActionDataAccessService: getEpisodeActionsOfPodcastSince(""username"", //podcastURL//, //since//) +activate EpisodeActionDataAccessService +database Database +EpisodeActionDataAccessService -> Database: get all EpisodeActions the given podcast (//podcastURL//) +activate Database +Database --> EpisodeActionDataAccessService: List selectedEpisodeActions \n-> then remove all older than //since// +EpisodeActionDataAccessService -> Database: join EpisodeActions in selectedEpisodeActions with episodeURL of Episode +Database --> EpisodeActionDataAccessService +deactivate Database +EpisodeActionDataAccessService --> EpisodeActionService: List episodeActionPosts +deactivate EpisodeActionDataAccessService +EpisodeActionService --> EpisodeActionController: List episodeActionPosts +deactivate EpisodeActionService +<-- EpisodeActionController: ResponseEntity response \n\n-> ""HTTP status code"" \n-> ""JSON"" +deactivate EpisodeActionController + +@enduml \ No newline at end of file diff --git a/20-implementierungsheft/assets/diagrams/sequencediagram-getSubscriptions.puml b/20-implementierungsheft/assets/diagrams/sequencediagram-getSubscriptions.puml new file mode 100644 index 0000000..4d8ab90 --- /dev/null +++ b/20-implementierungsheft/assets/diagrams/sequencediagram-getSubscriptions.puml @@ -0,0 +1,38 @@ +@startuml + +' title =**Get All Subscriptions** + +participant SubscriptionController << (C, #ADD1B2) @Controller >> +-> SubscriptionController: ""GET /subscriptions/{username}.json"" \n"" /subscriptions/{username}/{deviceid}.json"" \n//@RequestParam("jsonp") String functionJSONP// \n\n-> getSubscriptions(""username"", ""deviceid"", //functionJSONP//) +activate SubscriptionController +note right + Die Parameter ""deviceid"" und + //functionJSONP// werden ignoriert, + da nicht zwischen Geräten unterschieden + und JSONP nicht unterstützt wird. +end note +participant SubscriptionService << (C, #ADD1B2) @Service >> +SubscriptionController -> SubscriptionService: getSubscriptions(""username"") +activate SubscriptionService +participant SubscriptionDataAccessService << (C, #ADD1B2) @Repository >> +SubscriptionService -> SubscriptionDataAccessService: getSubscriptions(""username"") +activate SubscriptionDataAccessService +SubscriptionDataAccessService -> SubscriptionDataAccessService: getSubscriptionsSince(""username"", LocalDateTime.MIN) +database Database +activate SubscriptionDataAccessService +SubscriptionDataAccessService -> Database: get all Subscriptions for ""username"" +activate Database +Database --> SubscriptionDataAccessService: List subscriptions +SubscriptionDataAccessService -> Database: get Podcasts from Subscriptions +Database --> SubscriptionDataAccessService: List subscribedPodcasts +deactivate Database +SubscriptionDataAccessService --> SubscriptionDataAccessService: List podcastURLs +deactivate SubscriptionDataAccessService +SubscriptionDataAccessService --> SubscriptionService: List podcastURLs +deactivate SubscriptionDataAccessService +SubscriptionService --> SubscriptionController: List podcastURLs +deactivate SubscriptionService +<-- SubscriptionController: ResponseEntity> podcastURLs \n \n-> ""HTTP status code"" \n-> ""JSON"" +deactivate SubscriptionController + +@enduml \ No newline at end of file diff --git a/20-implementierungsheft/assets/diagrams/sequencediagram-register.puml b/20-implementierungsheft/assets/diagrams/sequencediagram-register.puml new file mode 100644 index 0000000..b7b7aa1 --- /dev/null +++ b/20-implementierungsheft/assets/diagrams/sequencediagram-register.puml @@ -0,0 +1,26 @@ +@startuml + +' title =**Register** + +participant AuthenticationController << (C, #ADD1B2) @Controller >> +-> AuthenticationController: ""POST /api/2/auth/register.json"" \n//@RequestBody UserDetails user// \n\n-> registerUser(//user//) +activate AuthenticationController +participant AuthenticationService << (C, #ADD1B2) @Service >> +AuthenticationController -> AuthenticationService: registerUser(//user//) +activate AuthenticationService +participant JdbcUserDetailsManager << (C, #ADD1B2) @Repository >> +AuthenticationService -> JdbcUserDetailsManager: createUser(//user//) +activate JdbcUserDetailsManager +database Database +JdbcUserDetailsManager -> Database: create new User with given UserDetails (//user//) +activate Database +Database --> JdbcUserDetailsManager +deactivate Database +JdbcUserDetailsManager --> AuthenticationService: int indicating status +deactivate JdbcUserDetailsManager +AuthenticationService --> AuthenticationController: int indicating status +deactivate AuthenticationService +<-- AuthenticationController: ResponseEntity indicating status \n\n-> ""HTTP status code"" +deactivate AuthenticationController + +@enduml \ No newline at end of file diff --git a/20-implementierungsheft/assets/diagrams/sequencediagram-uploadEpisodeActions.puml b/20-implementierungsheft/assets/diagrams/sequencediagram-uploadEpisodeActions.puml new file mode 100644 index 0000000..d3dac57 --- /dev/null +++ b/20-implementierungsheft/assets/diagrams/sequencediagram-uploadEpisodeActions.puml @@ -0,0 +1,38 @@ +@startuml + +' title =**Upload Episode Actions** + +participant EpisodeActionController << (C, #ADD1B2) @Controller >> +-> EpisodeActionController: ""POST /api/2/episodes/{username}.json"" \n//@RequestBody EpisodeActionPostRequest episodeActionPostRequest// \n\n-> addEpisodeActions(""username"", //episodeActionPostRequest//) +activate EpisodeActionController +participant EpisodeActionService << (C, #ADD1B2) @Service >> +EpisodeActionController -> EpisodeActionService: addEpisodeActions(""username"", \nepisodeActionPosts = //episodeActionPostRequest//.getEpisodeActionPosts()) +activate EpisodeActionService +participant EpisodeActionDataAccessService << (C, #ADD1B2) @Repository >> +EpisodeActionService -> EpisodeActionDataAccessService: addEpisodeActions(""username"", episodeActionPosts) +database Database +activate EpisodeActionDataAccessService +loop for each EpisodeActionPost in episodeActionPosts -> episodeAction = episodeActionPost.getEpisodeAction() +opt episodeAction.getAction().equals(Action.PLAY) +EpisodeActionDataAccessService -> Database: set episodeID field of episodeAction for this ""username"" via podcastURL and episodeURL +activate Database +Database --> EpisodeActionDataAccessService +EpisodeActionDataAccessService -> Database: get last EpisodeAction with this episodeID if present +Database --> EpisodeActionDataAccessService: Optional lastEpisodeAction +opt lastEpisodeAction.isPresent() +EpisodeActionDataAccessService -> Database: replace lastEpisodeAction with episodeAction +else else +EpisodeActionDataAccessService -> Database: add episodeAction to DB as new entry +end +Database --> EpisodeActionDataAccessService +deactivate Database +end +end +EpisodeActionDataAccessService --> EpisodeActionService: long latestTimestamp +deactivate EpisodeActionDataAccessService +EpisodeActionService --> EpisodeActionController: LocalDateTime timestamp = LocalDateTime.ofEpochSecond(latestTimestamp, 0, ZoneOffset.UTC) +deactivate EpisodeActionService +<-- EpisodeActionController: ResponseEntity \n(with empty list for updateURLs) \n\n-> ""HTTP status code"" \n-> ""JSON"" +deactivate EpisodeActionController + +@enduml \ No newline at end of file diff --git a/20-implementierungsheft/assets/diagrams/sequencediagram-uploadSubscriptions.puml b/20-implementierungsheft/assets/diagrams/sequencediagram-uploadSubscriptions.puml new file mode 100644 index 0000000..1edc8cf --- /dev/null +++ b/20-implementierungsheft/assets/diagrams/sequencediagram-uploadSubscriptions.puml @@ -0,0 +1,32 @@ +@startuml + +' title =**Upload Subscriptions** + +participant SubscriptionController << (C, #ADD1B2) @Controller >> +-> SubscriptionController: ""PUT /subscriptions/{username}/{deviceid}.json"" \n//@RequestBody List subscriptions// \n\n-> uploadSubscriptions(""username"", ""deviceid"", //subscriptions//) +activate SubscriptionController +participant SubscriptionService << (C, #ADD1B2) @Service >> +SubscriptionController -> SubscriptionService: uploadSubscriptions(""username"", //subscriptions//) +activate SubscriptionService +participant SubscriptionDataAccessService << (C, #ADD1B2) @Repository >> +SubscriptionService -> SubscriptionDataAccessService: uploadSubscriptions(""username"", //subscriptions//) +activate SubscriptionDataAccessService +database Database +SubscriptionDataAccessService -> Database: delete all subsciptions of ""username"" +activate Database +Database --> SubscriptionDataAccessService +SubscriptionDataAccessService -> SubscriptionDataAccessService: addSubscriptions(""username"", //subscriptions//) +activate SubscriptionDataAccessService +SubscriptionDataAccessService -> Database: upload all subscriptions (//subscriptions//) for ""username"" +Database --> SubscriptionDataAccessService +deactivate Database +SubscriptionDataAccessService --> SubscriptionDataAccessService: int indicating status +deactivate SubscriptionDataAccessService +SubscriptionDataAccessService --> SubscriptionService: int indicating status +deactivate SubscriptionDataAccessService +SubscriptionService --> SubscriptionController: int indicating status +deactivate SubscriptionService +<-- SubscriptionController: ResponseEntity with empty String for success \n\n-> ""HTTP status code"" \n-> ""JSON"" +deactivate SubscriptionController + +@enduml \ No newline at end of file diff --git a/20-implementierungsheft/assets/episode.png b/20-implementierungsheft/assets/episode.png new file mode 100644 index 0000000..c0db4a2 Binary files /dev/null and b/20-implementierungsheft/assets/episode.png differ diff --git a/20-implementierungsheft/assets/errorlog.png b/20-implementierungsheft/assets/errorlog.png new file mode 100644 index 0000000..3f6ea82 Binary files /dev/null and b/20-implementierungsheft/assets/errorlog.png differ diff --git a/20-implementierungsheft/assets/floatinglabelinput.png b/20-implementierungsheft/assets/floatinglabelinput.png new file mode 100644 index 0000000..25f013b Binary files /dev/null and b/20-implementierungsheft/assets/floatinglabelinput.png differ diff --git a/20-implementierungsheft/assets/gantt-plan-eps-converted-to.pdf b/20-implementierungsheft/assets/gantt-plan-eps-converted-to.pdf new file mode 100644 index 0000000..add028f Binary files /dev/null and b/20-implementierungsheft/assets/gantt-plan-eps-converted-to.pdf differ diff --git a/20-implementierungsheft/assets/gantt-plan.eps b/20-implementierungsheft/assets/gantt-plan.eps new file mode 100644 index 0000000..0242842 --- /dev/null +++ b/20-implementierungsheft/assets/gantt-plan.eps @@ -0,0 +1,8314 @@ +%!PS-Adobe-3.0 EPSF-3.0 +%%Creator: PlantUML v1.2023.0 +%%Title: noTitle +%%BoundingBox: 0 0 1049 488 +%%ColorUsage: Color +%%Origin: 0 0 +%%EndComments + +gsave +0 488 translate +.01 -.01 scale +/simplerect { +newpath moveto 1 index 0 rlineto +0 exch rlineto +neg 0 rlineto +} def +/rquadto { +3 index 3 index 4 2 roll rcurveto +} def +/P$h { +134 12 rlineto +0 26 rlineto +-353 0 rlineto +0 -26 rlineto +134 -12 rlineto +0 -534 rlineto +-131 46 rlineto +0 -25 rlineto +190 -109 rlineto +25 0 rlineto +0 621 rlineto +closepath +} def +/P$1d { +0 -60 -42 -87 rquadto +-40 -26 -120 -26 rquadto +-173 0 rlineto +0 240 rlineto +173 0 rlineto +81 0 121 -31 rquadto +40 -31 40 -95 rquadto +closepath +} def +/P$30 { +109 17 rlineto +0 34 rlineto +-340 0 rlineto +0 -34 rlineto +109 -17 rlineto +0 -750 rlineto +-109 -15 rlineto +0 -34 rlineto +340 0 rlineto +0 34 rlineto +-109 15 rlineto +0 750 rlineto +} def +/P$k { +0 143 rlineto +-84 0 rlineto +0 -143 rlineto +-290 0 rlineto +0 -65 rlineto +318 -448 rlineto +56 0 rlineto +0 443 rlineto +89 0 rlineto +0 70 rlineto +-89 0 rlineto +closepath +} def +/P$a { +0 125 rlineto +-21 0 rlineto +-28 -54 rlineto +-25 0 -57 7 rquadto +-32 6 -57 17 rquadto +0 342 rlineto +78 12 rlineto +0 21 rlineto +-217 0 rlineto +0 -21 rlineto +57 -12 rlineto +0 -390 rlineto +-57 -12 rlineto +0 -21 rlineto +132 0 rlineto +4 57 rlineto +29 -25 79 -46 rquadto +50 -23 79 -23 rquadto +7 0 rlineto +closepath +} def +/P$1u { +0 -85 rlineto +268 0 rlineto +0 85 rlineto +-268 0 rlineto +} def +/P$2p { +0 -65 -45 -100 rquadto +-45 -35 -129 -35 rquadto +-243 0 rlineto +0 279 rlineto +248 0 rlineto +81 0 125 -37 rquadto +45 -37 45 -106 rquadto +} def +/P$1i { +-196 -265 rlineto +-71 59 rlineto +0 206 rlineto +-96 0 rlineto +0 -796 rlineto +96 0 rlineto +0 498 rlineto +256 -282 rlineto +112 0 rlineto +-235 250 rlineto +248 331 rlineto +-112 0 rlineto +} def +/P$8 { +0 51 -3 73 rquadto +34 -20 78 -34 rquadto +45 -15 76 -15 rquadto +59 0 89 35 rquadto +31 34 31 100 rquadto +0 301 rlineto +56 12 rlineto +0 21 rlineto +-198 0 rlineto +0 -21 rlineto +60 -12 rlineto +0 -295 rlineto +0 -84 -81 -84 rquadto +-45 0 -109 14 rquadto +0 365 rlineto +62 12 rlineto +0 21 rlineto +-201 0 rlineto +0 -21 rlineto +57 -12 rlineto +0 -626 rlineto +-68 -10 rlineto +0 -21 rlineto +150 0 rlineto +0 198 rlineto +closepath +} def +/P$2j { +0 -146 -175 -146 rquadto +-204 0 rlineto +0 298 rlineto +207 0 rlineto +171 0 171 -151 rquadto +} def +/P$x { +206 0 206 151 rquadto +0 357 rlineto +54 14 rlineto +0 39 rlineto +-201 0 rlineto +-14 -42 rlineto +-45 31 -82 42 rquadto +-35 12 -73 12 rquadto +-170 0 -170 -165 rquadto +0 -60 25 -98 rquadto +25 -37 71 -54 rquadto +48 -18 150 -20 rquadto +71 -1 rlineto +0 -81 rlineto +0 -100 -81 -100 rquadto +-50 0 -110 31 rquadto +-21 68 rlineto +-39 0 rlineto +0 -132 rlineto +89 -14 129 -17 rquadto +42 -3 85 -3 rquadto +closepath +} def +/P$3f { +235 0 350 106 rquadto +114 104 114 315 rquadto +0 212 -110 323 rquadto +-109 109 -326 109 rquadto +-303 -3 rlineto +-109 0 rlineto +0 -34 rlineto +109 -17 rlineto +0 -750 rlineto +-109 -15 rlineto +0 -34 rlineto +385 0 rlineto +} def +/P$1 { +0 240 -215 240 rquadto +-103 0 -156 -60 rquadto +-53 -62 -53 -179 rquadto +0 -117 53 -178 rquadto +53 -62 160 -62 rquadto +104 0 157 60 rquadto +53 60 53 179 rquadto +closepath +} def +/P$2w { +0 281 rlineto +423 0 rlineto +0 85 rlineto +-423 0 rlineto +0 306 rlineto +-101 0 rlineto +0 -756 rlineto +537 0 rlineto +0 82 rlineto +-435 0 rlineto +} def +/P$33 { +-82 3 rlineto +-85 3 -115 32 rquadto +-29 29 -29 100 rquadto +0 110 90 110 rquadto +42 0 73 -9 rquadto +32 -9 64 -25 rquadto +0 -212 rlineto +} def +/P$2l { +0 -121 -32 -173 rquadto +-31 -53 -104 -53 rquadto +-81 0 -118 56 rquadto +-37 54 -37 176 rquadto +0 114 35 168 rquadto +37 54 120 54 rquadto +71 0 104 -53 rquadto +32 -54 32 -176 rquadto +} def +/P$1j { +0 100 40 154 rquadto +42 53 121 53 rquadto +62 0 100 -25 rquadto +37 -25 51 -64 rquadto +84 25 rlineto +-51 137 -235 137 rquadto +-129 0 -196 -76 rquadto +-67 -76 -67 -228 rquadto +0 -145 67 -221 rquadto +67 -76 192 -76 rquadto +256 0 256 309 rquadto +0 12 rlineto +-414 0 rlineto +closepath +} def +/P$1p { +0 151 -67 226 rquadto +-67 75 -195 75 rquadto +-126 0 -192 -76 rquadto +-64 -78 -64 -225 rquadto +0 -301 259 -301 rquadto +134 0 196 73 rquadto +62 73 62 228 rquadto +closepath +} def +/P$0 { +-17 0 rlineto +-239 -562 rlineto +0 523 rlineto +87 12 rlineto +0 26 rlineto +-223 0 rlineto +0 -26 rlineto +84 -12 rlineto +0 -576 rlineto +-84 -14 rlineto +0 -25 rlineto +198 0 rlineto +214 498 rlineto +231 -498 rlineto +187 0 rlineto +0 25 rlineto +-82 14 rlineto +0 576 rlineto +82 12 rlineto +0 26 rlineto +-265 0 rlineto +0 -26 rlineto +89 -12 rlineto +0 -523 rlineto +-262 562 rlineto +} def +/P$35 { +0 109 101 109 rquadto +79 0 146 -20 rquadto +0 -470 rlineto +-89 -17 rlineto +0 -28 rlineto +193 0 rlineto +0 553 rlineto +76 15 rlineto +0 28 rlineto +-175 0 rlineto +-4 -48 rlineto +-45 25 -104 43 rquadto +-57 17 -98 17 rquadto +-151 0 -151 -175 rquadto +0 -389 rlineto +-76 -17 rlineto +0 -28 rlineto +181 0 rlineto +0 426 rlineto +} def +/P$l { +-1 0 rlineto +-234 329 rlineto +235 0 rlineto +0 -329 rlineto +closepath +} def +/P$1a { +0 -109 -26 -159 rquadto +-26 -50 -87 -50 rquadto +-21 0 -48 4 rquadto +-26 4 -43 14 rquadto +0 421 rlineto +37 9 92 9 rquadto +57 0 85 -56 rquadto +28 -56 28 -184 rquadto +closepath +} def +/P$7 { +-50 0 -78 42 rquadto +-26 40 -26 120 rquadto +196 0 rlineto +0 -87 -23 -125 rquadto +-21 -37 -68 -37 rquadto +closepath +} def +/P$2i { +0 107 -70 171 rquadto +-68 62 -189 62 rquadto +-223 0 rlineto +0 295 rlineto +-101 0 rlineto +0 -756 rlineto +318 0 rlineto +126 0 195 59 rquadto +70 59 70 167 rquadto +closepath +} def +/P$m { +112 0 167 46 rquadto +56 45 56 140 rquadto +0 98 -60 151 rquadto +-59 53 -170 53 rquadto +-93 0 -165 -20 rquadto +-6 -137 rlineto +32 0 rlineto +21 90 rlineto +21 12 51 20 rquadto +29 6 57 6 rquadto +76 0 112 -35 rquadto +37 -35 37 -123 rquadto +0 -59 -15 -90 rquadto +-15 -31 -50 -45 rquadto +-34 -15 -92 -15 rquadto +-45 0 -87 12 rquadto +-46 0 rlineto +0 -325 rlineto +332 0 rlineto +0 75 rlineto +-289 0 rlineto +0 207 rlineto +53 -10 114 -10 rquadto +closepath +} def +/P$j { +0 87 -60 137 rquadto +-59 50 -170 50 rquadto +-93 0 -176 -20 rquadto +-4 -137 rlineto +31 0 rlineto +21 90 rlineto +20 10 54 18 rquadto +34 7 65 7 rquadto +76 0 112 -34 rquadto +37 -35 37 -117 rquadto +0 -64 -34 -96 rquadto +-34 -34 -104 -37 rquadto +-70 -4 rlineto +0 -40 rlineto +70 -3 rlineto +54 -3 81 -34 rquadto +26 -31 26 -95 rquadto +0 -65 -28 -95 rquadto +-28 -31 -90 -31 rquadto +-26 0 -54 7 rquadto +-28 7 -50 18 rquadto +-17 79 rlineto +-32 0 rlineto +0 -125 rlineto +48 -12 82 -17 rquadto +35 -4 71 -4 rquadto +209 0 209 160 rquadto +0 68 -37 109 rquadto +-37 39 -106 50 rquadto +89 9 131 50 rquadto +42 40 42 114 rquadto +closepath +} def +/P$29 { +0 156 -48 279 rquadto +-48 123 -150 232 rquadto +-93 0 rlineto +101 -112 148 -237 rquadto +46 -125 46 -275 rquadto +0 -150 -48 -275 rquadto +-46 -125 -146 -237 rquadto +93 0 rlineto +101 109 150 232 rquadto +48 123 48 278 rquadto +0 1 rlineto +} def +/P$2d { +0 -73 rlineto +325 -432 rlineto +-307 0 rlineto +0 -75 rlineto +421 0 rlineto +0 73 rlineto +-326 432 rlineto +337 0 rlineto +0 75 rlineto +-450 0 rlineto +} def +/P$s { +0 -67 -26 -98 rquadto +-26 -32 -79 -32 rquadto +-51 0 -78 31 rquadto +-25 31 -25 100 rquadto +0 68 25 98 rquadto +25 28 78 28 rquadto +54 0 79 -29 rquadto +26 -29 26 -96 rquadto +closepath +} def +/P$1n { +0 121 31 175 rquadto +32 51 104 51 rquadto +82 0 118 -56 rquadto +37 -57 37 -176 rquadto +0 -115 -37 -168 rquadto +-35 -54 -117 -54 rquadto +-73 0 -106 54 rquadto +-31 53 -31 175 rquadto +closepath +} def +/P$1s { +0 -445 rlineto +0 -62 -3 -135 rquadto +90 0 rlineto +4 98 4 118 rquadto +1 0 rlineto +23 -75 53 -101 rquadto +31 -28 85 -28 rquadto +18 0 39 6 rquadto +0 87 rlineto +-20 -4 -51 -4 rquadto +-60 0 -92 51 rquadto +-31 51 -31 148 rquadto +0 303 rlineto +-96 0 rlineto +} def +/P$2a { +-85 -221 rlineto +-345 0 rlineto +-87 221 rlineto +-106 0 rlineto +309 -756 rlineto +117 0 rlineto +303 756 rlineto +-104 0 rlineto +closepath +} def +/P$3p { +-109 -15 rlineto +0 -34 rlineto +326 0 rlineto +0 34 rlineto +-95 15 rlineto +0 526 rlineto +0 85 -26 150 rquadto +-26 64 -79 101 rquadto +-53 35 -117 35 rquadto +-81 0 -132 -18 rquadto +0 -154 rlineto +42 0 rlineto +18 87 rlineto +12 15 34 23 rquadto +23 7 50 7 rquadto +89 0 89 -120 rquadto +0 -639 rlineto +} def +/P$20 { +0 -756 rlineto +103 0 rlineto +0 756 rlineto +-103 0 rlineto +} def +/P$1y { +0 -581 rlineto +96 0 rlineto +0 581 rlineto +-96 0 rlineto +} def +/P$3m { +-131 15 rlineto +0 746 rlineto +168 0 rlineto +134 0 198 -12 rquadto +39 -176 rlineto +40 0 rlineto +-10 243 rlineto +-667 0 rlineto +0 -34 rlineto +109 -17 rlineto +0 -750 rlineto +-109 -15 rlineto +0 -34 rlineto +362 0 rlineto +0 34 rlineto +} def +/P$n { +0 101 -51 157 rquadto +-51 54 -148 54 rquadto +-110 0 -168 -85 rquadto +-57 -85 -57 -246 rquadto +0 -104 29 -181 rquadto +31 -78 85 -117 rquadto +56 -40 128 -40 rquadto +71 0 142 17 rquadto +0 112 rlineto +-31 0 rlineto +-17 -65 rlineto +-17 -9 -45 -15 rquadto +-26 -7 -48 -7 rquadto +-70 0 -110 70 rquadto +-39 68 -43 201 rquadto +79 -42 160 -42 rquadto +85 0 131 48 rquadto +45 48 45 140 rquadto +closepath +} def +/P$10 { +-39 20 rlineto +-82 42 -148 42 rquadto +-151 0 -151 -162 rquadto +0 -350 rlineto +-54 -14 rlineto +0 -39 rlineto +223 0 rlineto +0 381 rlineto +0 48 20 76 rquadto +21 28 60 28 rquadto +43 0 89 -20 rquadto +0 -412 rlineto +-51 -14 rlineto +0 -39 rlineto +220 0 rlineto +0 498 rlineto +54 14 rlineto +0 39 rlineto +-214 0 rlineto +-9 -48 rlineto +} def +/P$38 { +103 15 rlineto +0 28 rlineto +-309 0 rlineto +0 -28 rlineto +101 -15 rlineto +0 -814 rlineto +-101 -15 rlineto +0 -28 rlineto +206 0 rlineto +0 857 rlineto +closepath +} def +/P$4 { +0 84 78 84 rquadto +60 0 112 -15 rquadto +0 -362 rlineto +-68 -12 rlineto +0 -21 rlineto +150 0 rlineto +0 425 rlineto +57 12 rlineto +0 21 rlineto +-134 0 rlineto +-3 -37 rlineto +-34 18 -79 32 rquadto +-45 14 -76 14 rquadto +-117 0 -117 -134 rquadto +0 -300 rlineto +-59 -12 rlineto +0 -21 rlineto +140 0 rlineto +0 328 rlineto +closepath +} def +/P$13 { +-498 0 rlineto +0 -110 rlineto +51 -53 93 -96 rquadto +93 -92 135 -145 rquadto +43 -53 64 -109 rquadto +20 -57 20 -129 rquadto +0 -64 -31 -103 rquadto +-29 -39 -81 -39 rquadto +-37 0 -59 7 rquadto +-20 6 -39 21 rquadto +-25 114 rlineto +-51 0 rlineto +0 -178 rlineto +46 -10 92 -18 rquadto +45 -7 96 -7 rquadto +129 0 198 54 rquadto +68 53 68 151 rquadto +0 60 -20 110 rquadto +-20 50 -65 98 rquadto +-43 46 -175 154 rquadto +-50 40 -109 92 rquadto +385 0 rlineto +0 132 rlineto +} def +/P$f { +0 339 -215 339 rquadto +-103 0 -156 -85 rquadto +-53 -87 -53 -253 rquadto +0 -162 53 -248 rquadto +53 -87 160 -87 rquadto +103 0 156 85 rquadto +54 84 54 250 rquadto +closepath +} def +/P$b { +32 0 rlineto +17 89 rlineto +17 21 60 40 rquadto +45 17 87 17 rquadto +68 0 106 -34 rquadto +39 -35 39 -96 rquadto +0 -35 -15 -57 rquadto +-14 -23 -39 -39 rquadto +-23 -15 -54 -26 rquadto +-29 -10 -62 -21 rquadto +-32 -12 -64 -25 rquadto +-29 -14 -54 -34 rquadto +-23 -21 -39 -53 rquadto +-14 -31 -14 -76 rquadto +0 -78 57 -121 rquadto +59 -45 162 -45 rquadto +79 0 171 21 rquadto +0 135 rlineto +-31 0 rlineto +-17 -79 rlineto +-50 -37 -123 -37 rquadto +-64 0 -101 28 rquadto +-37 26 -37 73 rquadto +0 31 14 53 rquadto +15 20 39 35 rquadto +25 14 56 25 rquadto +31 10 64 21 rquadto +32 10 62 26 rquadto +31 14 54 35 rquadto +25 21 39 54 rquadto +15 31 15 78 rquadto +0 95 -57 146 rquadto +-57 51 -167 51 rquadto +-53 0 -106 -9 rquadto +-53 -9 -95 -25 rquadto +0 -151 rlineto +} def +/P$2e { +-93 0 -151 -37 rquadto +-56 -37 -71 -106 rquadto +96 -14 rlineto +9 40 42 62 rquadto +34 21 87 21 rquadto +145 0 145 -168 rquadto +0 -93 rlineto +-1 0 rlineto +-28 56 -76 84 rquadto +-46 28 -110 28 rquadto +-106 0 -156 -70 rquadto +-50 -71 -50 -223 rquadto +0 -154 53 -228 rquadto +54 -73 164 -73 rquadto +62 0 107 28 rquadto +45 28 70 81 rquadto +0 0 rlineto +0 -17 1 -56 rquadto +3 -40 4 -43 rquadto +92 0 rlineto +-3 29 -3 120 rquadto +0 443 rlineto +0 245 -243 245 rquadto +closepath +} def +/P$2u { +0 -98 rlineto +87 0 rlineto +0 98 rlineto +-87 0 rlineto +closepath +} def +/P$1v { +0 104 -82 162 rquadto +-81 57 -229 57 rquadto +-276 0 -320 -192 rquadto +100 -20 rlineto +17 68 71 101 rquadto +56 31 153 31 rquadto +98 0 151 -34 rquadto +54 -34 54 -100 rquadto +0 -37 -17 -60 rquadto +-15 -23 -46 -37 rquadto +-31 -15 -73 -25 rquadto +-42 -10 -93 -21 rquadto +-89 -20 -135 -40 rquadto +-46 -20 -73 -43 rquadto +-26 -25 -40 -57 rquadto +-14 -32 -14 -75 rquadto +0 -96 73 -150 rquadto +75 -53 214 -53 rquadto +128 0 195 40 rquadto +68 39 96 134 rquadto +-101 17 rlineto +-15 -59 -62 -85 rquadto +-46 -28 -129 -28 rquadto +-90 0 -139 29 rquadto +-46 29 -46 90 rquadto +0 34 17 57 rquadto +18 21 53 37 rquadto +35 15 140 39 rquadto +34 7 68 17 rquadto +34 7 65 20 rquadto +32 10 59 26 rquadto +28 15 48 39 rquadto +20 21 31 53 rquadto +12 29 12 70 rquadto +} def +/P$1e { +0 -132 -187 -132 rquadto +-192 0 rlineto +0 271 rlineto +200 0 rlineto +93 0 135 -34 rquadto +43 -34 43 -104 rquadto +} def +/P$1c { +0 100 -73 156 rquadto +-73 56 -204 56 rquadto +-306 0 rlineto +0 -756 rlineto +275 0 rlineto +265 0 265 182 rquadto +0 67 -37 114 rquadto +-37 45 -106 60 rquadto +90 9 139 59 rquadto +48 50 48 126 rquadto +closepath +} def +/P$9 { +0 254 rlineto +107 12 rlineto +0 26 rlineto +-279 0 rlineto +0 -26 rlineto +76 -12 rlineto +0 -576 rlineto +-84 -14 rlineto +0 -25 rlineto +492 0 rlineto +0 156 rlineto +-32 0 rlineto +-15 -106 rlineto +-54 -6 -157 -6 rquadto +-106 0 rlineto +0 273 rlineto +192 0 rlineto +15 -78 rlineto +29 0 rlineto +0 200 rlineto +-29 0 rlineto +-15 -78 rlineto +-192 0 rlineto +} def +/P$1w { +31 -57 75 -84 rquadto +43 -26 110 -26 rquadto +93 0 139 46 rquadto +45 46 45 157 rquadto +0 387 rlineto +-96 0 rlineto +0 -368 rlineto +0 -60 -12 -90 rquadto +-10 -29 -37 -43 rquadto +-25 -14 -70 -14 rquadto +-68 0 -109 46 rquadto +-40 46 -40 128 rquadto +0 342 rlineto +-96 0 rlineto +0 -796 rlineto +96 0 rlineto +0 207 rlineto +0 32 -3 67 rquadto +-1 34 -1 40 rquadto +1 0 rlineto +} def +/P$1b { +-56 -14 rlineto +0 -37 rlineto +225 0 rlineto +0 201 rlineto +0 54 -6 110 rquadto +23 -18 67 -31 rquadto +45 -14 87 -14 rquadto +120 0 175 65 rquadto +54 65 54 209 rquadto +0 142 -70 223 rquadto +-70 79 -198 79 rquadto +-95 0 -278 -40 rquadto +0 -753 rlineto +} def +/P$34 { +48 -28 103 -45 rquadto +56 -18 92 -18 rquadto +78 0 117 45 rquadto +39 45 39 129 rquadto +0 393 rlineto +71 15 rlineto +0 28 rlineto +-256 0 rlineto +0 -28 rlineto +79 -15 rlineto +0 -381 rlineto +0 -53 -26 -82 rquadto +-25 -31 -79 -31 rquadto +-56 0 -140 18 rquadto +0 476 rlineto +81 15 rlineto +0 28 rlineto +-257 0 rlineto +0 -28 rlineto +71 -15 rlineto +0 -507 rlineto +-71 -17 rlineto +0 -28 rlineto +170 0 rlineto +6 48 rlineto +} def +/P$3l { +0 -137 -40 -198 rquadto +-39 -62 -123 -62 rquadto +-84 0 -121 59 rquadto +-35 59 -35 201 rquadto +0 143 37 204 rquadto +37 59 120 59 rquadto +82 0 123 -62 rquadto +40 -62 40 -201 rquadto +closepath +} def +/P$12 { +-220 -515 rlineto +-37 -14 rlineto +0 -39 rlineto +281 0 rlineto +0 39 rlineto +-65 15 rlineto +129 304 rlineto +115 -306 rlineto +-65 -14 rlineto +0 -39 rlineto +181 0 rlineto +0 39 rlineto +-40 12 rlineto +-217 534 rlineto +-37 95 -65 137 rquadto +-28 43 -62 65 rquadto +-34 21 -78 21 rquadto +-46 0 -96 -10 rquadto +0 -142 rlineto +35 0 rlineto +25 73 rlineto +17 14 42 14 rquadto +23 0 39 -10 rquadto +17 -10 32 -31 rquadto +15 -18 28 -48 rquadto +14 -28 39 -85 rquadto +} def +/P$2c { +0 510 rlineto +-96 0 rlineto +0 -510 rlineto +-81 0 rlineto +0 -70 rlineto +81 0 rlineto +0 -65 rlineto +0 -79 34 -114 rquadto +35 -34 107 -34 rquadto +40 0 68 6 rquadto +0 73 rlineto +-25 -4 -43 -4 rquadto +-37 0 -54 18 rquadto +-15 18 -15 68 rquadto +0 51 rlineto +114 0 rlineto +0 70 rlineto +-114 0 rlineto +} def +/P$o { +57 0 84 -39 rquadto +26 -39 26 -126 rquadto +0 -79 -25 -115 rquadto +-25 -35 -79 -35 rquadto +-67 0 -142 25 rquadto +0 148 32 220 rquadto +34 71 103 71 rquadto +closepath +} def +/P$18 { +112 0 162 59 rquadto +50 59 50 185 rquadto +0 48 rlineto +-289 0 rlineto +0 9 rlineto +0 87 14 125 rquadto +14 35 45 56 rquadto +32 18 87 18 rquadto +51 0 129 -17 rquadto +0 45 rlineto +-31 18 -82 31 rquadto +-51 12 -100 12 rquadto +-135 0 -201 -70 rquadto +-64 -70 -64 -218 rquadto +0 -143 60 -214 rquadto +62 -71 187 -71 rquadto +closepath +} def +/P$u { +-121 0 -121 170 rquadto +0 75 29 110 rquadto +29 34 90 34 rquadto +62 0 126 -25 rquadto +0 -150 -29 -220 rquadto +-29 -70 -95 -70 rquadto +closepath +} def +/P$2f { +0 -70 -20 -121 rquadto +-18 -51 -54 -78 rquadto +-34 -28 -79 -28 rquadto +-73 0 -107 54 rquadto +-32 53 -32 173 rquadto +0 118 31 171 rquadto +31 51 107 51 rquadto +45 0 81 -26 rquadto +35 -26 54 -76 rquadto +20 -51 20 -120 rquadto +} def +/P$3c { +0 -142 -26 -204 rquadto +-26 -62 -85 -62 rquadto +-56 0 -81 59 rquadto +-25 59 -25 207 rquadto +0 148 25 209 rquadto +25 60 81 60 rquadto +57 0 84 -62 rquadto +28 -64 28 -207 rquadto +closepath +} def +/P$g { +0 -157 -29 -226 rquadto +-29 -70 -95 -70 rquadto +-62 0 -90 65 rquadto +-28 65 -28 231 rquadto +0 165 28 234 rquadto +28 67 90 67 rquadto +64 0 93 -70 rquadto +31 -71 31 -231 rquadto +closepath +} def +/P$1z { +-67 0 -104 -42 rquadto +-35 -40 -35 -115 rquadto +0 -193 rlineto +0 -62 -28 -92 rquadto +-28 -31 -87 -34 rquadto +0 -68 rlineto +57 -1 85 -32 rquadto +29 -31 29 -93 rquadto +0 -193 rlineto +0 -76 34 -117 rquadto +35 -40 106 -40 rquadto +73 0 rlineto +0 68 rlineto +-34 0 rlineto +-48 0 -70 29 rquadto +-20 28 -20 85 rquadto +0 190 rlineto +0 51 -28 89 rquadto +-28 35 -75 46 rquadto +0 1 rlineto +46 10 75 48 rquadto +28 35 28 87 rquadto +0 192 rlineto +0 56 20 85 rquadto +21 29 70 29 rquadto +34 0 rlineto +0 68 rlineto +-73 0 rlineto +} def +/P$p { +-32 0 rlineto +0 -154 rlineto +406 0 rlineto +0 37 rlineto +-292 617 rlineto +-64 0 rlineto +287 -579 rlineto +-287 0 rlineto +-17 79 rlineto +closepath +} def +/P$3i { +-73 23 -153 40 rquadto +-79 15 -171 15 rquadto +-206 0 -321 -110 rquadto +-115 -112 -115 -317 rquadto +0 -223 112 -334 rquadto +112 -110 328 -110 rquadto +154 0 298 37 rquadto +0 182 rlineto +-42 0 rlineto +-17 -104 rlineto +-43 -31 -104 -46 rquadto +-60 -17 -129 -17 rquadto +-162 0 -237 96 rquadto +-75 96 -75 295 rquadto +0 187 76 284 rquadto +78 96 229 96 rquadto +53 0 110 -12 rquadto +59 -12 90 -31 rquadto +0 -242 rlineto +-109 -15 rlineto +0 -34 rlineto +314 0 rlineto +0 34 rlineto +-82 15 rlineto +0 278 rlineto +} def +/P$2q { +50 0 70 -29 rquadto +21 -29 21 -85 rquadto +0 -192 rlineto +0 -53 28 -89 rquadto +28 -35 75 -46 rquadto +0 -1 rlineto +-45 -10 -75 -46 rquadto +-28 -35 -28 -89 rquadto +0 -190 rlineto +0 -57 -21 -85 rquadto +-20 -29 -70 -29 rquadto +-31 0 rlineto +0 -68 rlineto +70 0 rlineto +70 0 106 40 rquadto +35 40 35 117 rquadto +0 193 rlineto +0 62 28 93 rquadto +28 31 87 32 rquadto +0 68 rlineto +-59 1 -87 32 rquadto +-28 31 -28 93 rquadto +0 193 rlineto +0 75 -37 115 rquadto +-35 42 -104 42 rquadto +-70 0 rlineto +0 -68 rlineto +31 0 rlineto +} def +/P$2g { +0 -184 98 -285 rquadto +100 -101 278 -101 rquadto +126 0 204 43 rquadto +78 42 120 135 rquadto +-96 28 rlineto +-32 -64 -89 -93 rquadto +-56 -29 -140 -29 rquadto +-131 0 -201 79 rquadto +-68 78 -68 223 rquadto +0 142 73 226 rquadto +73 82 203 82 rquadto +75 0 139 -21 rquadto +64 -23 104 -62 rquadto +0 -135 rlineto +-226 0 rlineto +0 -85 rlineto +320 0 rlineto +0 260 rlineto +-59 60 -146 95 rquadto +-87 32 -190 32 rquadto +-117 0 -203 -46 rquadto +-85 -48 -132 -135 rquadto +-45 -89 -45 -209 rquadto +} def +/P$24 { +-114 0 rlineto +-212 -581 rlineto +103 0 rlineto +128 378 rlineto +7 21 37 128 rquadto +18 -64 rlineto +20 -62 rlineto +132 -379 rlineto +103 0 rlineto +-217 581 rlineto +} def +/P$2y { +0 304 -214 304 rquadto +-134 0 -179 -101 rquadto +-3 0 rlineto +1 4 1 92 rquadto +0 226 rlineto +-96 0 rlineto +0 -690 rlineto +0 -89 -3 -118 rquadto +93 0 rlineto +1 1 1 15 rquadto +1 12 3 40 rquadto +1 26 1 37 rquadto +1 0 rlineto +26 -54 68 -78 rquadto +42 -25 110 -25 rquadto +107 0 160 71 rquadto +53 71 53 225 rquadto +closepath +} def +/P$37 { +-65 0 -101 54 rquadto +-35 53 -35 156 rquadto +256 0 rlineto +0 -112 -29 -160 rquadto +-29 -50 -89 -50 rquadto +} def +/P$11 { +62 -70 112 -101 rquadto +51 -31 92 -31 rquadto +31 0 rlineto +0 201 rlineto +-32 0 rlineto +-34 -73 rlineto +-35 0 -82 15 rquadto +-46 15 -82 37 rquadto +0 334 rlineto +87 14 rlineto +0 39 rlineto +-328 0 rlineto +0 -39 rlineto +70 -14 rlineto +0 -445 rlineto +-70 -14 rlineto +0 -39 rlineto +231 0 rlineto +6 115 rlineto +} def +/P$3e { +0 -178 -96 -270 rquadto +-95 -92 -275 -92 rquadto +-114 0 rlineto +0 734 rlineto +76 4 181 4 rquadto +156 0 229 -92 rquadto +75 -92 75 -284 rquadto +closepath +} def +/P$2n { +-92 0 -162 -32 rquadto +-68 -34 -107 -98 rquadto +-37 -65 -37 -154 rquadto +0 -481 rlineto +103 0 rlineto +0 471 rlineto +0 104 51 159 rquadto +53 53 153 53 rquadto +101 0 157 -56 rquadto +57 -56 57 -162 rquadto +0 -465 rlineto +101 0 rlineto +0 471 rlineto +0 92 -39 159 rquadto +-39 65 -110 101 rquadto +-70 34 -167 34 rquadto +} def +/P$2s { +53 0 93 -23 rquadto +42 -23 65 -65 rquadto +25 -42 25 -87 rquadto +0 -48 rlineto +-106 3 rlineto +-67 0 -103 14 rquadto +-34 12 -53 39 rquadto +-18 26 -18 70 rquadto +0 46 25 73 rquadto +25 25 71 25 rquadto +closepath +} def +/P$i { +-401 0 rlineto +0 -71 rlineto +90 -82 rlineto +87 -76 128 -123 rquadto +42 -46 59 -96 rquadto +18 -51 18 -115 rquadto +0 -64 -29 -96 rquadto +-28 -34 -93 -34 rquadto +-26 0 -54 7 rquadto +-26 7 -46 18 rquadto +-17 79 rlineto +-32 0 rlineto +0 -125 rlineto +89 -21 151 -21 rquadto +107 0 160 45 rquadto +54 43 54 126 rquadto +0 54 -21 103 rquadto +-21 48 -65 96 rquadto +-43 46 -145 134 rquadto +-43 35 -92 81 rquadto +337 0 rlineto +0 75 rlineto +closepath +} def +/P$1h { +0 117 35 173 rquadto +35 54 109 54 rquadto +51 0 85 -28 rquadto +35 -28 43 -85 rquadto +96 6 rlineto +-10 84 -71 134 rquadto +-59 50 -151 50 rquadto +-121 0 -185 -76 rquadto +-64 -78 -64 -225 rquadto +0 -146 64 -223 rquadto +64 -78 184 -78 rquadto +89 0 148 46 rquadto +59 45 73 126 rquadto +-98 7 rlineto +-7 -48 -39 -76 rquadto +-29 -28 -85 -28 rquadto +-76 0 -110 51 rquadto +-34 50 -34 170 rquadto +} def +/P$2x { +-301 -365 rlineto +-100 75 rlineto +0 290 rlineto +-101 0 rlineto +0 -756 rlineto +101 0 rlineto +0 378 rlineto +365 -378 rlineto +120 0 rlineto +-321 328 rlineto +365 428 rlineto +-128 0 rlineto +} def +/P$2h { +0 -504 rlineto +0 -84 4 -160 rquadto +-26 95 -48 150 rquadto +-195 515 rlineto +-71 0 rlineto +-198 -515 rlineto +-29 -90 rlineto +-17 -59 rlineto +1 59 rlineto +1 101 rlineto +0 504 rlineto +-90 0 rlineto +0 -756 rlineto +134 0 rlineto +201 523 rlineto +10 32 20 68 rquadto +10 35 14 51 rquadto +3 -20 17 -64 rquadto +14 -43 18 -56 rquadto +196 -523 rlineto +132 0 rlineto +0 756 rlineto +-92 0 rlineto +} def +/P$39 { +120 12 rlineto +0 23 rlineto +-315 0 rlineto +0 -23 rlineto +120 -12 rlineto +0 -479 rlineto +-118 42 rlineto +0 -23 rlineto +171 -96 rlineto +21 0 rlineto +0 557 rlineto +} def +/P$3a { +0 306 -193 306 rquadto +-92 0 -140 -78 rquadto +-46 -79 -46 -228 rquadto +0 -146 46 -223 rquadto +48 -78 145 -78 rquadto +92 0 140 76 rquadto +48 76 48 225 rquadto +closepath +} def +/P$3d { +-360 0 rlineto +0 -64 rlineto +82 -75 rlineto +78 -68 114 -110 rquadto +37 -43 53 -89 rquadto +17 -45 17 -103 rquadto +0 -57 -26 -87 rquadto +-25 -29 -84 -29 rquadto +-23 0 -48 6 rquadto +-25 6 -43 17 rquadto +-14 71 rlineto +-29 0 rlineto +0 -112 rlineto +79 -18 135 -18 rquadto +96 0 145 40 rquadto +48 39 48 112 rquadto +0 50 -20 93 rquadto +-18 43 -57 87 rquadto +-39 42 -131 120 rquadto +-39 32 -82 73 rquadto +303 0 rlineto +0 67 rlineto +} def +/P$15 { +0 -182 -20 -262 rquadto +-20 -79 -64 -79 rquadto +-43 0 -62 78 rquadto +-18 76 -18 264 rquadto +0 192 18 271 rquadto +18 78 62 78 rquadto +42 0 62 -81 rquadto +21 -81 21 -268 rquadto +} def +/P$d { +-64 3 rlineto +-65 1 -89 25 rquadto +-23 21 -23 76 rquadto +0 85 70 85 rquadto +32 0 56 -7 rquadto +25 -7 50 -18 rquadto +0 -164 rlineto +closepath +} def +/P$c { +75 0 110 31 rquadto +35 29 35 93 rquadto +0 309 rlineto +56 12 rlineto +0 21 rlineto +-125 0 rlineto +-9 -45 rlineto +-56 54 -142 54 rquadto +-117 0 -117 -135 rquadto +0 -46 17 -76 rquadto +17 -29 56 -45 rquadto +39 -15 114 -17 rquadto +68 -3 rlineto +0 -71 rlineto +0 -46 -17 -68 rquadto +-17 -23 -53 -23 rquadto +-50 0 -90 23 rquadto +-15 57 rlineto +-28 0 rlineto +0 -100 rlineto +79 -17 139 -17 rquadto +closepath +} def +/P$1k { +-7 -92 -46 -134 rquadto +-37 -42 -110 -42 rquadto +-70 0 -110 46 rquadto +-40 46 -43 129 rquadto +312 0 rlineto +} def +/P$16 { +0 107 -76 167 rquadto +-76 59 -214 59 rquadto +-109 0 -217 -25 rquadto +-6 -189 rlineto +53 0 rlineto +31 125 rlineto +51 29 106 29 rquadto +71 0 110 -45 rquadto +40 -46 40 -128 rquadto +0 -70 -32 -107 rquadto +-31 -37 -103 -42 rquadto +-68 -4 rlineto +0 -70 rlineto +65 -4 rlineto +53 -4 78 -39 rquadto +25 -34 25 -104 rquadto +0 -65 -29 -103 rquadto +-29 -37 -84 -37 rquadto +-31 0 -51 9 rquadto +-20 9 -39 20 rquadto +-25 114 rlineto +-51 0 rlineto +0 -178 rlineto +60 -15 103 -20 rquadto +43 -6 85 -6 rquadto +264 0 264 193 rquadto +0 79 -42 129 rquadto +-42 48 -120 60 rquadto +198 23 198 196 rquadto +closepath +} def +/P$2v { +0 -98 rlineto +89 0 rlineto +0 98 rlineto +-89 0 rlineto +} def +/P$3o { +0 89 -56 134 rquadto +-56 45 -165 45 rquadto +-45 0 -100 -9 rquadto +-53 -9 -82 -20 rquadto +0 -146 rlineto +28 0 rlineto +31 82 rlineto +48 43 123 43 rquadto +123 0 123 -104 rquadto +0 -78 -96 -110 rquadto +-56 -18 rlineto +-64 -20 -93 -42 rquadto +-29 -21 -45 -53 rquadto +-15 -31 -15 -76 rquadto +0 -78 53 -123 rquadto +54 -45 145 -45 rquadto +65 0 164 20 rquadto +0 129 rlineto +-29 0 rlineto +-26 -68 rlineto +-34 -31 -106 -31 rquadto +-51 0 -78 26 rquadto +-26 25 -26 68 rquadto +0 35 23 60 rquadto +25 23 75 40 rquadto +92 31 120 46 rquadto +29 14 50 35 rquadto +20 20 31 48 rquadto +10 26 10 67 rquadto +closepath +} def +/P$r { +0 -78 -31 -112 rquadto +-29 -35 -95 -35 rquadto +-64 0 -92 34 rquadto +-28 32 -28 114 rquadto +0 82 28 115 rquadto +29 32 92 32 rquadto +65 0 95 -34 rquadto +31 -34 31 -114 rquadto +closepath +} def +/P$2b { +-4 15 rlineto +-12 43 -39 114 rquadto +-96 248 rlineto +282 0 rlineto +-96 -250 rlineto +-15 -35 -29 -82 rquadto +-15 -45 rlineto +} def +/P$2r { +0 -756 rlineto +101 0 rlineto +0 671 rlineto +382 0 rlineto +0 84 rlineto +-484 0 rlineto +} def +/P$3g { +0 28 -20 48 rquadto +-20 20 -48 20 rquadto +-28 0 -48 -20 rquadto +-20 -20 -20 -48 rquadto +0 -28 20 -48 rquadto +20 -20 48 -20 rquadto +28 0 48 20 rquadto +20 20 20 48 rquadto +closepath +} def +/P$e { +0 87 -60 137 rquadto +-59 50 -170 50 rquadto +-93 0 -176 -20 rquadto +-4 -137 rlineto +31 0 rlineto +21 90 rlineto +20 10 54 18 rquadto +34 7 65 7 rquadto +76 0 112 -34 rquadto +37 -35 37 -117 rquadto +0 -64 -34 -96 rquadto +-34 -34 -104 -37 rquadto +-70 -4 rlineto +0 -40 rlineto +70 -3 rlineto +54 -3 81 -34 rquadto +26 -31 26 -95 rquadto +0 -65 -28 -95 rquadto +-28 -31 -90 -31 rquadto +-26 0 -54 7 rquadto +-28 7 -50 18 rquadto +-17 79 rlineto +-32 0 rlineto +0 -125 rlineto +48 -12 82 -17 rquadto +35 -4 71 -4 rquadto +209 0 209 160 rquadto +0 68 -37 109 rquadto +-37 39 -106 50 rquadto +89 9 131 50 rquadto +42 40 42 114 rquadto +} def +/P$22 { +0 368 rlineto +0 57 10 89 rquadto +10 31 35 45 rquadto +25 14 73 14 rquadto +68 0 109 -46 rquadto +40 -48 40 -132 rquadto +0 -337 rlineto +96 0 rlineto +0 457 rlineto +0 101 3 123 rquadto +-92 0 rlineto +0 -3 -1 -14 rquadto +0 -12 -1 -26 rquadto +0 -15 0 -59 rquadto +-1 0 rlineto +-34 60 -78 85 rquadto +-43 25 -107 25 rquadto +-96 0 -140 -46 rquadto +-43 -48 -43 -157 rquadto +0 -387 rlineto +96 0 rlineto +} def +/P$5 { +-25 0 rlineto +-170 -451 rlineto +-173 451 rlineto +-26 0 rlineto +-217 -631 rlineto +-56 -14 rlineto +0 -25 rlineto +250 0 rlineto +0 25 rlineto +-96 14 rlineto +156 460 rlineto +176 -454 rlineto +21 0 rlineto +168 454 rlineto +148 -460 rlineto +-101 -14 rlineto +0 -25 rlineto +217 0 rlineto +0 25 rlineto +-57 14 rlineto +-214 631 rlineto +closepath +} def +/P$1m { +-26 56 -71 81 rquadto +-43 23 -109 23 rquadto +-109 0 -160 -73 rquadto +-51 -75 -51 -225 rquadto +0 -304 212 -304 rquadto +67 0 110 25 rquadto +43 23 70 76 rquadto +1 0 rlineto +-1 -65 rlineto +0 -240 rlineto +96 0 rlineto +0 676 rlineto +0 90 3 120 rquadto +-92 0 rlineto +-1 -9 -3 -39 rquadto +-1 -31 -1 -54 rquadto +-3 0 rlineto +closepath +} def +/P$3j { +0 160 rlineto +-28 0 rlineto +-35 -70 rlineto +-32 0 -76 9 rquadto +-42 7 -75 21 rquadto +0 446 rlineto +103 15 rlineto +0 28 rlineto +-282 0 rlineto +0 -28 rlineto +75 -15 rlineto +0 -507 rlineto +-75 -17 rlineto +0 -28 rlineto +173 0 rlineto +6 75 rlineto +37 -32 101 -60 rquadto +65 -29 104 -29 rquadto +9 0 rlineto +} def +/P$q { +0 53 -26 90 rquadto +-26 37 -70 57 rquadto +56 20 85 64 rquadto +31 43 31 106 rquadto +0 92 -53 139 rquadto +-51 46 -162 46 rquadto +-209 0 -209 -185 rquadto +0 -65 31 -107 rquadto +31 -42 84 -62 rquadto +-42 -20 -68 -56 rquadto +-26 -37 -26 -92 rquadto +0 -81 50 -125 rquadto +50 -45 143 -45 rquadto +90 0 140 45 rquadto +50 43 50 125 rquadto +closepath +} def +/P$1l { +0 -368 rlineto +0 -57 -12 -89 rquadto +-10 -31 -35 -45 rquadto +-23 -14 -71 -14 rquadto +-70 0 -110 48 rquadto +-39 46 -39 131 rquadto +0 337 rlineto +-96 0 rlineto +0 -457 rlineto +0 -101 -3 -123 rquadto +90 0 rlineto +1 3 1 15 rquadto +0 10 0 26 rquadto +1 14 3 57 rquadto +1 0 rlineto +32 -60 76 -85 rquadto +43 -25 109 -25 rquadto +95 0 139 48 rquadto +45 46 45 156 rquadto +0 387 rlineto +-96 0 rlineto +} def +/P$28 { +0 121 31 175 rquadto +32 51 104 51 rquadto +82 0 118 -56 rquadto +37 -57 37 -176 rquadto +0 -115 -37 -168 rquadto +-35 -54 -117 -54 rquadto +-73 0 -106 54 rquadto +-31 53 -31 175 rquadto +} def +/P$2t { +-192 0 -228 -198 rquadto +100 -17 rlineto +9 62 43 98 rquadto +34 34 84 34 rquadto +56 0 87 -39 rquadto +32 -39 32 -112 rquadto +0 -450 rlineto +-145 0 rlineto +0 -82 rlineto +248 0 rlineto +0 531 rlineto +0 109 -60 173 rquadto +-59 62 -162 62 rquadto +} def +/P$31 { +48 -26 101 -45 rquadto +53 -18 93 -18 rquadto +43 0 81 17 rquadto +37 15 56 51 rquadto +48 -26 114 -46 rquadto +65 -21 109 -21 rquadto +151 0 151 175 rquadto +0 393 rlineto +76 15 rlineto +0 28 rlineto +-270 0 rlineto +0 -28 rlineto +89 -15 rlineto +0 -381 rlineto +0 -109 -101 -109 rquadto +-17 0 -39 3 rquadto +-21 1 -43 4 rquadto +-21 3 -42 7 rquadto +-20 4 -32 6 rquadto +10 34 10 75 rquadto +0 393 rlineto +89 15 rlineto +0 28 rlineto +-282 0 rlineto +0 -28 rlineto +87 -15 rlineto +0 -381 rlineto +0 -53 -26 -81 rquadto +-26 -28 -81 -28 rquadto +-56 0 -139 18 rquadto +0 471 rlineto +90 15 rlineto +0 28 rlineto +-271 0 rlineto +0 -28 rlineto +76 -15 rlineto +0 -507 rlineto +-76 -17 rlineto +0 -28 rlineto +175 0 rlineto +4 48 rlineto +} def +/P$2 { +0 -106 -31 -153 rquadto +-29 -48 -95 -48 rquadto +-64 0 -92 46 rquadto +-28 45 -28 154 rquadto +0 109 28 156 rquadto +29 46 92 46 rquadto +64 0 95 -48 rquadto +31 -48 31 -154 rquadto +closepath +} def +/P$v { +134 12 rlineto +0 26 rlineto +-353 0 rlineto +0 -26 rlineto +134 -12 rlineto +0 -534 rlineto +-131 46 rlineto +0 -25 rlineto +190 -109 rlineto +25 0 rlineto +0 621 rlineto +} def +/P$w { +-101 -15 rlineto +0 -42 rlineto +375 0 rlineto +0 42 rlineto +-89 15 rlineto +0 475 rlineto +0 126 -70 196 rquadto +-70 68 -200 68 rquadto +-43 0 -87 -6 rquadto +-42 -4 -67 -12 rquadto +0 -178 rlineto +53 0 rlineto +17 104 rlineto +10 14 31 21 rquadto +20 7 45 7 rquadto +40 0 67 -28 rquadto +26 -28 26 -84 rquadto +0 -565 rlineto +} def +/P$26 { +0 117 -46 204 rquadto +-45 87 -129 134 rquadto +-82 46 -192 46 rquadto +-282 0 rlineto +0 -756 rlineto +250 0 rlineto +192 0 296 96 rquadto +104 95 104 273 rquadto +closepath +} def +/P$3n { +243 -264 rlineto +-62 -17 rlineto +0 -28 rlineto +210 0 rlineto +0 28 rlineto +-73 15 rlineto +-170 173 rlineto +218 335 rlineto +64 15 rlineto +0 28 rlineto +-243 0 rlineto +0 -28 rlineto +54 -15 rlineto +-164 -257 rlineto +-78 85 rlineto +0 171 rlineto +62 15 rlineto +0 28 rlineto +-243 0 rlineto +0 -28 rlineto +75 -15 rlineto +0 -814 rlineto +-87 -15 rlineto +0 -28 rlineto +193 0 rlineto +0 614 rlineto +} def +/P$17 { +0 276 rlineto +129 17 rlineto +0 42 rlineto +-407 0 rlineto +0 -42 rlineto +93 -17 rlineto +0 -668 rlineto +-101 -15 rlineto +0 -42 rlineto +654 0 rlineto +0 210 rlineto +-54 0 rlineto +-18 -137 rlineto +-28 -4 -95 -6 rquadto +-65 -3 -103 -3 rquadto +-96 0 rlineto +0 321 rlineto +190 0 rlineto +18 -100 rlineto +51 0 rlineto +0 265 rlineto +-51 0 rlineto +-18 -101 rlineto +-190 0 rlineto +} def +/P$2m { +0 82 -62 128 rquadto +-62 43 -173 43 rquadto +-109 0 -168 -35 rquadto +-57 -35 -75 -110 rquadto +84 -17 rlineto +12 46 51 68 rquadto +39 21 107 21 rquadto +73 0 106 -21 rquadto +34 -23 34 -68 rquadto +0 -34 -23 -54 rquadto +-23 -21 -76 -35 rquadto +-68 -18 rlineto +-82 -21 -118 -42 rquadto +-34 -20 -54 -50 rquadto +-20 -29 -20 -73 rquadto +0 -79 56 -120 rquadto +57 -42 165 -42 rquadto +96 0 153 34 rquadto +56 34 71 107 rquadto +-87 10 rlineto +-7 -39 -43 -59 rquadto +-34 -20 -93 -20 rquadto +-65 0 -96 20 rquadto +-29 18 -29 59 rquadto +0 25 12 42 rquadto +12 15 37 26 rquadto +25 10 106 31 rquadto +76 18 110 35 rquadto +34 15 53 35 rquadto +20 20 31 46 rquadto +10 25 10 57 rquadto +} def +/P$1t { +0 -796 rlineto +96 0 rlineto +0 796 rlineto +-96 0 rlineto +} def +/P$27 { +0 -140 -78 -214 rquadto +-76 -75 -221 -75 rquadto +-146 0 rlineto +0 592 rlineto +168 0 rlineto +84 0 146 -35 rquadto +62 -35 96 -104 rquadto +34 -68 34 -162 rquadto +} def +/P$3b { +0 -142 -26 -204 rquadto +-26 -62 -85 -62 rquadto +-56 0 -81 59 rquadto +-25 59 -25 207 rquadto +0 148 25 209 rquadto +25 60 81 60 rquadto +57 0 84 -62 rquadto +28 -64 28 -207 rquadto +} def +/P$2k { +0 304 -214 304 rquadto +-65 0 -109 -23 rquadto +-43 -25 -71 -78 rquadto +0 0 rlineto +0 17 -3 51 rquadto +-1 34 -3 39 rquadto +-93 0 rlineto +3 -29 3 -120 rquadto +0 -676 rlineto +96 0 rlineto +0 226 rlineto +0 35 -1 82 rquadto +1 0 rlineto +28 -56 71 -79 rquadto +43 -25 109 -25 rquadto +110 0 162 75 rquadto +51 73 51 223 rquadto +closepath +} def +/P$1g { +53 0 93 -23 rquadto +42 -23 65 -65 rquadto +25 -42 25 -87 rquadto +0 -48 rlineto +-106 3 rlineto +-67 0 -103 14 rquadto +-34 12 -53 39 rquadto +-18 26 -18 70 rquadto +0 46 25 73 rquadto +25 25 71 25 rquadto +} def +/P$2o { +-196 -314 rlineto +-235 0 rlineto +0 314 rlineto +-101 0 rlineto +0 -756 rlineto +356 0 rlineto +126 0 196 57 rquadto +70 56 70 157 rquadto +0 84 -50 142 rquadto +-48 57 -135 71 rquadto +215 326 rlineto +-118 0 rlineto +closepath +} def +/P$23 { +50 0 70 -29 rquadto +21 -29 21 -85 rquadto +0 -192 rlineto +0 -53 28 -89 rquadto +28 -35 75 -46 rquadto +0 -1 rlineto +-45 -10 -75 -46 rquadto +-28 -35 -28 -89 rquadto +0 -190 rlineto +0 -57 -21 -85 rquadto +-20 -29 -70 -29 rquadto +-31 0 rlineto +0 -68 rlineto +70 0 rlineto +70 0 106 40 rquadto +35 40 35 117 rquadto +0 193 rlineto +0 62 28 93 rquadto +28 31 87 32 rquadto +0 68 rlineto +-59 1 -87 32 rquadto +-28 31 -28 93 rquadto +0 193 rlineto +0 75 -37 115 rquadto +-35 42 -104 42 rquadto +-70 0 rlineto +0 -68 rlineto +31 0 rlineto +closepath +} def +/P$1q { +0 -120 -35 -175 rquadto +-35 -54 -120 -54 rquadto +-84 0 -121 56 rquadto +-37 54 -37 173 rquadto +0 114 35 171 rquadto +37 57 118 57 rquadto +85 0 123 -54 rquadto +37 -56 37 -175 rquadto +} def +/P$z { +39 -20 rlineto +82 -42 148 -42 rquadto +151 0 151 162 rquadto +0 350 rlineto +56 14 rlineto +0 39 rlineto +-275 0 rlineto +0 -39 rlineto +50 -14 rlineto +0 -326 rlineto +0 -50 -21 -76 rquadto +-20 -28 -57 -28 rquadto +-45 0 -89 20 rquadto +0 410 rlineto +50 14 rlineto +0 39 rlineto +-273 0 rlineto +0 -39 rlineto +53 -14 rlineto +0 -445 rlineto +-53 -14 rlineto +0 -39 rlineto +214 0 rlineto +7 48 rlineto +} def +/P$25 { +0 -154 48 -278 rquadto +48 -123 150 -232 rquadto +92 0 rlineto +-100 110 -146 237 rquadto +-46 125 -46 275 rquadto +0 148 45 273 rquadto +46 125 148 239 rquadto +-92 0 rlineto +-101 -109 -150 -232 rquadto +-48 -125 -48 -279 rquadto +0 -1 rlineto +} def +/P$3 { +0 -26 rlineto +104 -12 rlineto +0 -573 rlineto +-25 0 rlineto +-123 0 -168 9 rquadto +-12 101 rlineto +-32 0 rlineto +0 -153 rlineto +575 0 rlineto +0 153 rlineto +-32 0 rlineto +-12 -101 rlineto +-15 -3 -65 -6 rquadto +-48 -3 -106 -3 rquadto +-25 0 rlineto +0 573 rlineto +104 12 rlineto +0 26 rlineto +-303 0 rlineto +} def +/P$3h { +101 15 rlineto +0 28 rlineto +-309 0 rlineto +0 -28 rlineto +101 -15 rlineto +0 -507 rlineto +-84 -17 rlineto +0 -28 rlineto +190 0 rlineto +0 553 rlineto +} def +/P$t { +0 -98 54 -153 rquadto +54 -54 156 -54 rquadto +110 0 162 81 rquadto +53 79 53 251 rquadto +0 165 -67 253 rquadto +-67 85 -187 85 rquadto +-79 0 -146 -15 rquadto +0 -114 rlineto +32 0 rlineto +15 70 rlineto +15 7 42 14 rquadto +26 4 53 4 rquadto +78 0 120 -68 rquadto +42 -68 46 -201 rquadto +-75 42 -151 42 rquadto +-85 0 -135 -51 rquadto +-48 -51 -48 -143 rquadto +closepath +} def +/P$32 { +96 0 142 40 rquadto +46 39 46 121 rquadto +0 403 rlineto +75 15 rlineto +0 28 rlineto +-164 0 rlineto +-12 -59 rlineto +-71 71 -184 71 rquadto +-153 0 -153 -178 rquadto +0 -59 23 -98 rquadto +23 -39 73 -59 rquadto +51 -20 148 -21 rquadto +89 -3 rlineto +0 -93 rlineto +0 -60 -23 -90 rquadto +-21 -29 -68 -29 rquadto +-64 0 -115 31 rquadto +-21 73 rlineto +-35 0 rlineto +0 -129 rlineto +103 -21 181 -21 rquadto +closepath +} def +/P$1f { +-87 0 -131 -45 rquadto +-43 -46 -43 -128 rquadto +0 -90 59 -137 rquadto +59 -48 190 -53 rquadto +131 -1 rlineto +0 -31 rlineto +0 -71 -31 -101 rquadto +-29 -31 -93 -31 rquadto +-64 0 -93 21 rquadto +-29 21 -35 70 rquadto +-101 -7 rlineto +25 -157 234 -157 rquadto +109 0 164 50 rquadto +56 50 56 145 rquadto +0 250 rlineto +0 43 10 65 rquadto +10 21 42 21 rquadto +14 0 32 -4 rquadto +0 60 rlineto +-37 7 -75 7 rquadto +-54 0 -79 -28 rquadto +-23 -28 -26 -87 rquadto +-3 0 rlineto +-37 65 -87 93 rquadto +-48 28 -118 28 rquadto +closepath +} def +/P$14 { +0 409 -257 409 rquadto +-125 0 -189 -104 rquadto +-62 -104 -62 -304 rquadto +0 -193 62 -296 rquadto +64 -104 193 -104 rquadto +123 0 187 103 rquadto +65 101 65 298 rquadto +closepath +} def +/P$36 { +0 10 rlineto +0 87 18 135 rquadto +18 48 59 75 rquadto +40 25 106 25 rquadto +34 0 81 -4 rquadto +46 -6 76 -14 rquadto +0 35 rlineto +-29 18 -82 34 rquadto +-51 14 -106 14 rquadto +-139 0 -203 -75 rquadto +-64 -75 -64 -240 rquadto +0 -156 64 -232 rquadto +65 -76 187 -76 rquadto +228 0 228 260 rquadto +0 51 rlineto +-365 0 rlineto +closepath +} def +/P$y { +-50 1 rlineto +-56 3 -78 31 rquadto +-21 26 -21 87 rquadto +0 50 17 73 rquadto +17 23 45 23 rquadto +40 0 87 -20 rquadto +0 -196 rlineto +} def +/P$1x { +0 -92 rlineto +96 0 rlineto +0 92 rlineto +-96 0 rlineto +closepath +} def +/P$3k { +0 314 -278 314 rquadto +-135 0 -204 -79 rquadto +-67 -81 -67 -234 rquadto +0 -151 67 -231 rquadto +68 -79 209 -79 rquadto +135 0 204 78 rquadto +68 78 68 232 rquadto +closepath +} def +/P$21 { +0 -368 rlineto +0 -84 -23 -115 rquadto +-23 -32 -82 -32 rquadto +-62 0 -98 46 rquadto +-35 46 -35 132 rquadto +0 337 rlineto +-95 0 rlineto +0 -457 rlineto +0 -101 -3 -123 rquadto +90 0 rlineto +1 3 1 15 rquadto +0 10 0 26 rquadto +1 14 3 57 rquadto +1 0 rlineto +31 -62 70 -85 rquadto +40 -25 100 -25 rquadto +65 0 103 26 rquadto +39 26 54 84 rquadto +1 0 rlineto +29 -59 71 -84 rquadto +43 -26 103 -26 rquadto +89 0 128 48 rquadto +40 46 40 156 rquadto +0 387 rlineto +-95 0 rlineto +0 -368 rlineto +0 -84 -23 -115 rquadto +-23 -32 -82 -32 rquadto +-64 0 -100 46 rquadto +-34 46 -34 132 rquadto +0 337 rlineto +-95 0 rlineto +} def +/P$19 { +-34 0 -51 37 rquadto +-17 37 -17 134 rquadto +126 0 rlineto +0 -78 -4 -109 rquadto +-4 -32 -18 -46 rquadto +-12 -15 -34 -15 rquadto +} def +/P$3q { +103 15 rlineto +0 28 rlineto +-309 0 rlineto +0 -28 rlineto +101 -15 rlineto +0 -814 rlineto +-101 -15 rlineto +0 -28 rlineto +206 0 rlineto +0 857 rlineto +} def +/P$1r { +-46 14 -96 14 rquadto +-115 0 -115 -132 rquadto +0 -387 rlineto +-67 0 rlineto +0 -70 rlineto +70 0 rlineto +28 -129 rlineto +65 0 rlineto +0 129 rlineto +106 0 rlineto +0 70 rlineto +-106 0 rlineto +0 367 rlineto +0 42 12 59 rquadto +14 15 48 15 rquadto +18 0 54 -6 rquadto +0 70 rlineto +} def +/P$1o { +-125 0 -195 81 rquadto +-70 81 -70 221 rquadto +0 139 73 223 rquadto +73 84 196 84 rquadto +159 0 239 -157 rquadto +84 42 rlineto +-46 98 -132 150 rquadto +-84 50 -195 50 rquadto +-114 0 -198 -46 rquadto +-82 -48 -126 -135 rquadto +-43 -89 -43 -209 rquadto +0 -181 96 -284 rquadto +98 -103 271 -103 rquadto +120 0 201 48 rquadto +81 46 118 139 rquadto +-96 32 rlineto +-26 -65 -85 -100 rquadto +-57 -35 -137 -35 rquadto +} def +/P$2z { +0 -121 -32 -173 rquadto +-32 -53 -104 -53 rquadto +-56 0 -89 25 rquadto +-32 23 -50 75 rquadto +-17 51 -17 132 rquadto +0 115 35 170 rquadto +37 53 120 53 rquadto +71 0 104 -53 rquadto +32 -53 32 -176 rquadto +} def +/P$6 { +0 9 rlineto +0 67 14 104 rquadto +15 37 46 57 rquadto +31 18 81 18 rquadto +26 0 62 -4 rquadto +35 -4 59 -9 rquadto +0 26 rlineto +-23 15 -64 26 rquadto +-39 10 -81 10 rquadto +-107 0 -157 -57 rquadto +-48 -57 -48 -184 rquadto +0 -120 50 -179 rquadto +50 -59 143 -59 rquadto +176 0 176 201 rquadto +0 39 rlineto +-282 0 rlineto +closepath +} def +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +3720 2533 moveto +P$0 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +4662 2302 moveto +P$1 +4573 2302 moveto +P$2 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +11603 2533 moveto +P$3 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +12203 2402 moveto +P$4 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +20020 2549 moveto +P$5 +20376 2302 moveto +P$6 +20482 2100 moveto +P$7 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +27603 2533 moveto +P$3 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +28209 2038 moveto +P$8 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +35757 2239 moveto +P$9 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +36475 2061 moveto +P$a +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +43567 2357 moveto +P$b +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +44326 2064 moveto +P$c +44392 2299 moveto +P$d +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +51517 2357 moveto +P$b +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +52203 2402 moveto +P$4 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +59720 2533 moveto +P$0 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +60662 2302 moveto +P$1 +60573 2302 moveto +P$2 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +67603 2533 moveto +P$3 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +68203 2402 moveto +P$4 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +76020 2549 moveto +P$5 +76376 2302 moveto +P$6 +76482 2100 moveto +P$7 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +83603 2533 moveto +P$3 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +84209 2038 moveto +P$8 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +91757 2239 moveto +P$9 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +92475 2061 moveto +P$a +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +99567 2357 moveto +P$b +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +100326 2064 moveto +P$c +100392 2299 moveto +P$d +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +3960 3555 moveto +P$e +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +4462 3403 moveto +P$f +4371 3403 moveto +P$g +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +11960 3555 moveto +P$e +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +12306 3694 moveto +P$h +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +20056 3694 moveto +P$h +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +28195 3733 moveto +P$i +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +36210 3555 moveto +P$j +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +44145 3589 moveto +P$k +44060 3189 moveto +P$l +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +51987 3350 moveto +P$m +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +60220 3530 moveto +P$n +60018 3705 moveto +P$o +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +67848 3233 moveto +P$p +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +76192 3238 moveto +P$q +76123 3557 moveto +P$r +76104 3238 moveto +P$s +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +83782 3278 moveto +P$t +83993 3110 moveto +P$u +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +91806 3694 moveto +P$v +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +92462 3403 moveto +P$f +92371 3403 moveto +P$g +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +99806 3694 moveto +P$v +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +100306 3694 moveto +P$h +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +4793 392 moveto +P$w +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +5420 557 moveto +P$x +5457 843 moveto +P$y +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +5954 617 moveto +P$z +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +6812 1071 moveto +P$10 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +7420 557 moveto +P$x +7457 843 moveto +P$y +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +7970 684 moveto +P$11 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +8470 1137 moveto +P$12 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +9648 1120 moveto +P$13 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +10254 723 moveto +P$14 +10082 723 moveto +P$15 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +10848 1120 moveto +P$13 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +11459 906 moveto +P$16 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +56556 784 moveto +P$17 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +57239 556 moveto +P$18 +57232 615 moveto +P$19 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +57896 829 moveto +P$1a +57521 339 moveto +P$1b +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +58420 684 moveto +P$11 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +59062 1071 moveto +P$10 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +59670 557 moveto +P$x +59707 843 moveto +P$y +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +60220 684 moveto +P$11 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +60720 1137 moveto +P$12 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +61898 1120 moveto +P$13 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +62504 723 moveto +P$14 +62332 723 moveto +P$15 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +63098 1120 moveto +P$13 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +63709 906 moveto +P$16 +fill +100 setlinewidth +0.75 0.75 0.75 setrgbcolor +newpath +0 4100 moveto +0 40843 rlineto +stroke +100 setlinewidth +0.75 0.75 0.75 setrgbcolor +newpath +8000 4100 moveto +0 40843 rlineto +stroke +100 setlinewidth +0.75 0.75 0.75 setrgbcolor +newpath +16000 4100 moveto +0 40843 rlineto +stroke +100 setlinewidth +0.75 0.75 0.75 setrgbcolor +newpath +24000 4100 moveto +0 40843 rlineto +stroke +100 setlinewidth +0.75 0.75 0.75 setrgbcolor +newpath +32000 4100 moveto +0 40843 rlineto +stroke +100 setlinewidth +0.75 0.75 0.75 setrgbcolor +newpath +40000 4100 moveto +0 40843 rlineto +stroke +100 setlinewidth +0.75 0.75 0.75 setrgbcolor +newpath +48000 4100 moveto +0 40843 rlineto +stroke +100 setlinewidth +0.75 0.75 0.75 setrgbcolor +newpath +56000 4100 moveto +0 40843 rlineto +stroke +100 setlinewidth +0.75 0.75 0.75 setrgbcolor +newpath +64000 4100 moveto +0 40843 rlineto +stroke +100 setlinewidth +0.75 0.75 0.75 setrgbcolor +newpath +72000 4100 moveto +0 40843 rlineto +stroke +100 setlinewidth +0.75 0.75 0.75 setrgbcolor +newpath +80000 4100 moveto +0 40843 rlineto +stroke +100 setlinewidth +0.75 0.75 0.75 setrgbcolor +newpath +88000 4100 moveto +0 40843 rlineto +stroke +100 setlinewidth +0.75 0.75 0.75 setrgbcolor +newpath +96000 4100 moveto +0 40843 rlineto +stroke +100 setlinewidth +0.75 0.75 0.75 setrgbcolor +newpath +104000 4100 moveto +0 40843 rlineto +stroke +100 setlinewidth +0.75 0.75 0.75 setrgbcolor +newpath +0 4100 moveto +104000 0 rlineto +stroke +100 setlinewidth +0.75 0.75 0.75 setrgbcolor +newpath +0 44943 moveto +104000 0 rlineto +stroke +150 setlinewidth +0.09 0.09 0.09 setrgbcolor +newpath +12000 8829 moveto +0 1032 rlineto +stroke +150 setlinewidth +0.09 0.09 0.09 setrgbcolor +newpath +12000 9862 moveto +3900 0 rlineto +stroke +0.09 0.09 0.09 setrgbcolor +newpath +15500 9462 moveto +0 400 rlineto +0 400 rlineto +400 -400 rlineto +15500 9462 lineto +closepath eofill +150 setlinewidth +0.09 0.09 0.09 setrgbcolor +newpath +15500 9462 moveto +0 400 rlineto +0 400 rlineto +400 -400 rlineto +15500 9462 lineto +closepath stroke +150 setlinewidth +0.09 0.09 0.09 setrgbcolor +newpath +4000 13824 moveto +0 1032 rlineto +stroke +150 setlinewidth +0.09 0.09 0.09 setrgbcolor +newpath +4000 14856 moveto +3900 0 rlineto +stroke +0.09 0.09 0.09 setrgbcolor +newpath +7500 14456 moveto +0 400 rlineto +0 400 rlineto +400 -400 rlineto +7500 14456 lineto +closepath eofill +150 setlinewidth +0.09 0.09 0.09 setrgbcolor +newpath +7500 14456 moveto +0 400 rlineto +0 400 rlineto +400 -400 rlineto +7500 14456 lineto +closepath stroke +150 setlinewidth +0.09 0.09 0.09 setrgbcolor +newpath +12000 8829 moveto +0 2697 rlineto +stroke +150 setlinewidth +0.09 0.09 0.09 setrgbcolor +newpath +12000 11527 moveto +3900 0 rlineto +stroke +0.09 0.09 0.09 setrgbcolor +newpath +15500 11127 moveto +0 400 rlineto +0 400 rlineto +400 -400 rlineto +15500 11127 lineto +closepath eofill +150 setlinewidth +0.09 0.09 0.09 setrgbcolor +newpath +15500 11127 moveto +0 400 rlineto +0 400 rlineto +400 -400 rlineto +15500 11127 lineto +closepath stroke +150 setlinewidth +0.09 0.09 0.09 setrgbcolor +newpath +36000 15489 moveto +0 2697 rlineto +stroke +150 setlinewidth +0.09 0.09 0.09 setrgbcolor +newpath +36000 18186 moveto +3900 0 rlineto +stroke +0.09 0.09 0.09 setrgbcolor +newpath +39500 17786 moveto +0 400 rlineto +0 400 rlineto +400 -400 rlineto +39500 17786 lineto +closepath eofill +150 setlinewidth +0.09 0.09 0.09 setrgbcolor +newpath +39500 17786 moveto +0 400 rlineto +0 400 rlineto +400 -400 rlineto +39500 17786 lineto +closepath stroke +150 setlinewidth +0.09 0.09 0.09 setrgbcolor +newpath +36000 15489 moveto +0 1032 rlineto +stroke +150 setlinewidth +0.09 0.09 0.09 setrgbcolor +newpath +36000 16521 moveto +3900 0 rlineto +stroke +0.09 0.09 0.09 setrgbcolor +newpath +39500 16121 moveto +0 400 rlineto +0 400 rlineto +400 -400 rlineto +39500 16121 lineto +closepath eofill +150 setlinewidth +0.09 0.09 0.09 setrgbcolor +newpath +39500 16121 moveto +0 400 rlineto +0 400 rlineto +400 -400 rlineto +39500 16121 lineto +closepath stroke +150 setlinewidth +0.09 0.09 0.09 setrgbcolor +newpath +68000 18819 moveto +0 1032 rlineto +stroke +150 setlinewidth +0.09 0.09 0.09 setrgbcolor +newpath +68000 19851 moveto +3900 0 rlineto +stroke +0.09 0.09 0.09 setrgbcolor +newpath +71500 19451 moveto +0 400 rlineto +0 400 rlineto +400 -400 rlineto +71500 19451 lineto +closepath eofill +150 setlinewidth +0.09 0.09 0.09 setrgbcolor +newpath +71500 19451 moveto +0 400 rlineto +0 400 rlineto +400 -400 rlineto +71500 19451 lineto +closepath stroke +150 setlinewidth +0.09 0.09 0.09 setrgbcolor +newpath +68000 18819 moveto +0 2697 rlineto +stroke +150 setlinewidth +0.09 0.09 0.09 setrgbcolor +newpath +68000 21516 moveto +3900 0 rlineto +stroke +0.09 0.09 0.09 setrgbcolor +newpath +71500 21116 moveto +0 400 rlineto +0 400 rlineto +400 -400 rlineto +71500 21116 lineto +closepath eofill +150 setlinewidth +0.09 0.09 0.09 setrgbcolor +newpath +71500 21116 moveto +0 400 rlineto +0 400 rlineto +400 -400 rlineto +71500 21116 lineto +closepath stroke +150 setlinewidth +0.09 0.09 0.09 setrgbcolor +newpath +68000 18819 moveto +0 9292 rlineto +stroke +150 setlinewidth +0.09 0.09 0.09 setrgbcolor +newpath +68000 28111 moveto +3900 0 rlineto +stroke +0.09 0.09 0.09 setrgbcolor +newpath +71500 27711 moveto +0 400 rlineto +0 400 rlineto +400 -400 rlineto +71500 27711 lineto +closepath eofill +150 setlinewidth +0.09 0.09 0.09 setrgbcolor +newpath +71500 27711 moveto +0 400 rlineto +0 400 rlineto +400 -400 rlineto +71500 27711 lineto +closepath stroke +100 setlinewidth +0.09 0.09 0.09 setrgbcolor +newpath +0 5732 moveto +500 0 rlineto +stroke +100 setlinewidth +0.09 0.09 0.09 setrgbcolor +newpath +5800 5732 moveto +98100 0 rlineto +stroke +0.89 0.89 0.94 setrgbcolor +15600 1264 200 7564 simplerect +closepath eofill +100 setlinewidth +0.09 0.09 0.09 setrgbcolor +newpath +200 7564 moveto +15600 0 rlineto +stroke +100 setlinewidth +0.09 0.09 0.09 setrgbcolor +newpath +200 8829 moveto +15600 0 rlineto +stroke +100 setlinewidth +0.09 0.09 0.09 setrgbcolor +newpath +200 7564 moveto +0 1264 rlineto +stroke +100 setlinewidth +0.09 0.09 0.09 setrgbcolor +newpath +15800 7564 moveto +0 1264 rlineto +stroke +0.89 0.89 0.94 setrgbcolor +15600 1264 16200 9229 simplerect +closepath eofill +100 setlinewidth +0.09 0.09 0.09 setrgbcolor +newpath +16200 9229 moveto +15600 0 rlineto +stroke +100 setlinewidth +0.09 0.09 0.09 setrgbcolor +newpath +16200 10494 moveto +15600 0 rlineto +stroke +100 setlinewidth +0.09 0.09 0.09 setrgbcolor +newpath +16200 9229 moveto +0 1264 rlineto +stroke +100 setlinewidth +0.09 0.09 0.09 setrgbcolor +newpath +31800 9229 moveto +0 1264 rlineto +stroke +0.89 0.89 0.94 setrgbcolor +31600 1264 16200 10894 simplerect +closepath eofill +100 setlinewidth +0.09 0.09 0.09 setrgbcolor +newpath +16200 10894 moveto +31600 0 rlineto +stroke +100 setlinewidth +0.09 0.09 0.09 setrgbcolor +newpath +16200 12159 moveto +31600 0 rlineto +stroke +100 setlinewidth +0.09 0.09 0.09 setrgbcolor +newpath +16200 10894 moveto +0 1264 rlineto +stroke +100 setlinewidth +0.09 0.09 0.09 setrgbcolor +newpath +47800 10894 moveto +0 1264 rlineto +stroke +0.89 0.89 0.94 setrgbcolor +7600 1264 200 12559 simplerect +closepath eofill +100 setlinewidth +0.09 0.09 0.09 setrgbcolor +newpath +200 12559 moveto +7600 0 rlineto +stroke +100 setlinewidth +0.09 0.09 0.09 setrgbcolor +newpath +200 13824 moveto +7600 0 rlineto +stroke +100 setlinewidth +0.09 0.09 0.09 setrgbcolor +newpath +200 12559 moveto +0 1264 rlineto +stroke +100 setlinewidth +0.09 0.09 0.09 setrgbcolor +newpath +7800 12559 moveto +0 1264 rlineto +stroke +0.89 0.89 0.94 setrgbcolor +31600 1264 8200 14224 simplerect +closepath eofill +100 setlinewidth +0.09 0.09 0.09 setrgbcolor +newpath +8200 14224 moveto +31600 0 rlineto +stroke +100 setlinewidth +0.09 0.09 0.09 setrgbcolor +newpath +8200 15489 moveto +31600 0 rlineto +stroke +100 setlinewidth +0.09 0.09 0.09 setrgbcolor +newpath +8200 14224 moveto +0 1264 rlineto +stroke +100 setlinewidth +0.09 0.09 0.09 setrgbcolor +newpath +39800 14224 moveto +0 1264 rlineto +stroke +0.89 0.89 0.94 setrgbcolor +23600 1264 40200 15889 simplerect +closepath eofill +100 setlinewidth +0.09 0.09 0.09 setrgbcolor +newpath +40200 15889 moveto +23600 0 rlineto +stroke +100 setlinewidth +0.09 0.09 0.09 setrgbcolor +newpath +40200 17154 moveto +23600 0 rlineto +stroke +100 setlinewidth +0.09 0.09 0.09 setrgbcolor +newpath +40200 15889 moveto +0 1264 rlineto +stroke +100 setlinewidth +0.09 0.09 0.09 setrgbcolor +newpath +63800 15889 moveto +0 1264 rlineto +stroke +0.89 0.89 0.94 setrgbcolor +31600 1264 40200 17554 simplerect +closepath eofill +100 setlinewidth +0.09 0.09 0.09 setrgbcolor +newpath +40200 17554 moveto +31600 0 rlineto +stroke +100 setlinewidth +0.09 0.09 0.09 setrgbcolor +newpath +40200 18819 moveto +31600 0 rlineto +stroke +100 setlinewidth +0.09 0.09 0.09 setrgbcolor +newpath +40200 17554 moveto +0 1264 rlineto +stroke +100 setlinewidth +0.09 0.09 0.09 setrgbcolor +newpath +71800 17554 moveto +0 1264 rlineto +stroke +0.89 0.89 0.94 setrgbcolor +31600 1264 72200 19219 simplerect +closepath eofill +100 setlinewidth +0.09 0.09 0.09 setrgbcolor +newpath +72200 19219 moveto +31600 0 rlineto +stroke +100 setlinewidth +0.09 0.09 0.09 setrgbcolor +newpath +72200 20484 moveto +31600 0 rlineto +stroke +100 setlinewidth +0.09 0.09 0.09 setrgbcolor +newpath +72200 19219 moveto +0 1264 rlineto +stroke +100 setlinewidth +0.09 0.09 0.09 setrgbcolor +newpath +103800 19219 moveto +0 1264 rlineto +stroke +0.89 0.89 0.94 setrgbcolor +15600 1264 72200 20884 simplerect +closepath eofill +100 setlinewidth +0.09 0.09 0.09 setrgbcolor +newpath +72200 20884 moveto +15600 0 rlineto +stroke +100 setlinewidth +0.09 0.09 0.09 setrgbcolor +newpath +72200 22148 moveto +15600 0 rlineto +stroke +100 setlinewidth +0.09 0.09 0.09 setrgbcolor +newpath +72200 20884 moveto +0 1264 rlineto +stroke +100 setlinewidth +0.09 0.09 0.09 setrgbcolor +newpath +87800 20884 moveto +0 1264 rlineto +stroke +100 setlinewidth +0.09 0.09 0.09 setrgbcolor +newpath +0 23981 moveto +500 0 rlineto +stroke +100 setlinewidth +0.09 0.09 0.09 setrgbcolor +newpath +5900 23981 moveto +98000 0 rlineto +stroke +0.89 0.89 0.94 setrgbcolor +39600 1264 200 25813 simplerect +closepath eofill +100 setlinewidth +0.09 0.09 0.09 setrgbcolor +newpath +200 25813 moveto +39600 0 rlineto +stroke +100 setlinewidth +0.09 0.09 0.09 setrgbcolor +newpath +200 27078 moveto +39600 0 rlineto +stroke +100 setlinewidth +0.09 0.09 0.09 setrgbcolor +newpath +200 25813 moveto +0 1264 rlineto +stroke +100 setlinewidth +0.09 0.09 0.09 setrgbcolor +newpath +39800 25813 moveto +0 1264 rlineto +stroke +0.89 0.89 0.94 setrgbcolor +15600 1264 72200 27478 simplerect +closepath eofill +100 setlinewidth +0.09 0.09 0.09 setrgbcolor +newpath +72200 27478 moveto +15600 0 rlineto +stroke +100 setlinewidth +0.09 0.09 0.09 setrgbcolor +newpath +72200 28743 moveto +15600 0 rlineto +stroke +100 setlinewidth +0.09 0.09 0.09 setrgbcolor +newpath +72200 27478 moveto +0 1264 rlineto +stroke +100 setlinewidth +0.09 0.09 0.09 setrgbcolor +newpath +87800 27478 moveto +0 1264 rlineto +stroke +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +1675 5919 moveto +P$1c +1528 5570 moveto +P$1d +1571 5909 moveto +P$1e +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +1921 6142 moveto +P$1f +1943 6069 moveto +P$1g +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +2448 5838 moveto +P$1h +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +3339 6131 moveto +P$1i +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +3648 5861 moveto +P$1j +3962 5788 moveto +P$1k +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +4543 6131 moveto +P$1l +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +5140 6038 moveto +P$1m +4848 5841 moveto +P$1n +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +1025 7912 moveto +P$1o +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +1965 8306 moveto +P$1p +1864 8306 moveto +P$1q +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +2443 8596 moveto +P$1l +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +2896 8591 moveto +P$1r +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +2976 8596 moveto +P$1s +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +3865 8306 moveto +P$1p +3764 8306 moveto +P$1q +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +3973 8596 moveto +P$1t +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +4173 8596 moveto +P$1t +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +4448 8326 moveto +P$1j +4762 8252 moveto +P$1k +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +4976 8596 moveto +P$1s +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +5348 8346 moveto +P$1u +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +6382 8387 moveto +P$1v +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +6548 8302 moveto +P$1h +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +7170 8115 moveto +P$1w +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +7673 7891 moveto +P$1x +7673 8596 moveto +P$1y +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +7948 8302 moveto +P$1h +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +8570 8115 moveto +P$1w +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +9296 8591 moveto +P$1r +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +9875 8824 moveto +P$1z +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +10101 8596 moveto +P$20 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +10712 8596 moveto +P$21 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +11612 8596 moveto +P$21 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +12321 8607 moveto +P$1f +12343 8534 moveto +P$1g +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +13143 8596 moveto +P$1l +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +13468 8015 moveto +P$22 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +14048 8326 moveto +P$1j +14362 8252 moveto +P$1k +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +14573 8596 moveto +P$1t +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +14750 8756 moveto +P$23 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +32882 10052 moveto +P$1v +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +33048 9991 moveto +P$1j +33362 9917 moveto +P$1k +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +33576 10261 moveto +P$1s +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +34229 10261 moveto +P$24 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +34573 9556 moveto +P$1x +34573 10261 moveto +P$1y +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +34848 9967 moveto +P$1h +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +35448 9991 moveto +P$1j +35762 9917 moveto +P$1k +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +35948 10011 moveto +P$1u +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +36982 10052 moveto +P$1v +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +37148 9967 moveto +P$1h +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +37770 9780 moveto +P$1w +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +38273 9556 moveto +P$1x +38273 10261 moveto +P$1y +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +38548 9967 moveto +P$1h +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +39170 9780 moveto +P$1w +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +39896 10256 moveto +P$1r +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +40268 9975 moveto +P$25 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +41342 9875 moveto +P$26 +41239 9875 moveto +P$27 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +41621 10272 moveto +P$1f +41643 10199 moveto +P$1g +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +42296 10256 moveto +P$1r +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +42448 9991 moveto +P$1j +42762 9917 moveto +P$1k +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +43343 10261 moveto +P$1l +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +44240 10167 moveto +P$1m +43948 9970 moveto +P$28 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +44568 9680 moveto +P$22 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +45076 10261 moveto +P$1s +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +45548 9967 moveto +P$1h +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +46170 9780 moveto +P$1w +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +46676 10261 moveto +P$1s +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +47148 9991 moveto +P$1j +47462 9917 moveto +P$1k +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +47673 9556 moveto +P$1x +47673 10261 moveto +P$1y +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +47948 9967 moveto +P$1h +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +48570 9780 moveto +P$1w +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +49148 9991 moveto +P$1j +49462 9917 moveto +P$1k +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +50043 10261 moveto +P$1l +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +50498 9977 moveto +P$29 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +51175 10489 moveto +P$1z +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +52042 9875 moveto +P$26 +51939 9875 moveto +P$27 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +52321 10272 moveto +P$1f +52343 10199 moveto +P$1g +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +53143 10261 moveto +P$1l +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +53373 9556 moveto +P$1x +53373 10261 moveto +P$1y +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +53648 9991 moveto +P$1j +53962 9917 moveto +P$1k +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +54173 10261 moveto +P$1t +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +54350 10420 moveto +P$23 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +17226 11926 moveto +P$2a +16967 11246 moveto +P$2b +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +17468 11345 moveto +P$22 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +18196 11921 moveto +P$1r +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +18370 11445 moveto +P$1w +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +18948 11656 moveto +P$1j +19262 11582 moveto +P$1k +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +19843 11926 moveto +P$1l +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +20296 11921 moveto +P$1r +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +20373 11221 moveto +P$1x +20373 11926 moveto +P$1y +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +20693 11415 moveto +P$2c +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +20873 11221 moveto +P$1x +20873 11926 moveto +P$1y +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +21045 11926 moveto +P$2d +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +21673 11221 moveto +P$1x +21673 11926 moveto +P$1y +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +21948 11656 moveto +P$1j +22262 11582 moveto +P$1k +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +22476 11926 moveto +P$1s +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +22968 11345 moveto +P$22 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +23843 11926 moveto +P$1l +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +24293 12154 moveto +P$2e +24442 11635 moveto +P$2f +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +25175 12154 moveto +P$1z +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +25354 11545 moveto +P$2g +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +26348 11656 moveto +P$1j +26662 11582 moveto +P$1k +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +26876 11926 moveto +P$1s +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +27765 11635 moveto +P$1p +27664 11635 moveto +P$1q +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +27850 12085 moveto +P$23 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +8934 13591 moveto +P$2h +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +9665 13300 moveto +P$1p +9564 13300 moveto +P$1q +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +10140 13497 moveto +P$1m +9848 13300 moveto +P$28 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +10448 13321 moveto +P$1j +10762 13247 moveto +P$1k +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +10973 13591 moveto +P$1t +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +11148 13341 moveto +P$1u +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +12175 13061 moveto +P$2i +12071 13063 moveto +P$2j +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +12421 13602 moveto +P$1f +12443 13528 moveto +P$1g +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +13239 13591 moveto +P$1i +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +13548 13321 moveto +P$1j +13862 13247 moveto +P$1k +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +14296 13586 moveto +P$1r +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +14875 13819 moveto +P$1z +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +15742 13205 moveto +P$26 +15639 13205 moveto +P$27 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +16021 13602 moveto +P$1f +16043 13528 moveto +P$1g +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +16843 13591 moveto +P$1l +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +17073 12886 moveto +P$1x +17073 13591 moveto +P$1y +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +17348 13321 moveto +P$1j +17662 13247 moveto +P$1k +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +17873 13591 moveto +P$1t +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +18050 13750 moveto +P$23 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +9342 14870 moveto +P$26 +9239 14870 moveto +P$27 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +9621 15267 moveto +P$1f +9643 15193 moveto +P$1g +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +10296 15251 moveto +P$1r +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +10448 14985 moveto +P$1j +10762 14912 moveto +P$1k +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +11343 15256 moveto +P$1l +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +12065 14962 moveto +P$2k +11964 14965 moveto +P$2l +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +12321 15267 moveto +P$1f +12343 15193 moveto +P$1g +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +13143 15256 moveto +P$1l +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +13739 15256 moveto +P$1i +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +14421 15267 moveto +P$1f +14443 15193 moveto +P$1g +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +14968 14675 moveto +P$22 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +15593 14745 moveto +P$2c +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +16210 15095 moveto +P$2m +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +16448 14985 moveto +P$1j +16762 14912 moveto +P$1k +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +17196 15251 moveto +P$1r +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +17245 15256 moveto +P$2d +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +17948 14985 moveto +P$1j +18262 14912 moveto +P$1k +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +18843 15256 moveto +P$1l +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +19575 15484 moveto +P$1z +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +19801 15256 moveto +P$20 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +20412 15256 moveto +P$21 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +21312 15256 moveto +P$21 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +22021 15267 moveto +P$1f +22043 15193 moveto +P$1g +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +22843 15256 moveto +P$1l +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +23168 14675 moveto +P$22 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +23748 14985 moveto +P$1j +24062 14912 moveto +P$1k +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +24273 15256 moveto +P$1t +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +24450 15415 moveto +P$23 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +40992 16932 moveto +P$2n +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +41696 16916 moveto +P$1r +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +41773 16216 moveto +P$1x +41773 16921 moveto +P$1y +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +41973 16921 moveto +P$1t +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +42148 16671 moveto +P$1u +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +43175 16391 moveto +P$2i +43071 16393 moveto +P$2j +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +43421 16932 moveto +P$1f +43443 16858 moveto +P$1g +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +44239 16921 moveto +P$1i +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +44548 16650 moveto +P$1j +44862 16577 moveto +P$1k +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +45296 16916 moveto +P$1r +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +45668 16635 moveto +P$25 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +46625 16921 moveto +P$2o +46610 16382 moveto +P$2p +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +47482 16711 moveto +P$1v +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +48182 16711 moveto +P$1v +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +48875 16391 moveto +P$2i +48771 16393 moveto +P$2j +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +49121 16932 moveto +P$1f +49143 16858 moveto +P$1g +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +49576 16921 moveto +P$1s +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +50410 16760 moveto +P$2m +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +50648 16650 moveto +P$1j +50962 16577 moveto +P$1k +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +51176 16921 moveto +P$1s +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +51798 16636 moveto +P$29 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +52475 17149 moveto +P$1z +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +53342 16535 moveto +P$26 +53239 16535 moveto +P$27 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +53621 16932 moveto +P$1f +53643 16858 moveto +P$1g +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +54443 16921 moveto +P$1l +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +54673 16216 moveto +P$1x +54673 16921 moveto +P$1y +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +54948 16650 moveto +P$1j +55262 16577 moveto +P$1k +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +55473 16921 moveto +P$1t +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +55650 17080 moveto +P$2q +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +56575 17149 moveto +P$1z +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +56790 16921 moveto +P$2r +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +57468 16339 moveto +P$22 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +58339 16921 moveto +P$1i +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +58721 16932 moveto +P$1f +58743 16858 moveto +P$1g +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +59610 16760 moveto +P$2m +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +59750 17080 moveto +P$23 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +41342 18200 moveto +P$26 +41239 18200 moveto +P$27 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +41621 18596 moveto +P$1f +41643 18523 moveto +P$1g +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +42296 18581 moveto +P$1r +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +42521 18596 moveto +P$1f +42543 18523 moveto +P$2s +43526 18586 moveto +P$2a +43267 17906 moveto +P$2b +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +43748 18292 moveto +P$1h +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +44348 18292 moveto +P$1h +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +44948 18315 moveto +P$1j +45262 18242 moveto +P$1k +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +45910 18425 moveto +P$2m +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +46510 18425 moveto +P$2m +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +46648 18336 moveto +P$1u +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +47682 18376 moveto +P$1v +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +47848 18292 moveto +P$1h +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +48470 18104 moveto +P$1w +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +48973 17881 moveto +P$1x +48973 18586 moveto +P$1y +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +49248 18292 moveto +P$1h +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +49870 18104 moveto +P$1w +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +50596 18581 moveto +P$1r +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +51175 18814 moveto +P$1z +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +51401 18586 moveto +P$20 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +52012 18586 moveto +P$21 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +52912 18586 moveto +P$21 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +53621 18596 moveto +P$1f +53643 18523 moveto +P$1g +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +54443 18586 moveto +P$1l +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +54768 18004 moveto +P$22 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +55348 18315 moveto +P$1j +55662 18242 moveto +P$1k +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +55873 18586 moveto +P$1t +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +56050 18745 moveto +P$2q +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +56975 18814 moveto +P$1z +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +57345 18596 moveto +P$2t +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +57868 18004 moveto +P$22 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +58373 18586 moveto +P$1t +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +58573 17881 moveto +P$1x +58573 18586 moveto +P$1y +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +58868 18004 moveto +P$22 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +59810 18425 moveto +P$2m +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +59950 18745 moveto +P$23 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +73282 20041 moveto +P$1v +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +73448 19980 moveto +P$1j +73762 19907 moveto +P$1k +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +73976 20250 moveto +P$1s +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +74629 20250 moveto +P$24 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +74973 19546 moveto +P$1x +74973 20250 moveto +P$1y +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +75248 19957 moveto +P$1h +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +75848 19980 moveto +P$1j +76162 19907 moveto +P$1k +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +76348 20000 moveto +P$1u +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +77382 20041 moveto +P$1v +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +77548 19957 moveto +P$1h +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +78170 19769 moveto +P$1w +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +78673 19546 moveto +P$1x +78673 20250 moveto +P$1y +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +78948 19957 moveto +P$1h +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +79570 19769 moveto +P$1w +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +80296 20246 moveto +P$1r +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +80668 19964 moveto +P$25 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +81054 19869 moveto +P$2g +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +82048 19980 moveto +P$1j +82362 19907 moveto +P$1k +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +83010 20089 moveto +P$2m +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +83248 19957 moveto +P$1h +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +83870 19769 moveto +P$1w +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +84521 20261 moveto +P$1f +84543 20188 moveto +P$2s +84662 19596 moveto +P$2u +84450 19596 moveto +P$2v +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +85093 19739 moveto +P$2c +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +85496 20246 moveto +P$1r +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +86010 20089 moveto +P$2m +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +86173 20250 moveto +P$1t +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +86865 19960 moveto +P$1p +86764 19960 moveto +P$1q +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +87193 20479 moveto +P$2e +87342 19960 moveto +P$2f +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +87573 19546 moveto +P$1x +87573 20250 moveto +P$1y +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +88139 20250 moveto +P$1i +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +88598 19966 moveto +P$29 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +89275 20479 moveto +P$1z +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +90142 19864 moveto +P$26 +90039 19864 moveto +P$27 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +90421 20261 moveto +P$1f +90443 20188 moveto +P$1g +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +91243 20250 moveto +P$1l +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +91473 19546 moveto +P$1x +91473 20250 moveto +P$1y +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +91748 19980 moveto +P$1j +92062 19907 moveto +P$1k +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +92273 20250 moveto +P$1t +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +92450 20410 moveto +P$2q +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +93375 20479 moveto +P$1z +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +93601 20250 moveto +P$20 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +94212 20250 moveto +P$21 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +95112 20250 moveto +P$21 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +95821 20261 moveto +P$1f +95843 20188 moveto +P$1g +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +96643 20250 moveto +P$1l +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +96968 19669 moveto +P$22 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +97548 19980 moveto +P$1j +97862 19907 moveto +P$1k +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +98073 20250 moveto +P$1t +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +98250 20410 moveto +P$23 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +88592 21926 moveto +P$2n +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +89296 21911 moveto +P$1r +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +89373 21211 moveto +P$1x +89373 21915 moveto +P$1y +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +89573 21915 moveto +P$1t +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +89748 21665 moveto +P$1u +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +90775 21386 moveto +P$2i +90671 21387 moveto +P$2j +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +91021 21926 moveto +P$1f +91043 21853 moveto +P$1g +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +91839 21915 moveto +P$1i +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +92148 21645 moveto +P$1j +92462 21572 moveto +P$1k +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +92896 21911 moveto +P$1r +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +93268 21629 moveto +P$25 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +94025 21231 moveto +P$1o +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +94473 21915 moveto +P$1t +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +94748 21645 moveto +P$1j +95062 21572 moveto +P$1k +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +95421 21926 moveto +P$1f +95443 21853 moveto +P$1g +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +96243 21915 moveto +P$1l +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +96825 21231 moveto +P$1o +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +97276 21915 moveto +P$1s +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +98165 21625 moveto +P$1p +98064 21625 moveto +P$1q +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +98643 21915 moveto +P$1l +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +99045 21926 moveto +P$2t +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +99965 21625 moveto +P$1p +99864 21625 moveto +P$1q +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +100565 21622 moveto +P$2k +100464 21625 moveto +P$2l +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +100898 21631 moveto +P$29 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +101575 22143 moveto +P$1z +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +101945 21926 moveto +P$2t +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +102468 21334 moveto +P$22 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +102973 21915 moveto +P$1t +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +103173 21211 moveto +P$1x +103173 21915 moveto +P$1y +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +103468 21334 moveto +P$22 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +104410 21754 moveto +P$2m +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +104550 22075 moveto +P$23 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +1192 23707 moveto +P$2w +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +1776 24380 moveto +P$1s +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +2665 24090 moveto +P$1p +2564 24090 moveto +P$1q +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +3143 24380 moveto +P$1l +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +3596 24376 moveto +P$1r +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +3748 24110 moveto +P$1j +4062 24036 moveto +P$1k +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +4643 24380 moveto +P$1l +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +5240 24286 moveto +P$1m +4948 24090 moveto +P$1n +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +1193 26845 moveto +P$2x +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +1865 26554 moveto +P$1p +1764 26554 moveto +P$1q +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +2312 26845 moveto +P$21 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +3365 26551 moveto +P$2y +3264 26554 moveto +P$2z +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +3965 26554 moveto +P$1p +3864 26554 moveto +P$1q +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +4443 26845 moveto +P$1l +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +4748 26575 moveto +P$1j +5062 26501 moveto +P$1k +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +5643 26845 moveto +P$1l +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +6096 26840 moveto +P$1r +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +6248 26575 moveto +P$1j +6562 26501 moveto +P$1k +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +7143 26845 moveto +P$1l +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +7875 27073 moveto +P$1z +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +8054 26464 moveto +P$2g +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +9048 26575 moveto +P$1j +9362 26501 moveto +P$1k +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +9576 26845 moveto +P$1s +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +10465 26554 moveto +P$1p +10364 26554 moveto +P$1q +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +10550 27004 moveto +P$2q +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +11475 27073 moveto +P$1z +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +11845 26856 moveto +P$2t +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +12368 26264 moveto +P$22 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +12873 26845 moveto +P$1t +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +13073 26140 moveto +P$1x +13073 26845 moveto +P$1y +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +13368 26264 moveto +P$22 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +14310 26684 moveto +P$2m +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +14450 27004 moveto +P$2q +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +15375 27073 moveto +P$1z +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +15590 26845 moveto +P$2r +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +16268 26264 moveto +P$22 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +17139 26845 moveto +P$1i +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +17521 26856 moveto +P$1f +17543 26783 moveto +P$1g +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +18410 26684 moveto +P$2m +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +18550 27004 moveto +P$23 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +88826 28510 moveto +P$2a +88567 27830 moveto +P$2b +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +89575 27980 moveto +P$2i +89471 27982 moveto +P$2j +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +89701 28510 moveto +P$20 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +89948 28260 moveto +P$1u +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +90926 28510 moveto +P$2a +90667 27830 moveto +P$2b +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +91443 28510 moveto +P$1l +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +92165 28216 moveto +P$2k +92064 28219 moveto +P$2l +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +92273 27805 moveto +P$1x +92273 28510 moveto +P$1y +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +92843 28510 moveto +P$1l +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +93440 28416 moveto +P$1m +93148 28219 moveto +P$28 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +93768 27929 moveto +P$22 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +94643 28510 moveto +P$1l +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +95093 28738 moveto +P$2e +95242 28219 moveto +P$2f +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +95975 28738 moveto +P$1z +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +96154 28129 moveto +P$2g +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +97148 28240 moveto +P$1j +97462 28166 moveto +P$1k +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +97676 28510 moveto +P$1s +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +98565 28219 moveto +P$1p +98464 28219 moveto +P$1q +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +98650 28669 moveto +P$2q +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +99575 28738 moveto +P$1z +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +99790 28510 moveto +P$2r +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +100468 27929 moveto +P$22 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +101339 28510 moveto +P$1i +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +101721 28521 moveto +P$1f +101743 28447 moveto +P$1g +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +102610 28349 moveto +P$2m +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +102750 28669 moveto +P$23 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +278 30105 moveto +P$30 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +606 29608 moveto +P$31 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +1606 29608 moveto +P$31 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +2695 29547 moveto +P$32 +2779 29852 moveto +P$33 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +3206 29608 moveto +P$34 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +3898 29986 moveto +P$35 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +4565 29857 moveto +P$36 +4703 29594 moveto +P$37 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +5232 30113 moveto +P$38 +fill +100 setlinewidth +0 0 0 setrgbcolor +newpath +0 30438 moveto +104000 0 rlineto +stroke +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +3525 31347 moveto +P$39 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +4165 31086 moveto +P$3a +4084 31086 moveto +P$3b +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +4665 31086 moveto +P$3a +4584 31086 moveto +P$3c +fill +0 setlinewidth +[] 0 setdash +1 0 0 setrgbcolor +newpath +11650 31383 moveto +P$3d +fill +0 setlinewidth +[] 0 setdash +1 0 0 setrgbcolor +newpath +12165 31086 moveto +P$3a +12084 31086 moveto +P$3b +fill +0 setlinewidth +[] 0 setdash +1 0 0 setrgbcolor +newpath +12665 31086 moveto +P$3a +12584 31086 moveto +P$3c +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +19525 31347 moveto +P$39 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +20165 31086 moveto +P$3a +20084 31086 moveto +P$3b +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +20665 31086 moveto +P$3a +20584 31086 moveto +P$3c +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +27525 31347 moveto +P$39 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +28165 31086 moveto +P$3a +28084 31086 moveto +P$3b +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +28665 31086 moveto +P$3a +28584 31086 moveto +P$3c +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +35525 31347 moveto +P$39 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +36165 31086 moveto +P$3a +36084 31086 moveto +P$3b +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +36665 31086 moveto +P$3a +36584 31086 moveto +P$3c +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +43525 31347 moveto +P$39 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +44165 31086 moveto +P$3a +44084 31086 moveto +P$3b +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +44665 31086 moveto +P$3a +44584 31086 moveto +P$3c +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +51525 31347 moveto +P$39 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +52165 31086 moveto +P$3a +52084 31086 moveto +P$3b +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +52665 31086 moveto +P$3a +52584 31086 moveto +P$3c +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +59525 31347 moveto +P$39 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +60165 31086 moveto +P$3a +60084 31086 moveto +P$3b +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +60665 31086 moveto +P$3a +60584 31086 moveto +P$3c +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +67525 31347 moveto +P$39 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +68165 31086 moveto +P$3a +68084 31086 moveto +P$3b +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +68665 31086 moveto +P$3a +68584 31086 moveto +P$3c +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +75525 31347 moveto +P$39 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +76165 31086 moveto +P$3a +76084 31086 moveto +P$3b +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +76665 31086 moveto +P$3a +76584 31086 moveto +P$3c +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +83525 31347 moveto +P$39 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +84165 31086 moveto +P$3a +84084 31086 moveto +P$3b +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +84665 31086 moveto +P$3a +84584 31086 moveto +P$3c +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +91525 31347 moveto +P$39 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +92165 31086 moveto +P$3a +92084 31086 moveto +P$3b +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +92665 31086 moveto +P$3a +92584 31086 moveto +P$3c +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +99525 31347 moveto +P$39 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +100165 31086 moveto +P$3a +100084 31086 moveto +P$3b +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +100665 31086 moveto +P$3a +100584 31086 moveto +P$3c +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +754 32926 moveto +P$3e +423 32505 moveto +P$3f +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +1195 32747 moveto +P$32 +1279 33052 moveto +P$33 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +1706 32808 moveto +P$34 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +2440 32565 moveto +P$3g +2434 33313 moveto +P$3h +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +2765 33057 moveto +P$36 +2903 32794 moveto +P$37 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +3432 33313 moveto +P$38 +fill +100 setlinewidth +0 0 0 setrgbcolor +newpath +0 33638 moveto +104000 0 rlineto +stroke +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +3525 34547 moveto +P$39 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +4165 34286 moveto +P$3a +4084 34286 moveto +P$3b +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +4665 34286 moveto +P$3a +4584 34286 moveto +P$3c +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +19525 34547 moveto +P$39 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +20165 34286 moveto +P$3a +20084 34286 moveto +P$3b +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +20665 34286 moveto +P$3a +20584 34286 moveto +P$3c +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +27525 34547 moveto +P$39 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +28165 34286 moveto +P$3a +28084 34286 moveto +P$3b +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +28665 34286 moveto +P$3a +28584 34286 moveto +P$3c +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +43525 34547 moveto +P$39 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +44165 34286 moveto +P$3a +44084 34286 moveto +P$3b +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +44665 34286 moveto +P$3a +44584 34286 moveto +P$3c +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +51525 34547 moveto +P$39 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +52165 34286 moveto +P$3a +52084 34286 moveto +P$3b +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +52665 34286 moveto +P$3a +52584 34286 moveto +P$3c +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +59525 34547 moveto +P$39 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +60165 34286 moveto +P$3a +60084 34286 moveto +P$3b +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +60665 34286 moveto +P$3a +60584 34286 moveto +P$3c +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +75525 34547 moveto +P$39 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +76165 34286 moveto +P$3a +76084 34286 moveto +P$3b +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +76665 34286 moveto +P$3a +76584 34286 moveto +P$3c +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +83525 34547 moveto +P$39 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +84165 34286 moveto +P$3a +84084 34286 moveto +P$3b +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +84665 34286 moveto +P$3a +84584 34286 moveto +P$3c +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +91525 34547 moveto +P$39 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +92165 34286 moveto +P$3a +92084 34286 moveto +P$3b +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +92665 34286 moveto +P$3a +92584 34286 moveto +P$3c +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +99525 34547 moveto +P$39 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +100165 34286 moveto +P$3a +100084 34286 moveto +P$3b +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +100665 34286 moveto +P$3a +100584 34286 moveto +P$3c +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +815 36513 moveto +P$3i +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +1065 36257 moveto +P$36 +1203 35994 moveto +P$37 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +1921 35944 moveto +P$3j +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +2500 36255 moveto +P$3k +2385 36255 moveto +P$3l +fill +100 setlinewidth +0 0 0 setrgbcolor +newpath +0 36838 moveto +104000 0 rlineto +stroke +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +3525 37747 moveto +P$39 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +4165 37486 moveto +P$3a +4084 37486 moveto +P$3b +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +4665 37486 moveto +P$3a +4584 37486 moveto +P$3c +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +11525 37747 moveto +P$39 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +12165 37486 moveto +P$3a +12084 37486 moveto +P$3b +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +12665 37486 moveto +P$3a +12584 37486 moveto +P$3c +fill +0 setlinewidth +[] 0 setdash +1 0 0 setrgbcolor +newpath +19650 37783 moveto +P$3d +fill +0 setlinewidth +[] 0 setdash +1 0 0 setrgbcolor +newpath +20165 37486 moveto +P$3a +20084 37486 moveto +P$3b +fill +0 setlinewidth +[] 0 setdash +1 0 0 setrgbcolor +newpath +20665 37486 moveto +P$3a +20584 37486 moveto +P$3c +fill +0 setlinewidth +[] 0 setdash +1 0 0 setrgbcolor +newpath +27650 37783 moveto +P$3d +fill +0 setlinewidth +[] 0 setdash +1 0 0 setrgbcolor +newpath +28165 37486 moveto +P$3a +28084 37486 moveto +P$3b +fill +0 setlinewidth +[] 0 setdash +1 0 0 setrgbcolor +newpath +28665 37486 moveto +P$3a +28584 37486 moveto +P$3c +fill +0 setlinewidth +[] 0 setdash +1 0 0 setrgbcolor +newpath +35650 37783 moveto +P$3d +fill +0 setlinewidth +[] 0 setdash +1 0 0 setrgbcolor +newpath +36165 37486 moveto +P$3a +36084 37486 moveto +P$3b +fill +0 setlinewidth +[] 0 setdash +1 0 0 setrgbcolor +newpath +36665 37486 moveto +P$3a +36584 37486 moveto +P$3c +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +43525 37747 moveto +P$39 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +44165 37486 moveto +P$3a +44084 37486 moveto +P$3b +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +44665 37486 moveto +P$3a +44584 37486 moveto +P$3c +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +75525 37747 moveto +P$39 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +76165 37486 moveto +P$3a +76084 37486 moveto +P$3b +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +76665 37486 moveto +P$3a +76584 37486 moveto +P$3c +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +83525 37747 moveto +P$39 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +84165 37486 moveto +P$3a +84084 37486 moveto +P$3b +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +84665 37486 moveto +P$3a +84584 37486 moveto +P$3c +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +400 38940 moveto +P$3m +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +998 39586 moveto +P$35 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +1718 39469 moveto +P$3n +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +2495 39147 moveto +P$32 +2579 39452 moveto +P$33 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +3259 39590 moveto +P$3o +fill +100 setlinewidth +0 0 0 setrgbcolor +newpath +0 40038 moveto +104000 0 rlineto +stroke +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +3525 40947 moveto +P$39 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +4165 40686 moveto +P$3a +4084 40686 moveto +P$3b +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +4665 40686 moveto +P$3a +4584 40686 moveto +P$3c +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +11525 40947 moveto +P$39 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +12165 40686 moveto +P$3a +12084 40686 moveto +P$3b +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +12665 40686 moveto +P$3a +12584 40686 moveto +P$3c +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +19525 40947 moveto +P$39 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +20165 40686 moveto +P$3a +20084 40686 moveto +P$3b +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +20665 40686 moveto +P$3a +20584 40686 moveto +P$3c +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +27525 40947 moveto +P$39 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +28165 40686 moveto +P$3a +28084 40686 moveto +P$3b +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +28665 40686 moveto +P$3a +28584 40686 moveto +P$3c +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +35525 40947 moveto +P$39 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +36165 40686 moveto +P$3a +36084 40686 moveto +P$3b +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +36665 40686 moveto +P$3a +36584 40686 moveto +P$3c +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +43525 40947 moveto +P$39 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +44165 40686 moveto +P$3a +44084 40686 moveto +P$3b +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +44665 40686 moveto +P$3a +44584 40686 moveto +P$3c +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +51525 40947 moveto +P$39 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +52165 40686 moveto +P$3a +52084 40686 moveto +P$3b +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +52665 40686 moveto +P$3a +52584 40686 moveto +P$3c +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +59525 40947 moveto +P$39 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +60165 40686 moveto +P$3a +60084 40686 moveto +P$3b +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +60665 40686 moveto +P$3a +60584 40686 moveto +P$3c +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +75525 40947 moveto +P$39 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +76165 40686 moveto +P$3a +76084 40686 moveto +P$3b +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +76665 40686 moveto +P$3a +76584 40686 moveto +P$3c +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +83525 40947 moveto +P$39 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +84165 40686 moveto +P$3a +84084 40686 moveto +P$3b +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +84665 40686 moveto +P$3a +84584 40686 moveto +P$3c +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +260 42155 moveto +P$3p +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +698 42786 moveto +P$35 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +1432 42913 moveto +P$3q +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +1840 42165 moveto +P$3g +1834 42913 moveto +P$3h +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +2198 42786 moveto +P$35 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +3159 42790 moveto +P$3o +fill +100 setlinewidth +0 0 0 setrgbcolor +newpath +0 43238 moveto +104000 0 rlineto +stroke +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +3525 44147 moveto +P$39 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +4165 43886 moveto +P$3a +4084 43886 moveto +P$3b +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +4665 43886 moveto +P$3a +4584 43886 moveto +P$3c +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +11525 44147 moveto +P$39 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +12165 43886 moveto +P$3a +12084 43886 moveto +P$3b +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +12665 43886 moveto +P$3a +12584 43886 moveto +P$3c +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +19525 44147 moveto +P$39 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +20165 43886 moveto +P$3a +20084 43886 moveto +P$3b +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +20665 43886 moveto +P$3a +20584 43886 moveto +P$3c +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +27525 44147 moveto +P$39 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +28165 43886 moveto +P$3a +28084 43886 moveto +P$3b +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +28665 43886 moveto +P$3a +28584 43886 moveto +P$3c +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +35525 44147 moveto +P$39 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +36165 43886 moveto +P$3a +36084 43886 moveto +P$3b +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +36665 43886 moveto +P$3a +36584 43886 moveto +P$3c +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +43525 44147 moveto +P$39 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +44165 43886 moveto +P$3a +44084 43886 moveto +P$3b +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +44665 43886 moveto +P$3a +44584 43886 moveto +P$3c +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +51525 44147 moveto +P$39 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +52165 43886 moveto +P$3a +52084 43886 moveto +P$3b +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +52665 43886 moveto +P$3a +52584 43886 moveto +P$3c +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +59525 44147 moveto +P$39 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +60165 43886 moveto +P$3a +60084 43886 moveto +P$3b +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +60665 43886 moveto +P$3a +60584 43886 moveto +P$3c +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +67525 44147 moveto +P$39 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +68165 43886 moveto +P$3a +68084 43886 moveto +P$3b +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +68665 43886 moveto +P$3a +68584 43886 moveto +P$3c +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +75525 44147 moveto +P$39 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +76165 43886 moveto +P$3a +76084 43886 moveto +P$3b +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +76665 43886 moveto +P$3a +76584 43886 moveto +P$3c +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +83525 44147 moveto +P$39 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +84165 43886 moveto +P$3a +84084 43886 moveto +P$3b +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +84665 43886 moveto +P$3a +84584 43886 moveto +P$3c +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +3960 46899 moveto +P$e +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +4462 46747 moveto +P$f +4371 46747 moveto +P$g +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +11960 46899 moveto +P$e +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +12306 47038 moveto +P$h +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +20056 47038 moveto +P$h +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +28195 47077 moveto +P$i +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +36210 46899 moveto +P$j +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +44145 46933 moveto +P$k +44060 46533 moveto +P$l +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +51987 46694 moveto +P$m +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +60220 46874 moveto +P$n +60018 47049 moveto +P$o +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +67848 46577 moveto +P$p +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +76192 46581 moveto +P$q +76123 46900 moveto +P$r +76104 46581 moveto +P$s +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +83782 46622 moveto +P$t +83993 46453 moveto +P$u +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +91806 47038 moveto +P$v +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +92462 46747 moveto +P$f +92371 46747 moveto +P$g +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +99806 47038 moveto +P$v +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +100306 47038 moveto +P$h +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +3720 45877 moveto +P$0 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +4662 45645 moveto +P$1 +4573 45645 moveto +P$2 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +11603 45877 moveto +P$3 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +12203 45745 moveto +P$4 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +20020 45892 moveto +P$5 +20376 45645 moveto +P$6 +20482 45444 moveto +P$7 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +27603 45877 moveto +P$3 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +28209 45381 moveto +P$8 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +35757 45583 moveto +P$9 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +36475 45405 moveto +P$a +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +43567 45700 moveto +P$b +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +44326 45408 moveto +P$c +44392 45642 moveto +P$d +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +51517 45700 moveto +P$b +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +52203 45745 moveto +P$4 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +59720 45877 moveto +P$0 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +60662 45645 moveto +P$1 +60573 45645 moveto +P$2 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +67603 45877 moveto +P$3 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +68203 45745 moveto +P$4 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +76020 45892 moveto +P$5 +76376 45645 moveto +P$6 +76482 45444 moveto +P$7 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +83603 45877 moveto +P$3 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +84209 45381 moveto +P$8 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +91757 45583 moveto +P$9 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +92475 45405 moveto +P$a +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +99567 45700 moveto +P$b +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +100326 45408 moveto +P$c +100392 45642 moveto +P$d +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +4793 47735 moveto +P$w +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +5420 47901 moveto +P$x +5457 48187 moveto +P$y +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +5954 47960 moveto +P$z +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +6812 48415 moveto +P$10 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +7420 47901 moveto +P$x +7457 48187 moveto +P$y +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +7970 48027 moveto +P$11 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +8470 48481 moveto +P$12 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +9648 48463 moveto +P$13 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +10254 48067 moveto +P$14 +10082 48067 moveto +P$15 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +10848 48463 moveto +P$13 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +11459 48249 moveto +P$16 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +56556 48127 moveto +P$17 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +57239 47899 moveto +P$18 +57232 47959 moveto +P$19 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +57896 48173 moveto +P$1a +57521 47682 moveto +P$1b +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +58420 48027 moveto +P$11 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +59062 48415 moveto +P$10 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +59670 47901 moveto +P$x +59707 48187 moveto +P$y +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +60220 48027 moveto +P$11 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +60720 48481 moveto +P$12 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +61898 48463 moveto +P$13 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +62504 48067 moveto +P$14 +62332 48067 moveto +P$15 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +63098 48463 moveto +P$13 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +63709 48249 moveto +P$16 +fill +grestore +showpage +%plantuml done +%%EOF diff --git a/20-implementierungsheft/assets/gantt-plan.pdf b/20-implementierungsheft/assets/gantt-plan.pdf new file mode 100644 index 0000000..e69de29 diff --git a/20-implementierungsheft/assets/gantt-reality-eps-converted-to.pdf b/20-implementierungsheft/assets/gantt-reality-eps-converted-to.pdf new file mode 100644 index 0000000..5379947 Binary files /dev/null and b/20-implementierungsheft/assets/gantt-reality-eps-converted-to.pdf differ diff --git a/20-implementierungsheft/assets/gantt-reality.eps b/20-implementierungsheft/assets/gantt-reality.eps new file mode 100644 index 0000000..435dbb1 --- /dev/null +++ b/20-implementierungsheft/assets/gantt-reality.eps @@ -0,0 +1,11142 @@ +%!PS-Adobe-3.0 EPSF-3.0 +%%Creator: PlantUML v1.2023.0 +%%Title: noTitle +%%BoundingBox: 0 0 2081 554 +%%ColorUsage: Color +%%Origin: 0 0 +%%EndComments + +gsave +0 554 translate +.01 -.01 scale +/simplerect { +newpath moveto 1 index 0 rlineto +0 exch rlineto +neg 0 rlineto +} def +/rquadto { +3 index 3 index 4 2 roll rcurveto +} def +/P$h { +134 12 rlineto +0 26 rlineto +-353 0 rlineto +0 -26 rlineto +134 -12 rlineto +0 -534 rlineto +-131 46 rlineto +0 -25 rlineto +190 -109 rlineto +25 0 rlineto +0 621 rlineto +closepath +} def +/P$1e { +0 -60 -42 -87 rquadto +-40 -26 -120 -26 rquadto +-173 0 rlineto +0 240 rlineto +173 0 rlineto +81 0 121 -31 rquadto +40 -31 40 -95 rquadto +closepath +} def +/P$32 { +109 17 rlineto +0 34 rlineto +-340 0 rlineto +0 -34 rlineto +109 -17 rlineto +0 -750 rlineto +-109 -15 rlineto +0 -34 rlineto +340 0 rlineto +0 34 rlineto +-109 15 rlineto +0 750 rlineto +} def +/P$k { +0 143 rlineto +-84 0 rlineto +0 -143 rlineto +-290 0 rlineto +0 -65 rlineto +318 -448 rlineto +56 0 rlineto +0 443 rlineto +89 0 rlineto +0 70 rlineto +-89 0 rlineto +closepath +} def +/P$a { +0 125 rlineto +-21 0 rlineto +-28 -54 rlineto +-25 0 -57 7 rquadto +-32 6 -57 17 rquadto +0 342 rlineto +78 12 rlineto +0 21 rlineto +-217 0 rlineto +0 -21 rlineto +57 -12 rlineto +0 -390 rlineto +-57 -12 rlineto +0 -21 rlineto +132 0 rlineto +4 57 rlineto +29 -25 79 -46 rquadto +50 -23 79 -23 rquadto +7 0 rlineto +closepath +} def +/P$1v { +0 -85 rlineto +268 0 rlineto +0 85 rlineto +-268 0 rlineto +} def +/P$2j { +0 -65 -45 -100 rquadto +-45 -35 -129 -35 rquadto +-243 0 rlineto +0 279 rlineto +248 0 rlineto +81 0 125 -37 rquadto +45 -37 45 -106 rquadto +} def +/P$1j { +-196 -265 rlineto +-71 59 rlineto +0 206 rlineto +-96 0 rlineto +0 -796 rlineto +96 0 rlineto +0 498 rlineto +256 -282 rlineto +112 0 rlineto +-235 250 rlineto +248 331 rlineto +-112 0 rlineto +} def +/P$8 { +0 51 -3 73 rquadto +34 -20 78 -34 rquadto +45 -15 76 -15 rquadto +59 0 89 35 rquadto +31 34 31 100 rquadto +0 301 rlineto +56 12 rlineto +0 21 rlineto +-198 0 rlineto +0 -21 rlineto +60 -12 rlineto +0 -295 rlineto +0 -84 -81 -84 rquadto +-45 0 -109 14 rquadto +0 365 rlineto +62 12 rlineto +0 21 rlineto +-201 0 rlineto +0 -21 rlineto +57 -12 rlineto +0 -626 rlineto +-68 -10 rlineto +0 -21 rlineto +150 0 rlineto +0 198 rlineto +closepath +} def +/P$28 { +0 -146 -175 -146 rquadto +-204 0 rlineto +0 298 rlineto +207 0 rlineto +171 0 171 -151 rquadto +} def +/P$y { +206 0 206 151 rquadto +0 357 rlineto +54 14 rlineto +0 39 rlineto +-201 0 rlineto +-14 -42 rlineto +-45 31 -82 42 rquadto +-35 12 -73 12 rquadto +-170 0 -170 -165 rquadto +0 -60 25 -98 rquadto +25 -37 71 -54 rquadto +48 -18 150 -20 rquadto +71 -1 rlineto +0 -81 rlineto +0 -100 -81 -100 rquadto +-50 0 -110 31 rquadto +-21 68 rlineto +-39 0 rlineto +0 -132 rlineto +89 -14 129 -17 rquadto +42 -3 85 -3 rquadto +closepath +} def +/P$3g { +235 0 350 106 rquadto +114 104 114 315 rquadto +0 212 -110 323 rquadto +-109 109 -326 109 rquadto +-303 -3 rlineto +-109 0 rlineto +0 -34 rlineto +109 -17 rlineto +0 -750 rlineto +-109 -15 rlineto +0 -34 rlineto +385 0 rlineto +} def +/P$1 { +0 240 -215 240 rquadto +-103 0 -156 -60 rquadto +-53 -62 -53 -179 rquadto +0 -117 53 -178 rquadto +53 -62 160 -62 rquadto +104 0 157 60 rquadto +53 60 53 179 rquadto +closepath +} def +/P$2x { +0 281 rlineto +423 0 rlineto +0 85 rlineto +-423 0 rlineto +0 306 rlineto +-101 0 rlineto +0 -756 rlineto +537 0 rlineto +0 82 rlineto +-435 0 rlineto +} def +/P$35 { +-82 3 rlineto +-85 3 -115 32 rquadto +-29 29 -29 100 rquadto +0 110 90 110 rquadto +42 0 73 -9 rquadto +32 -9 64 -25 rquadto +0 -212 rlineto +} def +/P$2c { +0 -121 -32 -173 rquadto +-31 -53 -104 -53 rquadto +-81 0 -118 56 rquadto +-37 54 -37 176 rquadto +0 114 35 168 rquadto +37 54 120 54 rquadto +71 0 104 -53 rquadto +32 -54 32 -176 rquadto +} def +/P$1k { +0 100 40 154 rquadto +42 53 121 53 rquadto +62 0 100 -25 rquadto +37 -25 51 -64 rquadto +84 25 rlineto +-51 137 -235 137 rquadto +-129 0 -196 -76 rquadto +-67 -76 -67 -228 rquadto +0 -145 67 -221 rquadto +67 -76 192 -76 rquadto +256 0 256 309 rquadto +0 12 rlineto +-414 0 rlineto +closepath +} def +/P$1q { +0 151 -67 226 rquadto +-67 75 -195 75 rquadto +-126 0 -192 -76 rquadto +-64 -78 -64 -225 rquadto +0 -301 259 -301 rquadto +134 0 196 73 rquadto +62 73 62 228 rquadto +closepath +} def +/P$0 { +-17 0 rlineto +-239 -562 rlineto +0 523 rlineto +87 12 rlineto +0 26 rlineto +-223 0 rlineto +0 -26 rlineto +84 -12 rlineto +0 -576 rlineto +-84 -14 rlineto +0 -25 rlineto +198 0 rlineto +214 498 rlineto +231 -498 rlineto +187 0 rlineto +0 25 rlineto +-82 14 rlineto +0 576 rlineto +82 12 rlineto +0 26 rlineto +-265 0 rlineto +0 -26 rlineto +89 -12 rlineto +0 -523 rlineto +-262 562 rlineto +} def +/P$37 { +0 109 101 109 rquadto +79 0 146 -20 rquadto +0 -470 rlineto +-89 -17 rlineto +0 -28 rlineto +193 0 rlineto +0 553 rlineto +76 15 rlineto +0 28 rlineto +-175 0 rlineto +-4 -48 rlineto +-45 25 -104 43 rquadto +-57 17 -98 17 rquadto +-151 0 -151 -175 rquadto +0 -389 rlineto +-76 -17 rlineto +0 -28 rlineto +181 0 rlineto +0 426 rlineto +} def +/P$l { +-1 0 rlineto +-234 329 rlineto +235 0 rlineto +0 -329 rlineto +closepath +} def +/P$1b { +0 -109 -26 -159 rquadto +-26 -50 -87 -50 rquadto +-21 0 -48 4 rquadto +-26 4 -43 14 rquadto +0 421 rlineto +37 9 92 9 rquadto +57 0 85 -56 rquadto +28 -56 28 -184 rquadto +closepath +} def +/P$7 { +-50 0 -78 42 rquadto +-26 40 -26 120 rquadto +196 0 rlineto +0 -87 -23 -125 rquadto +-21 -37 -68 -37 rquadto +closepath +} def +/P$27 { +0 107 -70 171 rquadto +-68 62 -189 62 rquadto +-223 0 rlineto +0 295 rlineto +-101 0 rlineto +0 -756 rlineto +318 0 rlineto +126 0 195 59 rquadto +70 59 70 167 rquadto +closepath +} def +/P$m { +112 0 167 46 rquadto +56 45 56 140 rquadto +0 98 -60 151 rquadto +-59 53 -170 53 rquadto +-93 0 -165 -20 rquadto +-6 -137 rlineto +32 0 rlineto +21 90 rlineto +21 12 51 20 rquadto +29 6 57 6 rquadto +76 0 112 -35 rquadto +37 -35 37 -123 rquadto +0 -59 -15 -90 rquadto +-15 -31 -50 -45 rquadto +-34 -15 -92 -15 rquadto +-45 0 -87 12 rquadto +-46 0 rlineto +0 -325 rlineto +332 0 rlineto +0 75 rlineto +-289 0 rlineto +0 207 rlineto +53 -10 114 -10 rquadto +closepath +} def +/P$j { +0 87 -60 137 rquadto +-59 50 -170 50 rquadto +-93 0 -176 -20 rquadto +-4 -137 rlineto +31 0 rlineto +21 90 rlineto +20 10 54 18 rquadto +34 7 65 7 rquadto +76 0 112 -34 rquadto +37 -35 37 -117 rquadto +0 -64 -34 -96 rquadto +-34 -34 -104 -37 rquadto +-70 -4 rlineto +0 -40 rlineto +70 -3 rlineto +54 -3 81 -34 rquadto +26 -31 26 -95 rquadto +0 -65 -28 -95 rquadto +-28 -31 -90 -31 rquadto +-26 0 -54 7 rquadto +-28 7 -50 18 rquadto +-17 79 rlineto +-32 0 rlineto +0 -125 rlineto +48 -12 82 -17 rquadto +35 -4 71 -4 rquadto +209 0 209 160 rquadto +0 68 -37 109 rquadto +-37 39 -106 50 rquadto +89 9 131 50 rquadto +42 40 42 114 rquadto +closepath +} def +/P$2f { +0 -73 rlineto +325 -432 rlineto +-307 0 rlineto +0 -75 rlineto +421 0 rlineto +0 73 rlineto +-326 432 rlineto +337 0 rlineto +0 75 rlineto +-450 0 rlineto +} def +/P$2k { +0 156 -48 279 rquadto +-48 123 -150 232 rquadto +-93 0 rlineto +101 -112 148 -237 rquadto +46 -125 46 -275 rquadto +0 -150 -48 -275 rquadto +-46 -125 -146 -237 rquadto +93 0 rlineto +101 109 150 232 rquadto +48 123 48 278 rquadto +0 1 rlineto +} def +/P$s { +0 -67 -26 -98 rquadto +-26 -32 -79 -32 rquadto +-51 0 -78 31 rquadto +-25 31 -25 100 rquadto +0 68 25 98 rquadto +25 28 78 28 rquadto +54 0 79 -29 rquadto +26 -29 26 -96 rquadto +closepath +} def +/P$1o { +0 121 31 175 rquadto +32 51 104 51 rquadto +82 0 118 -56 rquadto +37 -57 37 -176 rquadto +0 -115 -37 -168 rquadto +-35 -54 -117 -54 rquadto +-73 0 -106 54 rquadto +-31 53 -31 175 rquadto +closepath +} def +/P$1t { +0 -445 rlineto +0 -62 -3 -135 rquadto +90 0 rlineto +4 98 4 118 rquadto +1 0 rlineto +23 -75 53 -101 rquadto +31 -28 85 -28 rquadto +18 0 39 6 rquadto +0 87 rlineto +-20 -4 -51 -4 rquadto +-60 0 -92 51 rquadto +-31 51 -31 148 rquadto +0 303 rlineto +-96 0 rlineto +} def +/P$2n { +-85 -221 rlineto +-345 0 rlineto +-87 221 rlineto +-106 0 rlineto +309 -756 rlineto +117 0 rlineto +303 756 rlineto +-104 0 rlineto +closepath +} def +/P$3n { +-109 -15 rlineto +0 -34 rlineto +326 0 rlineto +0 34 rlineto +-95 15 rlineto +0 526 rlineto +0 85 -26 150 rquadto +-26 64 -79 101 rquadto +-53 35 -117 35 rquadto +-81 0 -132 -18 rquadto +0 -154 rlineto +42 0 rlineto +18 87 rlineto +12 15 34 23 rquadto +23 7 50 7 rquadto +89 0 89 -120 rquadto +0 -639 rlineto +} def +/P$21 { +0 -756 rlineto +103 0 rlineto +0 756 rlineto +-103 0 rlineto +} def +/P$1z { +0 -581 rlineto +96 0 rlineto +0 581 rlineto +-96 0 rlineto +} def +/P$3k { +-131 15 rlineto +0 746 rlineto +168 0 rlineto +134 0 198 -12 rquadto +39 -176 rlineto +40 0 rlineto +-10 243 rlineto +-667 0 rlineto +0 -34 rlineto +109 -17 rlineto +0 -750 rlineto +-109 -15 rlineto +0 -34 rlineto +362 0 rlineto +0 34 rlineto +} def +/P$n { +0 101 -51 157 rquadto +-51 54 -148 54 rquadto +-110 0 -168 -85 rquadto +-57 -85 -57 -246 rquadto +0 -104 29 -181 rquadto +31 -78 85 -117 rquadto +56 -40 128 -40 rquadto +71 0 142 17 rquadto +0 112 rlineto +-31 0 rlineto +-17 -65 rlineto +-17 -9 -45 -15 rquadto +-26 -7 -48 -7 rquadto +-70 0 -110 70 rquadto +-39 68 -43 201 rquadto +79 -42 160 -42 rquadto +85 0 131 48 rquadto +45 48 45 140 rquadto +closepath +} def +/P$11 { +-39 20 rlineto +-82 42 -148 42 rquadto +-151 0 -151 -162 rquadto +0 -350 rlineto +-54 -14 rlineto +0 -39 rlineto +223 0 rlineto +0 381 rlineto +0 48 20 76 rquadto +21 28 60 28 rquadto +43 0 89 -20 rquadto +0 -412 rlineto +-51 -14 rlineto +0 -39 rlineto +220 0 rlineto +0 498 rlineto +54 14 rlineto +0 39 rlineto +-214 0 rlineto +-9 -48 rlineto +} def +/P$3a { +103 15 rlineto +0 28 rlineto +-309 0 rlineto +0 -28 rlineto +101 -15 rlineto +0 -814 rlineto +-101 -15 rlineto +0 -28 rlineto +206 0 rlineto +0 857 rlineto +closepath +} def +/P$4 { +0 84 78 84 rquadto +60 0 112 -15 rquadto +0 -362 rlineto +-68 -12 rlineto +0 -21 rlineto +150 0 rlineto +0 425 rlineto +57 12 rlineto +0 21 rlineto +-134 0 rlineto +-3 -37 rlineto +-34 18 -79 32 rquadto +-45 14 -76 14 rquadto +-117 0 -117 -134 rquadto +0 -300 rlineto +-59 -12 rlineto +0 -21 rlineto +140 0 rlineto +0 328 rlineto +closepath +} def +/P$14 { +-498 0 rlineto +0 -110 rlineto +51 -53 93 -96 rquadto +93 -92 135 -145 rquadto +43 -53 64 -109 rquadto +20 -57 20 -129 rquadto +0 -64 -31 -103 rquadto +-29 -39 -81 -39 rquadto +-37 0 -59 7 rquadto +-20 6 -39 21 rquadto +-25 114 rlineto +-51 0 rlineto +0 -178 rlineto +46 -10 92 -18 rquadto +45 -7 96 -7 rquadto +129 0 198 54 rquadto +68 53 68 151 rquadto +0 60 -20 110 rquadto +-20 50 -65 98 rquadto +-43 46 -175 154 rquadto +-50 40 -109 92 rquadto +385 0 rlineto +0 132 rlineto +} def +/P$f { +0 339 -215 339 rquadto +-103 0 -156 -85 rquadto +-53 -87 -53 -253 rquadto +0 -162 53 -248 rquadto +53 -87 160 -87 rquadto +103 0 156 85 rquadto +54 84 54 250 rquadto +closepath +} def +/P$b { +32 0 rlineto +17 89 rlineto +17 21 60 40 rquadto +45 17 87 17 rquadto +68 0 106 -34 rquadto +39 -35 39 -96 rquadto +0 -35 -15 -57 rquadto +-14 -23 -39 -39 rquadto +-23 -15 -54 -26 rquadto +-29 -10 -62 -21 rquadto +-32 -12 -64 -25 rquadto +-29 -14 -54 -34 rquadto +-23 -21 -39 -53 rquadto +-14 -31 -14 -76 rquadto +0 -78 57 -121 rquadto +59 -45 162 -45 rquadto +79 0 171 21 rquadto +0 135 rlineto +-31 0 rlineto +-17 -79 rlineto +-50 -37 -123 -37 rquadto +-64 0 -101 28 rquadto +-37 26 -37 73 rquadto +0 31 14 53 rquadto +15 20 39 35 rquadto +25 14 56 25 rquadto +31 10 64 21 rquadto +32 10 62 26 rquadto +31 14 54 35 rquadto +25 21 39 54 rquadto +15 31 15 78 rquadto +0 95 -57 146 rquadto +-57 51 -167 51 rquadto +-53 0 -106 -9 rquadto +-53 -9 -95 -25 rquadto +0 -151 rlineto +} def +/P$2s { +-93 0 -151 -37 rquadto +-56 -37 -71 -106 rquadto +96 -14 rlineto +9 40 42 62 rquadto +34 21 87 21 rquadto +145 0 145 -168 rquadto +0 -93 rlineto +-1 0 rlineto +-28 56 -76 84 rquadto +-46 28 -110 28 rquadto +-106 0 -156 -70 rquadto +-50 -71 -50 -223 rquadto +0 -154 53 -228 rquadto +54 -73 164 -73 rquadto +62 0 107 28 rquadto +45 28 70 81 rquadto +0 0 rlineto +0 -17 1 -56 rquadto +3 -40 4 -43 rquadto +92 0 rlineto +-3 29 -3 120 rquadto +0 443 rlineto +0 245 -243 245 rquadto +closepath +} def +/P$1w { +0 104 -82 162 rquadto +-81 57 -229 57 rquadto +-276 0 -320 -192 rquadto +100 -20 rlineto +17 68 71 101 rquadto +56 31 153 31 rquadto +98 0 151 -34 rquadto +54 -34 54 -100 rquadto +0 -37 -17 -60 rquadto +-15 -23 -46 -37 rquadto +-31 -15 -73 -25 rquadto +-42 -10 -93 -21 rquadto +-89 -20 -135 -40 rquadto +-46 -20 -73 -43 rquadto +-26 -25 -40 -57 rquadto +-14 -32 -14 -75 rquadto +0 -96 73 -150 rquadto +75 -53 214 -53 rquadto +128 0 195 40 rquadto +68 39 96 134 rquadto +-101 17 rlineto +-15 -59 -62 -85 rquadto +-46 -28 -129 -28 rquadto +-90 0 -139 29 rquadto +-46 29 -46 90 rquadto +0 34 17 57 rquadto +18 21 53 37 rquadto +35 15 140 39 rquadto +34 7 68 17 rquadto +34 7 65 20 rquadto +32 10 59 26 rquadto +28 15 48 39 rquadto +20 21 31 53 rquadto +12 29 12 70 rquadto +} def +/P$1f { +0 -132 -187 -132 rquadto +-192 0 rlineto +0 271 rlineto +200 0 rlineto +93 0 135 -34 rquadto +43 -34 43 -104 rquadto +} def +/P$1d { +0 100 -73 156 rquadto +-73 56 -204 56 rquadto +-306 0 rlineto +0 -756 rlineto +275 0 rlineto +265 0 265 182 rquadto +0 67 -37 114 rquadto +-37 45 -106 60 rquadto +90 9 139 59 rquadto +48 50 48 126 rquadto +closepath +} def +/P$9 { +0 254 rlineto +107 12 rlineto +0 26 rlineto +-279 0 rlineto +0 -26 rlineto +76 -12 rlineto +0 -576 rlineto +-84 -14 rlineto +0 -25 rlineto +492 0 rlineto +0 156 rlineto +-32 0 rlineto +-15 -106 rlineto +-54 -6 -157 -6 rquadto +-106 0 rlineto +0 273 rlineto +192 0 rlineto +15 -78 rlineto +29 0 rlineto +0 200 rlineto +-29 0 rlineto +-15 -78 rlineto +-192 0 rlineto +} def +/P$1x { +31 -57 75 -84 rquadto +43 -26 110 -26 rquadto +93 0 139 46 rquadto +45 46 45 157 rquadto +0 387 rlineto +-96 0 rlineto +0 -368 rlineto +0 -60 -12 -90 rquadto +-10 -29 -37 -43 rquadto +-25 -14 -70 -14 rquadto +-68 0 -109 46 rquadto +-40 46 -40 128 rquadto +0 342 rlineto +-96 0 rlineto +0 -796 rlineto +96 0 rlineto +0 207 rlineto +0 32 -3 67 rquadto +-1 34 -1 40 rquadto +1 0 rlineto +} def +/P$1c { +-56 -14 rlineto +0 -37 rlineto +225 0 rlineto +0 201 rlineto +0 54 -6 110 rquadto +23 -18 67 -31 rquadto +45 -14 87 -14 rquadto +120 0 175 65 rquadto +54 65 54 209 rquadto +0 142 -70 223 rquadto +-70 79 -198 79 rquadto +-95 0 -278 -40 rquadto +0 -753 rlineto +} def +/P$36 { +48 -28 103 -45 rquadto +56 -18 92 -18 rquadto +78 0 117 45 rquadto +39 45 39 129 rquadto +0 393 rlineto +71 15 rlineto +0 28 rlineto +-256 0 rlineto +0 -28 rlineto +79 -15 rlineto +0 -381 rlineto +0 -53 -26 -82 rquadto +-25 -31 -79 -31 rquadto +-56 0 -140 18 rquadto +0 476 rlineto +81 15 rlineto +0 28 rlineto +-257 0 rlineto +0 -28 rlineto +71 -15 rlineto +0 -507 rlineto +-71 -17 rlineto +0 -28 rlineto +170 0 rlineto +6 48 rlineto +} def +/P$3s { +0 -137 -40 -198 rquadto +-39 -62 -123 -62 rquadto +-84 0 -121 59 rquadto +-35 59 -35 201 rquadto +0 143 37 204 rquadto +37 59 120 59 rquadto +82 0 123 -62 rquadto +40 -62 40 -201 rquadto +closepath +} def +/P$13 { +-220 -515 rlineto +-37 -14 rlineto +0 -39 rlineto +281 0 rlineto +0 39 rlineto +-65 15 rlineto +129 304 rlineto +115 -306 rlineto +-65 -14 rlineto +0 -39 rlineto +181 0 rlineto +0 39 rlineto +-40 12 rlineto +-217 534 rlineto +-37 95 -65 137 rquadto +-28 43 -62 65 rquadto +-34 21 -78 21 rquadto +-46 0 -96 -10 rquadto +0 -142 rlineto +35 0 rlineto +25 73 rlineto +17 14 42 14 rquadto +23 0 39 -10 rquadto +17 -10 32 -31 rquadto +15 -18 28 -48 rquadto +14 -28 39 -85 rquadto +} def +/P$2d { +0 510 rlineto +-96 0 rlineto +0 -510 rlineto +-81 0 rlineto +0 -70 rlineto +81 0 rlineto +0 -65 rlineto +0 -79 34 -114 rquadto +35 -34 107 -34 rquadto +40 0 68 6 rquadto +0 73 rlineto +-25 -4 -43 -4 rquadto +-37 0 -54 18 rquadto +-15 18 -15 68 rquadto +0 51 rlineto +114 0 rlineto +0 70 rlineto +-114 0 rlineto +} def +/P$o { +57 0 84 -39 rquadto +26 -39 26 -126 rquadto +0 -79 -25 -115 rquadto +-25 -35 -79 -35 rquadto +-67 0 -142 25 rquadto +0 148 32 220 rquadto +34 71 103 71 rquadto +closepath +} def +/P$19 { +112 0 162 59 rquadto +50 59 50 185 rquadto +0 48 rlineto +-289 0 rlineto +0 9 rlineto +0 87 14 125 rquadto +14 35 45 56 rquadto +32 18 87 18 rquadto +51 0 129 -17 rquadto +0 45 rlineto +-31 18 -82 31 rquadto +-51 12 -100 12 rquadto +-135 0 -201 -70 rquadto +-64 -70 -64 -218 rquadto +0 -143 60 -214 rquadto +62 -71 187 -71 rquadto +closepath +} def +/P$u { +-121 0 -121 170 rquadto +0 75 29 110 rquadto +29 34 90 34 rquadto +62 0 126 -25 rquadto +0 -150 -29 -220 rquadto +-29 -70 -95 -70 rquadto +closepath +} def +/P$2t { +0 -70 -20 -121 rquadto +-18 -51 -54 -78 rquadto +-34 -28 -79 -28 rquadto +-73 0 -107 54 rquadto +-32 53 -32 173 rquadto +0 118 31 171 rquadto +31 51 107 51 rquadto +45 0 81 -26 rquadto +35 -26 54 -76 rquadto +20 -51 20 -120 rquadto +} def +/P$3e { +0 -142 -26 -204 rquadto +-26 -62 -85 -62 rquadto +-56 0 -81 59 rquadto +-25 59 -25 207 rquadto +0 148 25 209 rquadto +25 60 81 60 rquadto +57 0 84 -62 rquadto +28 -64 28 -207 rquadto +closepath +} def +/P$g { +0 -157 -29 -226 rquadto +-29 -70 -95 -70 rquadto +-62 0 -90 65 rquadto +-28 65 -28 231 rquadto +0 165 28 234 rquadto +28 67 90 67 rquadto +64 0 93 -70 rquadto +31 -71 31 -231 rquadto +closepath +} def +/P$20 { +-67 0 -104 -42 rquadto +-35 -40 -35 -115 rquadto +0 -193 rlineto +0 -62 -28 -92 rquadto +-28 -31 -87 -34 rquadto +0 -68 rlineto +57 -1 85 -32 rquadto +29 -31 29 -93 rquadto +0 -193 rlineto +0 -76 34 -117 rquadto +35 -40 106 -40 rquadto +73 0 rlineto +0 68 rlineto +-34 0 rlineto +-48 0 -70 29 rquadto +-20 28 -20 85 rquadto +0 190 rlineto +0 51 -28 89 rquadto +-28 35 -75 46 rquadto +0 1 rlineto +46 10 75 48 rquadto +28 35 28 87 rquadto +0 192 rlineto +0 56 20 85 rquadto +21 29 70 29 rquadto +34 0 rlineto +0 68 rlineto +-73 0 rlineto +} def +/P$p { +-32 0 rlineto +0 -154 rlineto +406 0 rlineto +0 37 rlineto +-292 617 rlineto +-64 0 rlineto +287 -579 rlineto +-287 0 rlineto +-17 79 rlineto +closepath +} def +/P$3p { +-73 23 -153 40 rquadto +-79 15 -171 15 rquadto +-206 0 -321 -110 rquadto +-115 -112 -115 -317 rquadto +0 -223 112 -334 rquadto +112 -110 328 -110 rquadto +154 0 298 37 rquadto +0 182 rlineto +-42 0 rlineto +-17 -104 rlineto +-43 -31 -104 -46 rquadto +-60 -17 -129 -17 rquadto +-162 0 -237 96 rquadto +-75 96 -75 295 rquadto +0 187 76 284 rquadto +78 96 229 96 rquadto +53 0 110 -12 rquadto +59 -12 90 -31 rquadto +0 -242 rlineto +-109 -15 rlineto +0 -34 rlineto +314 0 rlineto +0 34 rlineto +-82 15 rlineto +0 278 rlineto +} def +/P$2l { +50 0 70 -29 rquadto +21 -29 21 -85 rquadto +0 -192 rlineto +0 -53 28 -89 rquadto +28 -35 75 -46 rquadto +0 -1 rlineto +-45 -10 -75 -46 rquadto +-28 -35 -28 -89 rquadto +0 -190 rlineto +0 -57 -21 -85 rquadto +-20 -29 -70 -29 rquadto +-31 0 rlineto +0 -68 rlineto +70 0 rlineto +70 0 106 40 rquadto +35 40 35 117 rquadto +0 193 rlineto +0 62 28 93 rquadto +28 31 87 32 rquadto +0 68 rlineto +-59 1 -87 32 rquadto +-28 31 -28 93 rquadto +0 193 rlineto +0 75 -37 115 rquadto +-35 42 -104 42 rquadto +-70 0 rlineto +0 -68 rlineto +31 0 rlineto +} def +/P$2w { +0 -184 98 -285 rquadto +100 -101 278 -101 rquadto +126 0 204 43 rquadto +78 42 120 135 rquadto +-96 28 rlineto +-32 -64 -89 -93 rquadto +-56 -29 -140 -29 rquadto +-131 0 -201 79 rquadto +-68 78 -68 223 rquadto +0 142 73 226 rquadto +73 82 203 82 rquadto +75 0 139 -21 rquadto +64 -23 104 -62 rquadto +0 -135 rlineto +-226 0 rlineto +0 -85 rlineto +320 0 rlineto +0 260 rlineto +-59 60 -146 95 rquadto +-87 32 -190 32 rquadto +-117 0 -203 -46 rquadto +-85 -48 -132 -135 rquadto +-45 -89 -45 -209 rquadto +} def +/P$2u { +-114 0 rlineto +-212 -581 rlineto +103 0 rlineto +128 378 rlineto +7 21 37 128 rquadto +18 -64 rlineto +20 -62 rlineto +132 -379 rlineto +103 0 rlineto +-217 581 rlineto +} def +/P$2z { +0 304 -214 304 rquadto +-134 0 -179 -101 rquadto +-3 0 rlineto +1 4 1 92 rquadto +0 226 rlineto +-96 0 rlineto +0 -690 rlineto +0 -89 -3 -118 rquadto +93 0 rlineto +1 1 1 15 rquadto +1 12 3 40 rquadto +1 26 1 37 rquadto +1 0 rlineto +26 -54 68 -78 rquadto +42 -25 110 -25 rquadto +107 0 160 71 rquadto +53 71 53 225 rquadto +closepath +} def +/P$39 { +-65 0 -101 54 rquadto +-35 53 -35 156 rquadto +256 0 rlineto +0 -112 -29 -160 rquadto +-29 -50 -89 -50 rquadto +} def +/P$12 { +62 -70 112 -101 rquadto +51 -31 92 -31 rquadto +31 0 rlineto +0 201 rlineto +-32 0 rlineto +-34 -73 rlineto +-35 0 -82 15 rquadto +-46 15 -82 37 rquadto +0 334 rlineto +87 14 rlineto +0 39 rlineto +-328 0 rlineto +0 -39 rlineto +70 -14 rlineto +0 -445 rlineto +-70 -14 rlineto +0 -39 rlineto +231 0 rlineto +6 115 rlineto +} def +/P$2v { +0 -756 rlineto +573 0 rlineto +0 82 rlineto +-471 0 rlineto +0 243 rlineto +440 0 rlineto +0 82 rlineto +-440 0 rlineto +0 262 rlineto +493 0 rlineto +0 84 rlineto +-595 0 rlineto +} def +/P$3f { +0 -178 -96 -270 rquadto +-95 -92 -275 -92 rquadto +-114 0 rlineto +0 734 rlineto +76 4 181 4 rquadto +156 0 229 -92 rquadto +75 -92 75 -284 rquadto +closepath +} def +/P$2g { +-92 0 -162 -32 rquadto +-68 -34 -107 -98 rquadto +-37 -65 -37 -154 rquadto +0 -481 rlineto +103 0 rlineto +0 471 rlineto +0 104 51 159 rquadto +53 53 153 53 rquadto +101 0 157 -56 rquadto +57 -56 57 -162 rquadto +0 -465 rlineto +101 0 rlineto +0 471 rlineto +0 92 -39 159 rquadto +-39 65 -110 101 rquadto +-70 34 -167 34 rquadto +} def +/P$i { +-401 0 rlineto +0 -71 rlineto +90 -82 rlineto +87 -76 128 -123 rquadto +42 -46 59 -96 rquadto +18 -51 18 -115 rquadto +0 -64 -29 -96 rquadto +-28 -34 -93 -34 rquadto +-26 0 -54 7 rquadto +-26 7 -46 18 rquadto +-17 79 rlineto +-32 0 rlineto +0 -125 rlineto +89 -21 151 -21 rquadto +107 0 160 45 rquadto +54 43 54 126 rquadto +0 54 -21 103 rquadto +-21 48 -65 96 rquadto +-43 46 -145 134 rquadto +-43 35 -92 81 rquadto +337 0 rlineto +0 75 rlineto +closepath +} def +/P$1i { +0 117 35 173 rquadto +35 54 109 54 rquadto +51 0 85 -28 rquadto +35 -28 43 -85 rquadto +96 6 rlineto +-10 84 -71 134 rquadto +-59 50 -151 50 rquadto +-121 0 -185 -76 rquadto +-64 -78 -64 -225 rquadto +0 -146 64 -223 rquadto +64 -78 184 -78 rquadto +89 0 148 46 rquadto +59 45 73 126 rquadto +-98 7 rlineto +-7 -48 -39 -76 rquadto +-29 -28 -85 -28 rquadto +-76 0 -110 51 rquadto +-34 50 -34 170 rquadto +} def +/P$2y { +-301 -365 rlineto +-100 75 rlineto +0 290 rlineto +-101 0 rlineto +0 -756 rlineto +101 0 rlineto +0 378 rlineto +365 -378 rlineto +120 0 rlineto +-321 328 rlineto +365 428 rlineto +-128 0 rlineto +} def +/P$25 { +0 -504 rlineto +0 -84 4 -160 rquadto +-26 95 -48 150 rquadto +-195 515 rlineto +-71 0 rlineto +-198 -515 rlineto +-29 -90 rlineto +-17 -59 rlineto +1 59 rlineto +1 101 rlineto +0 504 rlineto +-90 0 rlineto +0 -756 rlineto +134 0 rlineto +201 523 rlineto +10 32 20 68 rquadto +10 35 14 51 rquadto +3 -20 17 -64 rquadto +14 -43 18 -56 rquadto +196 -523 rlineto +132 0 rlineto +0 756 rlineto +-92 0 rlineto +} def +/P$3b { +120 12 rlineto +0 23 rlineto +-315 0 rlineto +0 -23 rlineto +120 -12 rlineto +0 -479 rlineto +-118 42 rlineto +0 -23 rlineto +171 -96 rlineto +21 0 rlineto +0 557 rlineto +} def +/P$3c { +0 306 -193 306 rquadto +-92 0 -140 -78 rquadto +-46 -79 -46 -228 rquadto +0 -146 46 -223 rquadto +48 -78 145 -78 rquadto +92 0 140 76 rquadto +48 76 48 225 rquadto +closepath +} def +/P$3j { +-360 0 rlineto +0 -64 rlineto +82 -75 rlineto +78 -68 114 -110 rquadto +37 -43 53 -89 rquadto +17 -45 17 -103 rquadto +0 -57 -26 -87 rquadto +-25 -29 -84 -29 rquadto +-23 0 -48 6 rquadto +-25 6 -43 17 rquadto +-14 71 rlineto +-29 0 rlineto +0 -112 rlineto +79 -18 135 -18 rquadto +96 0 145 40 rquadto +48 39 48 112 rquadto +0 50 -20 93 rquadto +-18 43 -57 87 rquadto +-39 42 -131 120 rquadto +-39 32 -82 73 rquadto +303 0 rlineto +0 67 rlineto +} def +/P$16 { +0 -182 -20 -262 rquadto +-20 -79 -64 -79 rquadto +-43 0 -62 78 rquadto +-18 76 -18 264 rquadto +0 192 18 271 rquadto +18 78 62 78 rquadto +42 0 62 -81 rquadto +21 -81 21 -268 rquadto +} def +/P$d { +-64 3 rlineto +-65 1 -89 25 rquadto +-23 21 -23 76 rquadto +0 85 70 85 rquadto +32 0 56 -7 rquadto +25 -7 50 -18 rquadto +0 -164 rlineto +closepath +} def +/P$c { +75 0 110 31 rquadto +35 29 35 93 rquadto +0 309 rlineto +56 12 rlineto +0 21 rlineto +-125 0 rlineto +-9 -45 rlineto +-56 54 -142 54 rquadto +-117 0 -117 -135 rquadto +0 -46 17 -76 rquadto +17 -29 56 -45 rquadto +39 -15 114 -17 rquadto +68 -3 rlineto +0 -71 rlineto +0 -46 -17 -68 rquadto +-17 -23 -53 -23 rquadto +-50 0 -90 23 rquadto +-15 57 rlineto +-28 0 rlineto +0 -100 rlineto +79 -17 139 -17 rquadto +closepath +} def +/P$w { +-401 0 rlineto +0 -71 rlineto +90 -82 rlineto +87 -76 128 -123 rquadto +42 -46 59 -96 rquadto +18 -51 18 -115 rquadto +0 -64 -29 -96 rquadto +-28 -34 -93 -34 rquadto +-26 0 -54 7 rquadto +-26 7 -46 18 rquadto +-17 79 rlineto +-32 0 rlineto +0 -125 rlineto +89 -21 151 -21 rquadto +107 0 160 45 rquadto +54 43 54 126 rquadto +0 54 -21 103 rquadto +-21 48 -65 96 rquadto +-43 46 -145 134 rquadto +-43 35 -92 81 rquadto +337 0 rlineto +0 75 rlineto +} def +/P$1l { +-7 -92 -46 -134 rquadto +-37 -42 -110 -42 rquadto +-70 0 -110 46 rquadto +-40 46 -43 129 rquadto +312 0 rlineto +} def +/P$17 { +0 107 -76 167 rquadto +-76 59 -214 59 rquadto +-109 0 -217 -25 rquadto +-6 -189 rlineto +53 0 rlineto +31 125 rlineto +51 29 106 29 rquadto +71 0 110 -45 rquadto +40 -46 40 -128 rquadto +0 -70 -32 -107 rquadto +-31 -37 -103 -42 rquadto +-68 -4 rlineto +0 -70 rlineto +65 -4 rlineto +53 -4 78 -39 rquadto +25 -34 25 -104 rquadto +0 -65 -29 -103 rquadto +-29 -37 -84 -37 rquadto +-31 0 -51 9 rquadto +-20 9 -39 20 rquadto +-25 114 rlineto +-51 0 rlineto +0 -178 rlineto +60 -15 103 -20 rquadto +43 -6 85 -6 rquadto +264 0 264 193 rquadto +0 79 -42 129 rquadto +-42 48 -120 60 rquadto +198 23 198 196 rquadto +closepath +} def +/P$3m { +0 89 -56 134 rquadto +-56 45 -165 45 rquadto +-45 0 -100 -9 rquadto +-53 -9 -82 -20 rquadto +0 -146 rlineto +28 0 rlineto +31 82 rlineto +48 43 123 43 rquadto +123 0 123 -104 rquadto +0 -78 -96 -110 rquadto +-56 -18 rlineto +-64 -20 -93 -42 rquadto +-29 -21 -45 -53 rquadto +-15 -31 -15 -76 rquadto +0 -78 53 -123 rquadto +54 -45 145 -45 rquadto +65 0 164 20 rquadto +0 129 rlineto +-29 0 rlineto +-26 -68 rlineto +-34 -31 -106 -31 rquadto +-51 0 -78 26 rquadto +-26 25 -26 68 rquadto +0 35 23 60 rquadto +25 23 75 40 rquadto +92 31 120 46 rquadto +29 14 50 35 rquadto +20 20 31 48 rquadto +10 26 10 67 rquadto +closepath +} def +/P$r { +0 -78 -31 -112 rquadto +-29 -35 -95 -35 rquadto +-64 0 -92 34 rquadto +-28 32 -28 114 rquadto +0 82 28 115 rquadto +29 32 92 32 rquadto +65 0 95 -34 rquadto +31 -34 31 -114 rquadto +closepath +} def +/P$2q { +0 -142 -71 -221 rquadto +-70 -81 -198 -81 rquadto +-129 0 -201 79 rquadto +-70 79 -70 223 rquadto +0 142 71 226 rquadto +71 82 198 82 rquadto +131 0 201 -81 rquadto +70 -81 70 -228 rquadto +} def +/P$2o { +-4 15 rlineto +-12 43 -39 114 rquadto +-96 248 rlineto +282 0 rlineto +-96 -250 rlineto +-15 -35 -29 -82 rquadto +-15 -45 rlineto +} def +/P$2m { +0 -756 rlineto +101 0 rlineto +0 671 rlineto +382 0 rlineto +0 84 rlineto +-484 0 rlineto +} def +/P$3h { +0 28 -20 48 rquadto +-20 20 -48 20 rquadto +-28 0 -48 -20 rquadto +-20 -20 -20 -48 rquadto +0 -28 20 -48 rquadto +20 -20 48 -20 rquadto +28 0 48 20 rquadto +20 20 20 48 rquadto +closepath +} def +/P$e { +0 87 -60 137 rquadto +-59 50 -170 50 rquadto +-93 0 -176 -20 rquadto +-4 -137 rlineto +31 0 rlineto +21 90 rlineto +20 10 54 18 rquadto +34 7 65 7 rquadto +76 0 112 -34 rquadto +37 -35 37 -117 rquadto +0 -64 -34 -96 rquadto +-34 -34 -104 -37 rquadto +-70 -4 rlineto +0 -40 rlineto +70 -3 rlineto +54 -3 81 -34 rquadto +26 -31 26 -95 rquadto +0 -65 -28 -95 rquadto +-28 -31 -90 -31 rquadto +-26 0 -54 7 rquadto +-28 7 -50 18 rquadto +-17 79 rlineto +-32 0 rlineto +0 -125 rlineto +48 -12 82 -17 rquadto +35 -4 71 -4 rquadto +209 0 209 160 rquadto +0 68 -37 109 rquadto +-37 39 -106 50 rquadto +89 9 131 50 rquadto +42 40 42 114 rquadto +} def +/P$23 { +0 368 rlineto +0 57 10 89 rquadto +10 31 35 45 rquadto +25 14 73 14 rquadto +68 0 109 -46 rquadto +40 -48 40 -132 rquadto +0 -337 rlineto +96 0 rlineto +0 457 rlineto +0 101 3 123 rquadto +-92 0 rlineto +0 -3 -1 -14 rquadto +0 -12 -1 -26 rquadto +0 -15 0 -59 rquadto +-1 0 rlineto +-34 60 -78 85 rquadto +-43 25 -107 25 rquadto +-96 0 -140 -46 rquadto +-43 -48 -43 -157 rquadto +0 -387 rlineto +96 0 rlineto +} def +/P$5 { +-25 0 rlineto +-170 -451 rlineto +-173 451 rlineto +-26 0 rlineto +-217 -631 rlineto +-56 -14 rlineto +0 -25 rlineto +250 0 rlineto +0 25 rlineto +-96 14 rlineto +156 460 rlineto +176 -454 rlineto +21 0 rlineto +168 454 rlineto +148 -460 rlineto +-101 -14 rlineto +0 -25 rlineto +217 0 rlineto +0 25 rlineto +-57 14 rlineto +-214 631 rlineto +closepath +} def +/P$1n { +-26 56 -71 81 rquadto +-43 23 -109 23 rquadto +-109 0 -160 -73 rquadto +-51 -75 -51 -225 rquadto +0 -304 212 -304 rquadto +67 0 110 25 rquadto +43 23 70 76 rquadto +1 0 rlineto +-1 -65 rlineto +0 -240 rlineto +96 0 rlineto +0 676 rlineto +0 90 3 120 rquadto +-92 0 rlineto +-1 -9 -3 -39 rquadto +-1 -31 -1 -54 rquadto +-3 0 rlineto +closepath +} def +/P$3q { +0 160 rlineto +-28 0 rlineto +-35 -70 rlineto +-32 0 -76 9 rquadto +-42 7 -75 21 rquadto +0 446 rlineto +103 15 rlineto +0 28 rlineto +-282 0 rlineto +0 -28 rlineto +75 -15 rlineto +0 -507 rlineto +-75 -17 rlineto +0 -28 rlineto +173 0 rlineto +6 75 rlineto +37 -32 101 -60 rquadto +65 -29 104 -29 rquadto +9 0 rlineto +} def +/P$q { +0 53 -26 90 rquadto +-26 37 -70 57 rquadto +56 20 85 64 rquadto +31 43 31 106 rquadto +0 92 -53 139 rquadto +-51 46 -162 46 rquadto +-209 0 -209 -185 rquadto +0 -65 31 -107 rquadto +31 -42 84 -62 rquadto +-42 -20 -68 -56 rquadto +-26 -37 -26 -92 rquadto +0 -81 50 -125 rquadto +50 -45 143 -45 rquadto +90 0 140 45 rquadto +50 43 50 125 rquadto +closepath +} def +/P$1m { +0 -368 rlineto +0 -57 -12 -89 rquadto +-10 -31 -35 -45 rquadto +-23 -14 -71 -14 rquadto +-70 0 -110 48 rquadto +-39 46 -39 131 rquadto +0 337 rlineto +-96 0 rlineto +0 -457 rlineto +0 -101 -3 -123 rquadto +90 0 rlineto +1 3 1 15 rquadto +0 10 0 26 rquadto +1 14 3 57 rquadto +1 0 rlineto +32 -60 76 -85 rquadto +43 -25 109 -25 rquadto +95 0 139 48 rquadto +45 46 45 156 rquadto +0 387 rlineto +-96 0 rlineto +} def +/P$26 { +0 121 31 175 rquadto +32 51 104 51 rquadto +82 0 118 -56 rquadto +37 -57 37 -176 rquadto +0 -115 -37 -168 rquadto +-35 -54 -117 -54 rquadto +-73 0 -106 54 rquadto +-31 53 -31 175 rquadto +} def +/P$2r { +-192 0 -228 -198 rquadto +100 -17 rlineto +9 62 43 98 rquadto +34 34 84 34 rquadto +56 0 87 -39 rquadto +32 -39 32 -112 rquadto +0 -450 rlineto +-145 0 rlineto +0 -82 rlineto +248 0 rlineto +0 531 rlineto +0 109 -60 173 rquadto +-59 62 -162 62 rquadto +} def +/P$33 { +48 -26 101 -45 rquadto +53 -18 93 -18 rquadto +43 0 81 17 rquadto +37 15 56 51 rquadto +48 -26 114 -46 rquadto +65 -21 109 -21 rquadto +151 0 151 175 rquadto +0 393 rlineto +76 15 rlineto +0 28 rlineto +-270 0 rlineto +0 -28 rlineto +89 -15 rlineto +0 -381 rlineto +0 -109 -101 -109 rquadto +-17 0 -39 3 rquadto +-21 1 -43 4 rquadto +-21 3 -42 7 rquadto +-20 4 -32 6 rquadto +10 34 10 75 rquadto +0 393 rlineto +89 15 rlineto +0 28 rlineto +-282 0 rlineto +0 -28 rlineto +87 -15 rlineto +0 -381 rlineto +0 -53 -26 -81 rquadto +-26 -28 -81 -28 rquadto +-56 0 -139 18 rquadto +0 471 rlineto +90 15 rlineto +0 28 rlineto +-271 0 rlineto +0 -28 rlineto +76 -15 rlineto +0 -507 rlineto +-76 -17 rlineto +0 -28 rlineto +175 0 rlineto +4 48 rlineto +} def +/P$2 { +0 -106 -31 -153 rquadto +-29 -48 -95 -48 rquadto +-64 0 -92 46 rquadto +-28 45 -28 154 rquadto +0 109 28 156 rquadto +29 46 92 46 rquadto +64 0 95 -48 rquadto +31 -48 31 -154 rquadto +closepath +} def +/P$v { +134 12 rlineto +0 26 rlineto +-353 0 rlineto +0 -26 rlineto +134 -12 rlineto +0 -534 rlineto +-131 46 rlineto +0 -25 rlineto +190 -109 rlineto +25 0 rlineto +0 621 rlineto +} def +/P$x { +-101 -15 rlineto +0 -42 rlineto +375 0 rlineto +0 42 rlineto +-89 15 rlineto +0 475 rlineto +0 126 -70 196 rquadto +-70 68 -200 68 rquadto +-43 0 -87 -6 rquadto +-42 -4 -67 -12 rquadto +0 -178 rlineto +53 0 rlineto +17 104 rlineto +10 14 31 21 rquadto +20 7 45 7 rquadto +40 0 67 -28 rquadto +26 -28 26 -84 rquadto +0 -565 rlineto +} def +/P$29 { +0 117 -46 204 rquadto +-45 87 -129 134 rquadto +-82 46 -192 46 rquadto +-282 0 rlineto +0 -756 rlineto +250 0 rlineto +192 0 296 96 rquadto +104 95 104 273 rquadto +closepath +} def +/P$3l { +243 -264 rlineto +-62 -17 rlineto +0 -28 rlineto +210 0 rlineto +0 28 rlineto +-73 15 rlineto +-170 173 rlineto +218 335 rlineto +64 15 rlineto +0 28 rlineto +-243 0 rlineto +0 -28 rlineto +54 -15 rlineto +-164 -257 rlineto +-78 85 rlineto +0 171 rlineto +62 15 rlineto +0 28 rlineto +-243 0 rlineto +0 -28 rlineto +75 -15 rlineto +0 -814 rlineto +-87 -15 rlineto +0 -28 rlineto +193 0 rlineto +0 614 rlineto +} def +/P$18 { +0 276 rlineto +129 17 rlineto +0 42 rlineto +-407 0 rlineto +0 -42 rlineto +93 -17 rlineto +0 -668 rlineto +-101 -15 rlineto +0 -42 rlineto +654 0 rlineto +0 210 rlineto +-54 0 rlineto +-18 -137 rlineto +-28 -4 -95 -6 rquadto +-65 -3 -103 -3 rquadto +-96 0 rlineto +0 321 rlineto +190 0 rlineto +18 -100 rlineto +51 0 rlineto +0 265 rlineto +-51 0 rlineto +-18 -101 rlineto +-190 0 rlineto +} def +/P$2e { +0 82 -62 128 rquadto +-62 43 -173 43 rquadto +-109 0 -168 -35 rquadto +-57 -35 -75 -110 rquadto +84 -17 rlineto +12 46 51 68 rquadto +39 21 107 21 rquadto +73 0 106 -21 rquadto +34 -23 34 -68 rquadto +0 -34 -23 -54 rquadto +-23 -21 -76 -35 rquadto +-68 -18 rlineto +-82 -21 -118 -42 rquadto +-34 -20 -54 -50 rquadto +-20 -29 -20 -73 rquadto +0 -79 56 -120 rquadto +57 -42 165 -42 rquadto +96 0 153 34 rquadto +56 34 71 107 rquadto +-87 10 rlineto +-7 -39 -43 -59 rquadto +-34 -20 -93 -20 rquadto +-65 0 -96 20 rquadto +-29 18 -29 59 rquadto +0 25 12 42 rquadto +12 15 37 26 rquadto +25 10 106 31 rquadto +76 18 110 35 rquadto +34 15 53 35 rquadto +20 20 31 46 rquadto +10 25 10 57 rquadto +} def +/P$1u { +0 -796 rlineto +96 0 rlineto +0 796 rlineto +-96 0 rlineto +} def +/P$3t { +0 79 -54 125 rquadto +-54 45 -154 45 rquadto +-84 0 -157 -18 rquadto +-4 -125 rlineto +28 0 rlineto +20 82 rlineto +17 9 48 17 rquadto +31 6 57 6 rquadto +70 0 103 -31 rquadto +32 -31 32 -104 rquadto +0 -59 -31 -89 rquadto +-29 -29 -93 -32 rquadto +-62 -3 rlineto +0 -35 rlineto +62 -4 rlineto +50 -3 73 -31 rquadto +23 -28 23 -84 rquadto +0 -59 -26 -85 rquadto +-25 -28 -81 -28 rquadto +-21 0 -48 6 rquadto +-25 6 -43 17 rquadto +-15 71 rlineto +-29 0 rlineto +0 -112 rlineto +43 -12 75 -15 rquadto +31 -3 62 -3 rquadto +189 0 189 143 rquadto +0 62 -34 98 rquadto +-32 35 -93 45 rquadto +79 7 117 45 rquadto +39 35 39 101 rquadto +} def +/P$2a { +0 -140 -78 -214 rquadto +-76 -75 -221 -75 rquadto +-146 0 rlineto +0 592 rlineto +168 0 rlineto +84 0 146 -35 rquadto +62 -35 96 -104 rquadto +34 -68 34 -162 rquadto +} def +/P$3d { +0 -142 -26 -204 rquadto +-26 -62 -85 -62 rquadto +-56 0 -81 59 rquadto +-25 59 -25 207 rquadto +0 148 25 209 rquadto +25 60 81 60 rquadto +57 0 84 -62 rquadto +28 -64 28 -207 rquadto +} def +/P$2b { +0 304 -214 304 rquadto +-65 0 -109 -23 rquadto +-43 -25 -71 -78 rquadto +0 0 rlineto +0 17 -3 51 rquadto +-1 34 -3 39 rquadto +-93 0 rlineto +3 -29 3 -120 rquadto +0 -676 rlineto +96 0 rlineto +0 226 rlineto +0 35 -1 82 rquadto +1 0 rlineto +28 -56 71 -79 rquadto +43 -25 109 -25 rquadto +110 0 162 75 rquadto +51 73 51 223 rquadto +closepath +} def +/P$1h { +53 0 93 -23 rquadto +42 -23 65 -65 rquadto +25 -42 25 -87 rquadto +0 -48 rlineto +-106 3 rlineto +-67 0 -103 14 rquadto +-34 12 -53 39 rquadto +-18 26 -18 70 rquadto +0 46 25 73 rquadto +25 25 71 25 rquadto +} def +/P$2i { +-196 -314 rlineto +-235 0 rlineto +0 314 rlineto +-101 0 rlineto +0 -756 rlineto +356 0 rlineto +126 0 196 57 rquadto +70 56 70 157 rquadto +0 84 -50 142 rquadto +-48 57 -135 71 rquadto +215 326 rlineto +-118 0 rlineto +closepath +} def +/P$24 { +50 0 70 -29 rquadto +21 -29 21 -85 rquadto +0 -192 rlineto +0 -53 28 -89 rquadto +28 -35 75 -46 rquadto +0 -1 rlineto +-45 -10 -75 -46 rquadto +-28 -35 -28 -89 rquadto +0 -190 rlineto +0 -57 -21 -85 rquadto +-20 -29 -70 -29 rquadto +-31 0 rlineto +0 -68 rlineto +70 0 rlineto +70 0 106 40 rquadto +35 40 35 117 rquadto +0 193 rlineto +0 62 28 93 rquadto +28 31 87 32 rquadto +0 68 rlineto +-59 1 -87 32 rquadto +-28 31 -28 93 rquadto +0 193 rlineto +0 75 -37 115 rquadto +-35 42 -104 42 rquadto +-70 0 rlineto +0 -68 rlineto +31 0 rlineto +closepath +} def +/P$1r { +0 -120 -35 -175 rquadto +-35 -54 -120 -54 rquadto +-84 0 -121 56 rquadto +-37 54 -37 173 rquadto +0 114 35 171 rquadto +37 57 118 57 rquadto +85 0 123 -54 rquadto +37 -56 37 -175 rquadto +} def +/P$2p { +0 118 -45 207 rquadto +-45 89 -131 137 rquadto +-84 46 -200 46 rquadto +-115 0 -201 -46 rquadto +-84 -48 -129 -137 rquadto +-43 -89 -43 -207 rquadto +0 -182 100 -284 rquadto +100 -103 276 -103 rquadto +115 0 200 46 rquadto +85 45 129 132 rquadto +45 87 45 207 rquadto +closepath +} def +/P$10 { +39 -20 rlineto +82 -42 148 -42 rquadto +151 0 151 162 rquadto +0 350 rlineto +56 14 rlineto +0 39 rlineto +-275 0 rlineto +0 -39 rlineto +50 -14 rlineto +0 -326 rlineto +0 -50 -21 -76 rquadto +-20 -28 -57 -28 rquadto +-45 0 -89 20 rquadto +0 410 rlineto +50 14 rlineto +0 39 rlineto +-273 0 rlineto +0 -39 rlineto +53 -14 rlineto +0 -445 rlineto +-53 -14 rlineto +0 -39 rlineto +214 0 rlineto +7 48 rlineto +} def +/P$2h { +0 -154 48 -278 rquadto +48 -123 150 -232 rquadto +92 0 rlineto +-100 110 -146 237 rquadto +-46 125 -46 275 rquadto +0 148 45 273 rquadto +46 125 148 239 rquadto +-92 0 rlineto +-101 -109 -150 -232 rquadto +-48 -125 -48 -279 rquadto +0 -1 rlineto +} def +/P$3 { +0 -26 rlineto +104 -12 rlineto +0 -573 rlineto +-25 0 rlineto +-123 0 -168 9 rquadto +-12 101 rlineto +-32 0 rlineto +0 -153 rlineto +575 0 rlineto +0 153 rlineto +-32 0 rlineto +-12 -101 rlineto +-15 -3 -65 -6 rquadto +-48 -3 -106 -3 rquadto +-25 0 rlineto +0 573 rlineto +104 12 rlineto +0 26 rlineto +-303 0 rlineto +} def +/P$3i { +101 15 rlineto +0 28 rlineto +-309 0 rlineto +0 -28 rlineto +101 -15 rlineto +0 -507 rlineto +-84 -17 rlineto +0 -28 rlineto +190 0 rlineto +0 553 rlineto +} def +/P$t { +0 -98 54 -153 rquadto +54 -54 156 -54 rquadto +110 0 162 81 rquadto +53 79 53 251 rquadto +0 165 -67 253 rquadto +-67 85 -187 85 rquadto +-79 0 -146 -15 rquadto +0 -114 rlineto +32 0 rlineto +15 70 rlineto +15 7 42 14 rquadto +26 4 53 4 rquadto +78 0 120 -68 rquadto +42 -68 46 -201 rquadto +-75 42 -151 42 rquadto +-85 0 -135 -51 rquadto +-48 -51 -48 -143 rquadto +closepath +} def +/P$34 { +96 0 142 40 rquadto +46 39 46 121 rquadto +0 403 rlineto +75 15 rlineto +0 28 rlineto +-164 0 rlineto +-12 -59 rlineto +-71 71 -184 71 rquadto +-153 0 -153 -178 rquadto +0 -59 23 -98 rquadto +23 -39 73 -59 rquadto +51 -20 148 -21 rquadto +89 -3 rlineto +0 -93 rlineto +0 -60 -23 -90 rquadto +-21 -29 -68 -29 rquadto +-64 0 -115 31 rquadto +-21 73 rlineto +-35 0 rlineto +0 -129 rlineto +103 -21 181 -21 rquadto +closepath +} def +/P$1g { +-87 0 -131 -45 rquadto +-43 -46 -43 -128 rquadto +0 -90 59 -137 rquadto +59 -48 190 -53 rquadto +131 -1 rlineto +0 -31 rlineto +0 -71 -31 -101 rquadto +-29 -31 -93 -31 rquadto +-64 0 -93 21 rquadto +-29 21 -35 70 rquadto +-101 -7 rlineto +25 -157 234 -157 rquadto +109 0 164 50 rquadto +56 50 56 145 rquadto +0 250 rlineto +0 43 10 65 rquadto +10 21 42 21 rquadto +14 0 32 -4 rquadto +0 60 rlineto +-37 7 -75 7 rquadto +-54 0 -79 -28 rquadto +-23 -28 -26 -87 rquadto +-3 0 rlineto +-37 65 -87 93 rquadto +-48 28 -118 28 rquadto +closepath +} def +/P$31 { +0 -350 rlineto +-409 0 rlineto +0 350 rlineto +-101 0 rlineto +0 -756 rlineto +101 0 rlineto +0 320 rlineto +409 0 rlineto +0 -320 rlineto +103 0 rlineto +0 756 rlineto +-103 0 rlineto +} def +/P$15 { +0 409 -257 409 rquadto +-125 0 -189 -104 rquadto +-62 -104 -62 -304 rquadto +0 -193 62 -296 rquadto +64 -104 193 -104 rquadto +123 0 187 103 rquadto +65 101 65 298 rquadto +closepath +} def +/P$38 { +0 10 rlineto +0 87 18 135 rquadto +18 48 59 75 rquadto +40 25 106 25 rquadto +34 0 81 -4 rquadto +46 -6 76 -14 rquadto +0 35 rlineto +-29 18 -82 34 rquadto +-51 14 -106 14 rquadto +-139 0 -203 -75 rquadto +-64 -75 -64 -240 rquadto +0 -156 64 -232 rquadto +65 -76 187 -76 rquadto +228 0 228 260 rquadto +0 51 rlineto +-365 0 rlineto +closepath +} def +/P$z { +-50 1 rlineto +-56 3 -78 31 rquadto +-21 26 -21 87 rquadto +0 50 17 73 rquadto +17 23 45 23 rquadto +40 0 87 -20 rquadto +0 -196 rlineto +} def +/P$1y { +0 -92 rlineto +96 0 rlineto +0 92 rlineto +-96 0 rlineto +closepath +} def +/P$3r { +0 314 -278 314 rquadto +-135 0 -204 -79 rquadto +-67 -81 -67 -234 rquadto +0 -151 67 -231 rquadto +68 -79 209 -79 rquadto +135 0 204 78 rquadto +68 78 68 232 rquadto +closepath +} def +/P$22 { +0 -368 rlineto +0 -84 -23 -115 rquadto +-23 -32 -82 -32 rquadto +-62 0 -98 46 rquadto +-35 46 -35 132 rquadto +0 337 rlineto +-95 0 rlineto +0 -457 rlineto +0 -101 -3 -123 rquadto +90 0 rlineto +1 3 1 15 rquadto +0 10 0 26 rquadto +1 14 3 57 rquadto +1 0 rlineto +31 -62 70 -85 rquadto +40 -25 100 -25 rquadto +65 0 103 26 rquadto +39 26 54 84 rquadto +1 0 rlineto +29 -59 71 -84 rquadto +43 -26 103 -26 rquadto +89 0 128 48 rquadto +40 46 40 156 rquadto +0 387 rlineto +-95 0 rlineto +0 -368 rlineto +0 -84 -23 -115 rquadto +-23 -32 -82 -32 rquadto +-64 0 -100 46 rquadto +-34 46 -34 132 rquadto +0 337 rlineto +-95 0 rlineto +} def +/P$1a { +-34 0 -51 37 rquadto +-17 37 -17 134 rquadto +126 0 rlineto +0 -78 -4 -109 rquadto +-4 -32 -18 -46 rquadto +-12 -15 -34 -15 rquadto +} def +/P$3o { +103 15 rlineto +0 28 rlineto +-309 0 rlineto +0 -28 rlineto +101 -15 rlineto +0 -814 rlineto +-101 -15 rlineto +0 -28 rlineto +206 0 rlineto +0 857 rlineto +} def +/P$1s { +-46 14 -96 14 rquadto +-115 0 -115 -132 rquadto +0 -387 rlineto +-67 0 rlineto +0 -70 rlineto +70 0 rlineto +28 -129 rlineto +65 0 rlineto +0 129 rlineto +106 0 rlineto +0 70 rlineto +-106 0 rlineto +0 367 rlineto +0 42 12 59 rquadto +14 15 48 15 rquadto +18 0 54 -6 rquadto +0 70 rlineto +} def +/P$1p { +-125 0 -195 81 rquadto +-70 81 -70 221 rquadto +0 139 73 223 rquadto +73 84 196 84 rquadto +159 0 239 -157 rquadto +84 42 rlineto +-46 98 -132 150 rquadto +-84 50 -195 50 rquadto +-114 0 -198 -46 rquadto +-82 -48 -126 -135 rquadto +-43 -89 -43 -209 rquadto +0 -181 96 -284 rquadto +98 -103 271 -103 rquadto +120 0 201 48 rquadto +81 46 118 139 rquadto +-96 32 rlineto +-26 -65 -85 -100 rquadto +-57 -35 -137 -35 rquadto +} def +/P$30 { +0 -121 -32 -173 rquadto +-32 -53 -104 -53 rquadto +-56 0 -89 25 rquadto +-32 23 -50 75 rquadto +-17 51 -17 132 rquadto +0 115 35 170 rquadto +37 53 120 53 rquadto +71 0 104 -53 rquadto +32 -53 32 -176 rquadto +} def +/P$6 { +0 9 rlineto +0 67 14 104 rquadto +15 37 46 57 rquadto +31 18 81 18 rquadto +26 0 62 -4 rquadto +35 -4 59 -9 rquadto +0 26 rlineto +-23 15 -64 26 rquadto +-39 10 -81 10 rquadto +-107 0 -157 -57 rquadto +-48 -57 -48 -184 rquadto +0 -120 50 -179 rquadto +50 -59 143 -59 rquadto +176 0 176 201 rquadto +0 39 rlineto +-282 0 rlineto +closepath +} def +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +3720 2533 moveto +P$0 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +4662 2302 moveto +P$1 +4573 2302 moveto +P$2 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +11603 2533 moveto +P$3 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +12203 2402 moveto +P$4 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +20020 2549 moveto +P$5 +20376 2302 moveto +P$6 +20482 2100 moveto +P$7 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +27603 2533 moveto +P$3 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +28209 2038 moveto +P$8 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +35757 2239 moveto +P$9 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +36475 2061 moveto +P$a +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +43567 2357 moveto +P$b +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +44326 2064 moveto +P$c +44392 2299 moveto +P$d +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +51517 2357 moveto +P$b +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +52203 2402 moveto +P$4 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +59720 2533 moveto +P$0 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +60662 2302 moveto +P$1 +60573 2302 moveto +P$2 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +67603 2533 moveto +P$3 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +68203 2402 moveto +P$4 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +76020 2549 moveto +P$5 +76376 2302 moveto +P$6 +76482 2100 moveto +P$7 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +83603 2533 moveto +P$3 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +84209 2038 moveto +P$8 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +91757 2239 moveto +P$9 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +92475 2061 moveto +P$a +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +99567 2357 moveto +P$b +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +100326 2064 moveto +P$c +100392 2299 moveto +P$d +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +107517 2357 moveto +P$b +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +108203 2402 moveto +P$4 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +115720 2533 moveto +P$0 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +116662 2302 moveto +P$1 +116573 2302 moveto +P$2 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +123603 2533 moveto +P$3 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +124203 2402 moveto +P$4 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +132020 2549 moveto +P$5 +132376 2302 moveto +P$6 +132482 2100 moveto +P$7 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +139603 2533 moveto +P$3 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +140209 2038 moveto +P$8 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +147757 2239 moveto +P$9 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +148475 2061 moveto +P$a +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +155567 2357 moveto +P$b +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +156326 2064 moveto +P$c +156392 2299 moveto +P$d +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +163517 2357 moveto +P$b +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +164203 2402 moveto +P$4 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +171720 2533 moveto +P$0 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +172662 2302 moveto +P$1 +172573 2302 moveto +P$2 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +179603 2533 moveto +P$3 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +180203 2402 moveto +P$4 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +188020 2549 moveto +P$5 +188376 2302 moveto +P$6 +188482 2100 moveto +P$7 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +195603 2533 moveto +P$3 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +196209 2038 moveto +P$8 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +203757 2239 moveto +P$9 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +204475 2061 moveto +P$a +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +3960 3555 moveto +P$e +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +4462 3403 moveto +P$f +4371 3403 moveto +P$g +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +11960 3555 moveto +P$e +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +12306 3694 moveto +P$h +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +20056 3694 moveto +P$h +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +28195 3733 moveto +P$i +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +36210 3555 moveto +P$j +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +44145 3589 moveto +P$k +44060 3189 moveto +P$l +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +51987 3350 moveto +P$m +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +60220 3530 moveto +P$n +60018 3705 moveto +P$o +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +67848 3233 moveto +P$p +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +76192 3238 moveto +P$q +76123 3557 moveto +P$r +76104 3238 moveto +P$s +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +83782 3278 moveto +P$t +83993 3110 moveto +P$u +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +91806 3694 moveto +P$v +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +92462 3403 moveto +P$f +92371 3403 moveto +P$g +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +99806 3694 moveto +P$v +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +100306 3694 moveto +P$h +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +107806 3694 moveto +P$v +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +108445 3733 moveto +P$i +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +115806 3694 moveto +P$v +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +116460 3555 moveto +P$j +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +123806 3694 moveto +P$v +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +124395 3589 moveto +P$k +124310 3189 moveto +P$l +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +131806 3694 moveto +P$v +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +132237 3350 moveto +P$m +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +139806 3694 moveto +P$v +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +140470 3530 moveto +P$n +140268 3705 moveto +P$o +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +147806 3694 moveto +P$v +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +148098 3233 moveto +P$p +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +155806 3694 moveto +P$v +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +156442 3238 moveto +P$q +156373 3557 moveto +P$r +156354 3238 moveto +P$s +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +163806 3694 moveto +P$v +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +164032 3278 moveto +P$t +164243 3110 moveto +P$u +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +171945 3733 moveto +P$w +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +172462 3403 moveto +P$f +172371 3403 moveto +P$g +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +179945 3733 moveto +P$w +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +180306 3694 moveto +P$h +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +187945 3733 moveto +P$w +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +188445 3733 moveto +P$i +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +195945 3733 moveto +P$w +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +196460 3555 moveto +P$j +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +203945 3733 moveto +P$w +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +204395 3589 moveto +P$k +204310 3189 moveto +P$l +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +4793 392 moveto +P$x +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +5420 557 moveto +P$y +5457 843 moveto +P$z +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +5954 617 moveto +P$10 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +6812 1071 moveto +P$11 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +7420 557 moveto +P$y +7457 843 moveto +P$z +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +7970 684 moveto +P$12 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +8470 1137 moveto +P$13 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +9648 1120 moveto +P$14 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +10254 723 moveto +P$15 +10082 723 moveto +P$16 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +10848 1120 moveto +P$14 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +11459 906 moveto +P$17 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +108556 784 moveto +P$18 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +109239 556 moveto +P$19 +109232 615 moveto +P$1a +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +109896 829 moveto +P$1b +109521 339 moveto +P$1c +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +110420 684 moveto +P$12 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +111062 1071 moveto +P$11 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +111670 557 moveto +P$y +111707 843 moveto +P$z +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +112220 684 moveto +P$12 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +112720 1137 moveto +P$13 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +113898 1120 moveto +P$14 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +114504 723 moveto +P$15 +114332 723 moveto +P$16 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +115098 1120 moveto +P$14 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +115709 906 moveto +P$17 +fill +100 setlinewidth +0.75 0.75 0.75 setrgbcolor +newpath +0 4100 moveto +0 47503 rlineto +stroke +100 setlinewidth +0.75 0.75 0.75 setrgbcolor +newpath +8000 4100 moveto +0 47503 rlineto +stroke +100 setlinewidth +0.75 0.75 0.75 setrgbcolor +newpath +16000 4100 moveto +0 47503 rlineto +stroke +100 setlinewidth +0.75 0.75 0.75 setrgbcolor +newpath +24000 4100 moveto +0 47503 rlineto +stroke +100 setlinewidth +0.75 0.75 0.75 setrgbcolor +newpath +32000 4100 moveto +0 47503 rlineto +stroke +100 setlinewidth +0.75 0.75 0.75 setrgbcolor +newpath +40000 4100 moveto +0 47503 rlineto +stroke +100 setlinewidth +0.75 0.75 0.75 setrgbcolor +newpath +48000 4100 moveto +0 47503 rlineto +stroke +100 setlinewidth +0.75 0.75 0.75 setrgbcolor +newpath +56000 4100 moveto +0 47503 rlineto +stroke +100 setlinewidth +0.75 0.75 0.75 setrgbcolor +newpath +64000 4100 moveto +0 47503 rlineto +stroke +100 setlinewidth +0.75 0.75 0.75 setrgbcolor +newpath +72000 4100 moveto +0 47503 rlineto +stroke +100 setlinewidth +0.75 0.75 0.75 setrgbcolor +newpath +80000 4100 moveto +0 47503 rlineto +stroke +100 setlinewidth +0.75 0.75 0.75 setrgbcolor +newpath +88000 4100 moveto +0 47503 rlineto +stroke +100 setlinewidth +0.75 0.75 0.75 setrgbcolor +newpath +96000 4100 moveto +0 47503 rlineto +stroke +100 setlinewidth +0.75 0.75 0.75 setrgbcolor +newpath +104000 4100 moveto +0 47503 rlineto +stroke +100 setlinewidth +0.75 0.75 0.75 setrgbcolor +newpath +112000 4100 moveto +0 47503 rlineto +stroke +100 setlinewidth +0.75 0.75 0.75 setrgbcolor +newpath +120000 4100 moveto +0 47503 rlineto +stroke +100 setlinewidth +0.75 0.75 0.75 setrgbcolor +newpath +128000 4100 moveto +0 47503 rlineto +stroke +100 setlinewidth +0.75 0.75 0.75 setrgbcolor +newpath +136000 4100 moveto +0 47503 rlineto +stroke +100 setlinewidth +0.75 0.75 0.75 setrgbcolor +newpath +144000 4100 moveto +0 47503 rlineto +stroke +100 setlinewidth +0.75 0.75 0.75 setrgbcolor +newpath +152000 4100 moveto +0 47503 rlineto +stroke +100 setlinewidth +0.75 0.75 0.75 setrgbcolor +newpath +160000 4100 moveto +0 47503 rlineto +stroke +100 setlinewidth +0.75 0.75 0.75 setrgbcolor +newpath +168000 4100 moveto +0 47503 rlineto +stroke +100 setlinewidth +0.75 0.75 0.75 setrgbcolor +newpath +176000 4100 moveto +0 47503 rlineto +stroke +100 setlinewidth +0.75 0.75 0.75 setrgbcolor +newpath +184000 4100 moveto +0 47503 rlineto +stroke +100 setlinewidth +0.75 0.75 0.75 setrgbcolor +newpath +192000 4100 moveto +0 47503 rlineto +stroke +100 setlinewidth +0.75 0.75 0.75 setrgbcolor +newpath +200000 4100 moveto +0 47503 rlineto +stroke +100 setlinewidth +0.75 0.75 0.75 setrgbcolor +newpath +208000 4100 moveto +0 47503 rlineto +stroke +100 setlinewidth +0.75 0.75 0.75 setrgbcolor +newpath +0 4100 moveto +208000 0 rlineto +stroke +100 setlinewidth +0.75 0.75 0.75 setrgbcolor +newpath +0 51603 moveto +208000 0 rlineto +stroke +150 setlinewidth +0.09 0.09 0.09 setrgbcolor +newpath +20000 10494 moveto +0 1032 rlineto +stroke +150 setlinewidth +0.09 0.09 0.09 setrgbcolor +newpath +20000 11527 moveto +3900 0 rlineto +stroke +0.09 0.09 0.09 setrgbcolor +newpath +23500 11127 moveto +0 400 rlineto +0 400 rlineto +400 -400 rlineto +23500 11127 lineto +closepath eofill +150 setlinewidth +0.09 0.09 0.09 setrgbcolor +newpath +23500 11127 moveto +0 400 rlineto +0 400 rlineto +400 -400 rlineto +23500 11127 lineto +closepath stroke +150 setlinewidth +0.09 0.09 0.09 setrgbcolor +newpath +68000 12159 moveto +0 1032 rlineto +stroke +150 setlinewidth +0.09 0.09 0.09 setrgbcolor +newpath +68000 13192 moveto +3900 0 rlineto +stroke +0.09 0.09 0.09 setrgbcolor +newpath +71500 12792 moveto +0 400 rlineto +0 400 rlineto +400 -400 rlineto +71500 12792 lineto +closepath eofill +150 setlinewidth +0.09 0.09 0.09 setrgbcolor +newpath +71500 12792 moveto +0 400 rlineto +0 400 rlineto +400 -400 rlineto +71500 12792 lineto +closepath stroke +150 setlinewidth +0.09 0.09 0.09 setrgbcolor +newpath +68000 12159 moveto +0 2697 rlineto +stroke +150 setlinewidth +0.09 0.09 0.09 setrgbcolor +newpath +68000 14856 moveto +3900 0 rlineto +stroke +0.09 0.09 0.09 setrgbcolor +newpath +71500 14456 moveto +0 400 rlineto +0 400 rlineto +400 -400 rlineto +71500 14456 lineto +closepath eofill +150 setlinewidth +0.09 0.09 0.09 setrgbcolor +newpath +71500 14456 moveto +0 400 rlineto +0 400 rlineto +400 -400 rlineto +71500 14456 lineto +closepath stroke +150 setlinewidth +0.09 0.09 0.09 setrgbcolor +newpath +92000 15489 moveto +0 1032 rlineto +stroke +150 setlinewidth +0.09 0.09 0.09 setrgbcolor +newpath +92000 16521 moveto +3900 0 rlineto +stroke +0.09 0.09 0.09 setrgbcolor +newpath +95500 16121 moveto +0 400 rlineto +0 400 rlineto +400 -400 rlineto +95500 16121 lineto +closepath eofill +150 setlinewidth +0.09 0.09 0.09 setrgbcolor +newpath +95500 16121 moveto +0 400 rlineto +0 400 rlineto +400 -400 rlineto +95500 16121 lineto +closepath stroke +150 setlinewidth +0.09 0.09 0.09 setrgbcolor +newpath +92000 15489 moveto +0 2697 rlineto +stroke +150 setlinewidth +0.09 0.09 0.09 setrgbcolor +newpath +92000 18186 moveto +3900 0 rlineto +stroke +0.09 0.09 0.09 setrgbcolor +newpath +95500 17786 moveto +0 400 rlineto +0 400 rlineto +400 -400 rlineto +95500 17786 lineto +closepath eofill +150 setlinewidth +0.09 0.09 0.09 setrgbcolor +newpath +95500 17786 moveto +0 400 rlineto +0 400 rlineto +400 -400 rlineto +95500 17786 lineto +closepath stroke +150 setlinewidth +0.09 0.09 0.09 setrgbcolor +newpath +92000 15489 moveto +0 4362 rlineto +stroke +150 setlinewidth +0.09 0.09 0.09 setrgbcolor +newpath +92000 19851 moveto +3900 0 rlineto +stroke +0.09 0.09 0.09 setrgbcolor +newpath +95500 19451 moveto +0 400 rlineto +0 400 rlineto +400 -400 rlineto +95500 19451 lineto +closepath eofill +150 setlinewidth +0.09 0.09 0.09 setrgbcolor +newpath +95500 19451 moveto +0 400 rlineto +0 400 rlineto +400 -400 rlineto +95500 19451 lineto +closepath stroke +150 setlinewidth +0.09 0.09 0.09 setrgbcolor +newpath +108000 20484 moveto +0 1032 rlineto +stroke +150 setlinewidth +0.09 0.09 0.09 setrgbcolor +newpath +108000 21516 moveto +3900 0 rlineto +stroke +0.09 0.09 0.09 setrgbcolor +newpath +111500 21116 moveto +0 400 rlineto +0 400 rlineto +400 -400 rlineto +111500 21116 lineto +closepath eofill +150 setlinewidth +0.09 0.09 0.09 setrgbcolor +newpath +111500 21116 moveto +0 400 rlineto +0 400 rlineto +400 -400 rlineto +111500 21116 lineto +closepath stroke +150 setlinewidth +0.09 0.09 0.09 setrgbcolor +newpath +52000 30408 moveto +0 1032 rlineto +stroke +150 setlinewidth +0.09 0.09 0.09 setrgbcolor +newpath +52000 31440 moveto +3900 0 rlineto +stroke +0.09 0.09 0.09 setrgbcolor +newpath +55500 31040 moveto +0 400 rlineto +0 400 rlineto +400 -400 rlineto +55500 31040 lineto +closepath eofill +150 setlinewidth +0.09 0.09 0.09 setrgbcolor +newpath +55500 31040 moveto +0 400 rlineto +0 400 rlineto +400 -400 rlineto +55500 31040 lineto +closepath stroke +150 setlinewidth +0.09 0.09 0.09 setrgbcolor +newpath +60000 32073 moveto +0 1032 rlineto +stroke +150 setlinewidth +0.09 0.09 0.09 setrgbcolor +newpath +60000 33105 moveto +3900 0 rlineto +stroke +0.09 0.09 0.09 setrgbcolor +newpath +63500 32705 moveto +0 400 rlineto +0 400 rlineto +400 -400 rlineto +63500 32705 lineto +closepath eofill +150 setlinewidth +0.09 0.09 0.09 setrgbcolor +newpath +63500 32705 moveto +0 400 rlineto +0 400 rlineto +400 -400 rlineto +63500 32705 lineto +closepath stroke +100 setlinewidth +0.09 0.09 0.09 setrgbcolor +newpath +0 5732 moveto +500 0 rlineto +stroke +100 setlinewidth +0.09 0.09 0.09 setrgbcolor +newpath +5800 5732 moveto +202100 0 rlineto +stroke +0.89 0.89 0.94 setrgbcolor +23600 1264 200 7564 simplerect +closepath eofill +100 setlinewidth +0.09 0.09 0.09 setrgbcolor +newpath +200 7564 moveto +23600 0 rlineto +stroke +100 setlinewidth +0.09 0.09 0.09 setrgbcolor +newpath +200 8829 moveto +23600 0 rlineto +stroke +100 setlinewidth +0.09 0.09 0.09 setrgbcolor +newpath +200 7564 moveto +0 1264 rlineto +stroke +100 setlinewidth +0.09 0.09 0.09 setrgbcolor +newpath +23800 7564 moveto +0 1264 rlineto +stroke +0.89 0.89 0.94 setrgbcolor +23600 1264 200 9229 simplerect +closepath eofill +100 setlinewidth +0.09 0.09 0.09 setrgbcolor +newpath +200 9229 moveto +23600 0 rlineto +stroke +100 setlinewidth +0.09 0.09 0.09 setrgbcolor +newpath +200 10494 moveto +23600 0 rlineto +stroke +100 setlinewidth +0.09 0.09 0.09 setrgbcolor +newpath +200 9229 moveto +0 1264 rlineto +stroke +100 setlinewidth +0.09 0.09 0.09 setrgbcolor +newpath +23800 9229 moveto +0 1264 rlineto +stroke +0.89 0.89 0.94 setrgbcolor +47600 1264 24200 10894 simplerect +closepath eofill +100 setlinewidth +0.09 0.09 0.09 setrgbcolor +newpath +24200 10894 moveto +47600 0 rlineto +stroke +100 setlinewidth +0.09 0.09 0.09 setrgbcolor +newpath +24200 12159 moveto +47600 0 rlineto +stroke +100 setlinewidth +0.09 0.09 0.09 setrgbcolor +newpath +24200 10894 moveto +0 1264 rlineto +stroke +100 setlinewidth +0.09 0.09 0.09 setrgbcolor +newpath +71800 10894 moveto +0 1264 rlineto +stroke +0.89 0.89 0.94 setrgbcolor +127600 1264 72200 12559 simplerect +closepath eofill +100 setlinewidth +0.09 0.09 0.09 setrgbcolor +newpath +72200 12559 moveto +127600 0 rlineto +stroke +100 setlinewidth +0.09 0.09 0.09 setrgbcolor +newpath +72200 13824 moveto +127600 0 rlineto +stroke +100 setlinewidth +0.09 0.09 0.09 setrgbcolor +newpath +72200 12559 moveto +0 1264 rlineto +stroke +100 setlinewidth +0.09 0.09 0.09 setrgbcolor +newpath +199800 12559 moveto +0 1264 rlineto +stroke +0.89 0.89 0.94 setrgbcolor +23600 1264 72200 14224 simplerect +closepath eofill +100 setlinewidth +0.09 0.09 0.09 setrgbcolor +newpath +72200 14224 moveto +23600 0 rlineto +stroke +100 setlinewidth +0.09 0.09 0.09 setrgbcolor +newpath +72200 15489 moveto +23600 0 rlineto +stroke +100 setlinewidth +0.09 0.09 0.09 setrgbcolor +newpath +72200 14224 moveto +0 1264 rlineto +stroke +100 setlinewidth +0.09 0.09 0.09 setrgbcolor +newpath +95800 14224 moveto +0 1264 rlineto +stroke +0.89 0.89 0.94 setrgbcolor +103600 1264 96200 15889 simplerect +closepath eofill +100 setlinewidth +0.09 0.09 0.09 setrgbcolor +newpath +96200 15889 moveto +103600 0 rlineto +stroke +100 setlinewidth +0.09 0.09 0.09 setrgbcolor +newpath +96200 17154 moveto +103600 0 rlineto +stroke +100 setlinewidth +0.09 0.09 0.09 setrgbcolor +newpath +96200 15889 moveto +0 1264 rlineto +stroke +100 setlinewidth +0.09 0.09 0.09 setrgbcolor +newpath +199800 15889 moveto +0 1264 rlineto +stroke +0.89 0.89 0.94 setrgbcolor +111600 1264 96200 17554 simplerect +closepath eofill +100 setlinewidth +0.09 0.09 0.09 setrgbcolor +newpath +96200 17554 moveto +111600 0 rlineto +stroke +100 setlinewidth +0.09 0.09 0.09 setrgbcolor +newpath +96200 18819 moveto +111600 0 rlineto +stroke +100 setlinewidth +0.09 0.09 0.09 setrgbcolor +newpath +96200 17554 moveto +0 1264 rlineto +stroke +100 setlinewidth +0.09 0.09 0.09 setrgbcolor +newpath +207800 17554 moveto +0 1264 rlineto +stroke +0.89 0.89 0.94 setrgbcolor +15600 1264 96200 19219 simplerect +closepath eofill +100 setlinewidth +0.09 0.09 0.09 setrgbcolor +newpath +96200 19219 moveto +15600 0 rlineto +stroke +100 setlinewidth +0.09 0.09 0.09 setrgbcolor +newpath +96200 20484 moveto +15600 0 rlineto +stroke +100 setlinewidth +0.09 0.09 0.09 setrgbcolor +newpath +96200 19219 moveto +0 1264 rlineto +stroke +100 setlinewidth +0.09 0.09 0.09 setrgbcolor +newpath +111800 19219 moveto +0 1264 rlineto +stroke +0.89 0.89 0.94 setrgbcolor +95600 1264 112200 20884 simplerect +closepath eofill +100 setlinewidth +0.09 0.09 0.09 setrgbcolor +newpath +112200 20884 moveto +95600 0 rlineto +stroke +100 setlinewidth +0.09 0.09 0.09 setrgbcolor +newpath +112200 22148 moveto +95600 0 rlineto +stroke +100 setlinewidth +0.09 0.09 0.09 setrgbcolor +newpath +112200 20884 moveto +0 1264 rlineto +stroke +100 setlinewidth +0.09 0.09 0.09 setrgbcolor +newpath +207800 20884 moveto +0 1264 rlineto +stroke +0.89 0.89 0.94 setrgbcolor +7600 1264 120200 22548 simplerect +closepath eofill +100 setlinewidth +0.09 0.09 0.09 setrgbcolor +newpath +120200 22548 moveto +7600 0 rlineto +stroke +100 setlinewidth +0.09 0.09 0.09 setrgbcolor +newpath +120200 23813 moveto +7600 0 rlineto +stroke +100 setlinewidth +0.09 0.09 0.09 setrgbcolor +newpath +120200 22548 moveto +0 1264 rlineto +stroke +100 setlinewidth +0.09 0.09 0.09 setrgbcolor +newpath +127800 22548 moveto +0 1264 rlineto +stroke +100 setlinewidth +0.09 0.09 0.09 setrgbcolor +newpath +0 25646 moveto +500 0 rlineto +stroke +100 setlinewidth +0.09 0.09 0.09 setrgbcolor +newpath +5900 25646 moveto +202000 0 rlineto +stroke +0.89 0.89 0.94 setrgbcolor +63600 1264 200 27478 simplerect +closepath eofill +100 setlinewidth +0.09 0.09 0.09 setrgbcolor +newpath +200 27478 moveto +63600 0 rlineto +stroke +100 setlinewidth +0.09 0.09 0.09 setrgbcolor +newpath +200 28743 moveto +63600 0 rlineto +stroke +100 setlinewidth +0.09 0.09 0.09 setrgbcolor +newpath +200 27478 moveto +0 1264 rlineto +stroke +100 setlinewidth +0.09 0.09 0.09 setrgbcolor +newpath +63800 27478 moveto +0 1264 rlineto +stroke +0.89 0.89 0.94 setrgbcolor +39600 1264 16200 29143 simplerect +closepath eofill +100 setlinewidth +0.09 0.09 0.09 setrgbcolor +newpath +16200 29143 moveto +39600 0 rlineto +stroke +100 setlinewidth +0.09 0.09 0.09 setrgbcolor +newpath +16200 30408 moveto +39600 0 rlineto +stroke +100 setlinewidth +0.09 0.09 0.09 setrgbcolor +newpath +16200 29143 moveto +0 1264 rlineto +stroke +100 setlinewidth +0.09 0.09 0.09 setrgbcolor +newpath +55800 29143 moveto +0 1264 rlineto +stroke +0.89 0.89 0.94 setrgbcolor +7600 1264 56200 30808 simplerect +closepath eofill +100 setlinewidth +0.09 0.09 0.09 setrgbcolor +newpath +56200 30808 moveto +7600 0 rlineto +stroke +100 setlinewidth +0.09 0.09 0.09 setrgbcolor +newpath +56200 32073 moveto +7600 0 rlineto +stroke +100 setlinewidth +0.09 0.09 0.09 setrgbcolor +newpath +56200 30808 moveto +0 1264 rlineto +stroke +100 setlinewidth +0.09 0.09 0.09 setrgbcolor +newpath +63800 30808 moveto +0 1264 rlineto +stroke +0.89 0.89 0.94 setrgbcolor +111600 1264 64200 32473 simplerect +closepath eofill +100 setlinewidth +0.09 0.09 0.09 setrgbcolor +newpath +64200 32473 moveto +111600 0 rlineto +stroke +100 setlinewidth +0.09 0.09 0.09 setrgbcolor +newpath +64200 33738 moveto +111600 0 rlineto +stroke +100 setlinewidth +0.09 0.09 0.09 setrgbcolor +newpath +64200 32473 moveto +0 1264 rlineto +stroke +100 setlinewidth +0.09 0.09 0.09 setrgbcolor +newpath +175800 32473 moveto +0 1264 rlineto +stroke +0.89 0.89 0.94 setrgbcolor +39600 1264 48200 34138 simplerect +closepath eofill +100 setlinewidth +0.09 0.09 0.09 setrgbcolor +newpath +48200 34138 moveto +39600 0 rlineto +stroke +100 setlinewidth +0.09 0.09 0.09 setrgbcolor +newpath +48200 35403 moveto +39600 0 rlineto +stroke +100 setlinewidth +0.09 0.09 0.09 setrgbcolor +newpath +48200 34138 moveto +0 1264 rlineto +stroke +100 setlinewidth +0.09 0.09 0.09 setrgbcolor +newpath +87800 34138 moveto +0 1264 rlineto +stroke +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +1675 5919 moveto +P$1d +1528 5570 moveto +P$1e +1571 5909 moveto +P$1f +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +1921 6142 moveto +P$1g +1943 6069 moveto +P$1h +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +2448 5838 moveto +P$1i +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +3339 6131 moveto +P$1j +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +3648 5861 moveto +P$1k +3962 5788 moveto +P$1l +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +4543 6131 moveto +P$1m +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +5140 6038 moveto +P$1n +4848 5841 moveto +P$1o +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +1025 7912 moveto +P$1p +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +1965 8306 moveto +P$1q +1864 8306 moveto +P$1r +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +2443 8596 moveto +P$1m +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +2896 8591 moveto +P$1s +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +2976 8596 moveto +P$1t +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +3865 8306 moveto +P$1q +3764 8306 moveto +P$1r +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +3973 8596 moveto +P$1u +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +4173 8596 moveto +P$1u +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +4448 8326 moveto +P$1k +4762 8252 moveto +P$1l +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +4976 8596 moveto +P$1t +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +5348 8346 moveto +P$1v +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +6382 8387 moveto +P$1w +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +6548 8302 moveto +P$1i +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +7170 8115 moveto +P$1x +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +7673 7891 moveto +P$1y +7673 8596 moveto +P$1z +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +7948 8302 moveto +P$1i +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +8570 8115 moveto +P$1x +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +9296 8591 moveto +P$1s +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +9875 8824 moveto +P$20 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +10101 8596 moveto +P$21 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +10712 8596 moveto +P$22 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +11612 8596 moveto +P$22 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +12321 8607 moveto +P$1g +12343 8534 moveto +P$1h +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +13143 8596 moveto +P$1m +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +13468 8015 moveto +P$23 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +14048 8326 moveto +P$1k +14362 8252 moveto +P$1l +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +14573 8596 moveto +P$1u +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +14750 8756 moveto +P$24 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +1334 10261 moveto +P$25 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +2065 9970 moveto +P$1q +1964 9970 moveto +P$1r +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +2540 10167 moveto +P$1n +2248 9970 moveto +P$26 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +2848 9991 moveto +P$1k +3162 9917 moveto +P$1l +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +3373 10261 moveto +P$1u +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +3548 10011 moveto +P$1v +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +4575 9731 moveto +P$27 +4471 9733 moveto +P$28 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +4821 10272 moveto +P$1g +4843 10199 moveto +P$1h +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +5639 10261 moveto +P$1j +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +5948 9991 moveto +P$1k +6262 9917 moveto +P$1l +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +6696 10256 moveto +P$1s +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +7275 10489 moveto +P$20 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +8142 9875 moveto +P$29 +8039 9875 moveto +P$2a +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +8421 10272 moveto +P$1g +8443 10199 moveto +P$1h +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +9243 10261 moveto +P$1m +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +9473 9556 moveto +P$1y +9473 10261 moveto +P$1z +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +9748 9991 moveto +P$1k +10062 9917 moveto +P$1l +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +10273 10261 moveto +P$1u +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +10450 10420 moveto +P$24 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +25342 11540 moveto +P$29 +25239 11540 moveto +P$2a +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +25621 11937 moveto +P$1g +25643 11863 moveto +P$1h +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +26296 11921 moveto +P$1s +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +26448 11656 moveto +P$1k +26762 11582 moveto +P$1l +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +27343 11926 moveto +P$1m +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +28065 11632 moveto +P$2b +27964 11635 moveto +P$2c +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +28321 11937 moveto +P$1g +28343 11863 moveto +P$1h +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +29143 11926 moveto +P$1m +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +29739 11926 moveto +P$1j +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +30421 11937 moveto +P$1g +30443 11863 moveto +P$1h +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +30968 11345 moveto +P$23 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +31593 11415 moveto +P$2d +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +32210 11765 moveto +P$2e +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +32448 11656 moveto +P$1k +32762 11582 moveto +P$1l +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +33196 11921 moveto +P$1s +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +33245 11926 moveto +P$2f +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +33948 11656 moveto +P$1k +34262 11582 moveto +P$1l +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +34843 11926 moveto +P$1m +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +35575 12154 moveto +P$20 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +35801 11926 moveto +P$21 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +36412 11926 moveto +P$22 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +37312 11926 moveto +P$22 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +38021 11937 moveto +P$1g +38043 11863 moveto +P$1h +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +38843 11926 moveto +P$1m +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +39168 11345 moveto +P$23 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +39748 11656 moveto +P$1k +40062 11582 moveto +P$1l +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +40273 11926 moveto +P$1u +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +40450 12085 moveto +P$24 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +72992 13602 moveto +P$2g +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +73696 13586 moveto +P$1s +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +73773 12886 moveto +P$1y +73773 13591 moveto +P$1z +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +73973 13591 moveto +P$1u +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +74148 13341 moveto +P$1v +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +75175 13061 moveto +P$27 +75071 13063 moveto +P$28 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +75421 13602 moveto +P$1g +75443 13528 moveto +P$1h +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +76239 13591 moveto +P$1j +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +76548 13321 moveto +P$1k +76862 13247 moveto +P$1l +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +77296 13586 moveto +P$1s +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +77668 13305 moveto +P$2h +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +78625 13591 moveto +P$2i +78610 13052 moveto +P$2j +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +79482 13381 moveto +P$1w +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +80182 13381 moveto +P$1w +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +80875 13061 moveto +P$27 +80771 13063 moveto +P$28 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +81121 13602 moveto +P$1g +81143 13528 moveto +P$1h +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +81576 13591 moveto +P$1t +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +82410 13430 moveto +P$2e +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +82648 13321 moveto +P$1k +82962 13247 moveto +P$1l +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +83176 13591 moveto +P$1t +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +83798 13306 moveto +P$2k +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +84475 13819 moveto +P$20 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +85342 13205 moveto +P$29 +85239 13205 moveto +P$2a +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +85621 13602 moveto +P$1g +85643 13528 moveto +P$1h +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +86443 13591 moveto +P$1m +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +86673 12886 moveto +P$1y +86673 13591 moveto +P$1z +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +86948 13321 moveto +P$1k +87262 13247 moveto +P$1l +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +87473 13591 moveto +P$1u +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +87650 13750 moveto +P$2l +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +88575 13819 moveto +P$20 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +88790 13591 moveto +P$2m +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +89468 13010 moveto +P$23 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +90339 13591 moveto +P$1j +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +90721 13602 moveto +P$1g +90743 13528 moveto +P$1h +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +91610 13430 moveto +P$2e +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +91750 13750 moveto +P$24 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +73342 14870 moveto +P$29 +73239 14870 moveto +P$2a +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +74026 15256 moveto +P$2n +73767 14576 moveto +P$2o +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +74903 14875 moveto +P$2p +74798 14875 moveto +P$2q +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +75048 15006 moveto +P$1v +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +75501 15256 moveto +P$21 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +76143 15256 moveto +P$1m +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +76596 15251 moveto +P$1s +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +76748 14985 moveto +P$1k +77062 14912 moveto +P$1l +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +77276 15256 moveto +P$1t +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +77793 14745 moveto +P$2d +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +78121 15267 moveto +P$1g +78143 15193 moveto +P$1h +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +78648 14962 moveto +P$1i +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +79248 14985 moveto +P$1k +79562 14912 moveto +P$1l +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +80210 15095 moveto +P$2e +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +80875 15484 moveto +P$20 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +81245 15267 moveto +P$2r +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +81768 14675 moveto +P$23 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +82273 15256 moveto +P$1u +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +82473 14551 moveto +P$1y +82473 15256 moveto +P$1z +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +82768 14675 moveto +P$23 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +83710 15095 moveto +P$2e +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +83850 15415 moveto +P$2l +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +84775 15484 moveto +P$20 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +85001 15256 moveto +P$21 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +85612 15256 moveto +P$22 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +86512 15256 moveto +P$22 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +87221 15267 moveto +P$1g +87243 15193 moveto +P$1h +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +88043 15256 moveto +P$1m +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +88368 14675 moveto +P$23 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +88948 14985 moveto +P$1k +89262 14912 moveto +P$1l +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +89473 15256 moveto +P$1u +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +89650 15415 moveto +P$24 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +97226 16921 moveto +P$2n +96967 16241 moveto +P$2o +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +97468 16339 moveto +P$23 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +98196 16916 moveto +P$1s +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +98370 16439 moveto +P$1x +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +98948 16650 moveto +P$1k +99262 16577 moveto +P$1l +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +99843 16921 moveto +P$1m +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +100296 16916 moveto +P$1s +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +100373 16216 moveto +P$1y +100373 16921 moveto +P$1z +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +100693 16410 moveto +P$2d +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +100873 16216 moveto +P$1y +100873 16921 moveto +P$1z +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +101045 16921 moveto +P$2f +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +101673 16216 moveto +P$1y +101673 16921 moveto +P$1z +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +101948 16650 moveto +P$1k +102262 16577 moveto +P$1l +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +102476 16921 moveto +P$1t +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +102968 16339 moveto +P$23 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +103843 16921 moveto +P$1m +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +104293 17149 moveto +P$2s +104442 16630 moveto +P$2t +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +105175 17149 moveto +P$20 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +105401 16921 moveto +P$21 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +106012 16921 moveto +P$22 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +106912 16921 moveto +P$22 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +107621 16932 moveto +P$1g +107643 16858 moveto +P$1h +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +108443 16921 moveto +P$1m +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +108768 16339 moveto +P$23 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +109348 16650 moveto +P$1k +109662 16577 moveto +P$1l +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +109873 16921 moveto +P$1u +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +110050 17080 moveto +P$24 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +97282 18376 moveto +P$1w +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +97448 18315 moveto +P$1k +97762 18242 moveto +P$1l +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +97976 18586 moveto +P$1t +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +98629 18586 moveto +P$2u +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +98973 17881 moveto +P$1y +98973 18586 moveto +P$1z +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +99248 18292 moveto +P$1i +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +99848 18315 moveto +P$1k +100162 18242 moveto +P$1l +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +100348 18336 moveto +P$1v +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +101382 18376 moveto +P$1w +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +101548 18292 moveto +P$1i +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +102170 18104 moveto +P$1x +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +102673 17881 moveto +P$1y +102673 18586 moveto +P$1z +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +102948 18292 moveto +P$1i +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +103570 18104 moveto +P$1x +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +104296 18581 moveto +P$1s +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +105012 18586 moveto +P$22 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +105573 17881 moveto +P$1y +105573 18586 moveto +P$1z +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +105996 18581 moveto +P$1s +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +107042 18200 moveto +P$29 +106939 18200 moveto +P$2a +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +107321 18596 moveto +P$1g +107343 18523 moveto +P$1h +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +107996 18581 moveto +P$1s +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +108148 18315 moveto +P$1k +108462 18242 moveto +P$1l +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +109043 18586 moveto +P$1m +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +109245 18586 moveto +P$2f +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +109968 18004 moveto +P$23 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +110693 18814 moveto +P$2s +110842 18295 moveto +P$2t +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +111076 18586 moveto +P$1t +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +111473 17881 moveto +P$1y +111473 18586 moveto +P$1z +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +111793 18075 moveto +P$2d +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +112093 18075 moveto +P$2d +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +112775 18814 moveto +P$20 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +113145 18596 moveto +P$2r +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +113668 18004 moveto +P$23 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +114173 18586 moveto +P$1u +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +114373 17881 moveto +P$1y +114373 18586 moveto +P$1z +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +114668 18004 moveto +P$23 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +115610 18425 moveto +P$2e +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +115750 18745 moveto +P$24 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +112592 20261 moveto +P$2g +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +113296 20246 moveto +P$1s +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +113373 19546 moveto +P$1y +113373 20250 moveto +P$1z +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +113573 20250 moveto +P$1u +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +113748 20000 moveto +P$1v +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +114775 19721 moveto +P$27 +114671 19722 moveto +P$28 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +115021 20261 moveto +P$1g +115043 20188 moveto +P$1h +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +115839 20250 moveto +P$1j +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +116148 19980 moveto +P$1k +116462 19907 moveto +P$1l +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +116896 20246 moveto +P$1s +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +117268 19964 moveto +P$2h +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +118025 19566 moveto +P$1p +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +118473 20250 moveto +P$1u +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +118748 19980 moveto +P$1k +119062 19907 moveto +P$1l +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +119421 20261 moveto +P$1g +119443 20188 moveto +P$1h +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +120243 20250 moveto +P$1m +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +120825 19566 moveto +P$1p +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +121276 20250 moveto +P$1t +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +122165 19960 moveto +P$1q +122064 19960 moveto +P$1r +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +122643 20250 moveto +P$1m +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +123045 20261 moveto +P$2r +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +123965 19960 moveto +P$1q +123864 19960 moveto +P$1r +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +124565 19957 moveto +P$2b +124464 19960 moveto +P$2c +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +124898 19966 moveto +P$2k +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +125575 20479 moveto +P$20 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +126442 19864 moveto +P$29 +126339 19864 moveto +P$2a +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +126721 20261 moveto +P$1g +126743 20188 moveto +P$1h +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +127543 20250 moveto +P$1m +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +127773 19546 moveto +P$1y +127773 20250 moveto +P$1z +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +128048 19980 moveto +P$1k +128362 19907 moveto +P$1l +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +128573 20250 moveto +P$1u +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +128750 20410 moveto +P$24 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +113342 21529 moveto +P$29 +113239 21529 moveto +P$2a +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +113965 21625 moveto +P$1q +113864 21625 moveto +P$1r +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +114148 21622 moveto +P$1i +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +115039 21915 moveto +P$1j +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +115348 21645 moveto +P$1k +115662 21572 moveto +P$1l +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +115876 21915 moveto +P$1t +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +116775 22143 moveto +P$20 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +117642 21529 moveto +P$29 +117539 21529 moveto +P$2a +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +117921 21926 moveto +P$1g +117943 21853 moveto +P$1h +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +118743 21915 moveto +P$1m +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +118973 21211 moveto +P$1y +118973 21915 moveto +P$1z +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +119248 21645 moveto +P$1k +119562 21572 moveto +P$1l +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +119773 21915 moveto +P$1u +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +119950 22075 moveto +P$24 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +128290 23580 moveto +P$2v +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +129634 23580 moveto +P$25 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +130021 23591 moveto +P$1g +130043 23518 moveto +P$1h +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +130473 22876 moveto +P$1y +130473 23580 moveto +P$1z +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +130673 23580 moveto +P$1u +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +131482 23371 moveto +P$1w +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +131648 23310 moveto +P$1k +131962 23236 moveto +P$1l +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +132176 23580 moveto +P$1t +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +132829 23580 moveto +P$2u +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +133173 22876 moveto +P$1y +133173 23580 moveto +P$1z +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +133448 23286 moveto +P$1i +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +134048 23310 moveto +P$1k +134362 23236 moveto +P$1l +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +135075 23808 moveto +P$20 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +135254 23199 moveto +P$2w +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +136248 23310 moveto +P$1k +136562 23236 moveto +P$1l +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +136776 23580 moveto +P$1t +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +137665 23290 moveto +P$1q +137564 23290 moveto +P$1r +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +137750 23740 moveto +P$24 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +1192 25372 moveto +P$2x +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +1776 26045 moveto +P$1t +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +2665 25754 moveto +P$1q +2564 25754 moveto +P$1r +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +3143 26045 moveto +P$1m +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +3596 26040 moveto +P$1s +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +3748 25775 moveto +P$1k +4062 25701 moveto +P$1l +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +4643 26045 moveto +P$1m +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +5240 25951 moveto +P$1n +4948 25754 moveto +P$1o +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +1193 28510 moveto +P$2y +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +1865 28219 moveto +P$1q +1764 28219 moveto +P$1r +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +2312 28510 moveto +P$22 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +3365 28216 moveto +P$2z +3264 28219 moveto +P$30 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +3965 28219 moveto +P$1q +3864 28219 moveto +P$1r +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +4443 28510 moveto +P$1m +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +4748 28240 moveto +P$1k +5062 28166 moveto +P$1l +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +5643 28510 moveto +P$1m +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +6096 28505 moveto +P$1s +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +6248 28240 moveto +P$1k +6562 28166 moveto +P$1l +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +7143 28510 moveto +P$1m +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +7875 28738 moveto +P$20 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +8054 28129 moveto +P$2w +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +9048 28240 moveto +P$1k +9362 28166 moveto +P$1l +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +9576 28510 moveto +P$1t +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +10465 28219 moveto +P$1q +10364 28219 moveto +P$1r +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +10550 28669 moveto +P$2l +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +11475 28738 moveto +P$20 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +11845 28521 moveto +P$2r +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +12368 27929 moveto +P$23 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +12873 28510 moveto +P$1u +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +13073 27805 moveto +P$1y +13073 28510 moveto +P$1z +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +13368 27929 moveto +P$23 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +14310 28349 moveto +P$2e +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +14450 28669 moveto +P$24 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +17334 30175 moveto +P$25 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +17648 29905 moveto +P$1k +17962 29831 moveto +P$1l +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +18270 29694 moveto +P$1x +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +18776 30175 moveto +P$1t +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +19610 30014 moveto +P$2e +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +20265 29881 moveto +P$2z +20164 29884 moveto +P$30 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +20376 30175 moveto +P$1t +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +20921 30186 moveto +P$1g +20943 30112 moveto +P$1h +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +21448 29881 moveto +P$1i +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +22070 29694 moveto +P$1x +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +22573 29470 moveto +P$1y +22573 30175 moveto +P$1z +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +22993 30403 moveto +P$2s +23142 29884 moveto +P$2t +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +23739 30175 moveto +P$1j +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +24048 29905 moveto +P$1k +24362 29831 moveto +P$1l +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +24573 29470 moveto +P$1y +24573 30175 moveto +P$1z +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +24996 30170 moveto +P$1s +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +25575 30403 moveto +P$20 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +25790 30175 moveto +P$2m +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +26468 29594 moveto +P$23 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +27339 30175 moveto +P$1j +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +27721 30186 moveto +P$1g +27743 30112 moveto +P$1h +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +28610 30014 moveto +P$2e +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +28750 30334 moveto +P$24 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +64825 31840 moveto +P$2i +64810 31301 moveto +P$2j +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +65565 31549 moveto +P$1q +65464 31549 moveto +P$1r +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +65768 31259 moveto +P$23 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +66496 31835 moveto +P$1s +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +66648 31569 moveto +P$1k +66962 31496 moveto +P$1l +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +67176 31840 moveto +P$1t +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +68075 32068 moveto +P$20 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +68254 31459 moveto +P$2w +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +69248 31569 moveto +P$1k +69562 31496 moveto +P$1l +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +69776 31840 moveto +P$1t +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +70665 31549 moveto +P$1q +70564 31549 moveto +P$1r +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +70750 31999 moveto +P$24 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +65226 33505 moveto +P$2n +64967 32825 moveto +P$2o +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +65975 32975 moveto +P$27 +65871 32977 moveto +P$28 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +66101 33505 moveto +P$21 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +66348 33255 moveto +P$1v +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +67326 33505 moveto +P$2n +67067 32825 moveto +P$2o +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +67843 33505 moveto +P$1m +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +68565 33211 moveto +P$2b +68464 33214 moveto +P$2c +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +68673 32800 moveto +P$1y +68673 33505 moveto +P$1z +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +69243 33505 moveto +P$1m +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +69840 33411 moveto +P$1n +69548 33214 moveto +P$26 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +70168 32923 moveto +P$23 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +71043 33505 moveto +P$1m +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +71493 33733 moveto +P$2s +71642 33214 moveto +P$2t +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +72375 33733 moveto +P$20 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +72554 33123 moveto +P$2w +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +73548 33234 moveto +P$1k +73862 33161 moveto +P$1l +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +74076 33505 moveto +P$1t +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +74965 33214 moveto +P$1q +74864 33214 moveto +P$1r +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +75050 33664 moveto +P$2l +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +75975 33733 moveto +P$20 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +76190 33505 moveto +P$2m +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +76868 32923 moveto +P$23 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +77739 33505 moveto +P$1j +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +78121 33516 moveto +P$1g +78143 33442 moveto +P$1h +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +79010 33344 moveto +P$2e +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +79150 33664 moveto +P$24 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +48690 35170 moveto +P$2v +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +49376 35170 moveto +P$1t +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +49776 35170 moveto +P$1t +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +50665 34879 moveto +P$1q +50564 34879 moveto +P$1r +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +50776 35170 moveto +P$1t +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +51148 34920 moveto +P$1v +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +52101 35170 moveto +P$31 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +52521 35181 moveto +P$1g +52543 35107 moveto +P$1h +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +53343 35170 moveto +P$1m +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +53940 35076 moveto +P$1n +53648 34879 moveto +P$26 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +54173 35170 moveto +P$1u +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +54373 34465 moveto +P$1y +54373 35170 moveto +P$1z +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +54943 35170 moveto +P$1m +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +55393 35398 moveto +P$2s +55542 34879 moveto +P$2t +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +56275 35398 moveto +P$20 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +56454 34788 moveto +P$2w +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +57448 34899 moveto +P$1k +57762 34826 moveto +P$1l +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +57976 35170 moveto +P$1t +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +58865 34879 moveto +P$1q +58764 34879 moveto +P$1r +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +58950 35329 moveto +P$24 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +278 36765 moveto +P$32 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +606 36268 moveto +P$33 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +1606 36268 moveto +P$33 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +2695 36207 moveto +P$34 +2779 36512 moveto +P$35 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +3206 36268 moveto +P$36 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +3898 36646 moveto +P$37 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +4565 36516 moveto +P$38 +4703 36254 moveto +P$39 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +5232 36773 moveto +P$3a +fill +100 setlinewidth +0 0 0 setrgbcolor +newpath +0 37098 moveto +208000 0 rlineto +stroke +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +3525 38007 moveto +P$3b +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +4165 37746 moveto +P$3c +4084 37746 moveto +P$3d +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +4665 37746 moveto +P$3c +4584 37746 moveto +P$3e +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +11525 38007 moveto +P$3b +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +12165 37746 moveto +P$3c +12084 37746 moveto +P$3d +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +12665 37746 moveto +P$3c +12584 37746 moveto +P$3e +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +19525 38007 moveto +P$3b +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +20165 37746 moveto +P$3c +20084 37746 moveto +P$3d +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +20665 37746 moveto +P$3c +20584 37746 moveto +P$3e +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +27525 38007 moveto +P$3b +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +28165 37746 moveto +P$3c +28084 37746 moveto +P$3d +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +28665 37746 moveto +P$3c +28584 37746 moveto +P$3e +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +35525 38007 moveto +P$3b +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +36165 37746 moveto +P$3c +36084 37746 moveto +P$3d +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +36665 37746 moveto +P$3c +36584 37746 moveto +P$3e +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +43525 38007 moveto +P$3b +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +44165 37746 moveto +P$3c +44084 37746 moveto +P$3d +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +44665 37746 moveto +P$3c +44584 37746 moveto +P$3e +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +51525 38007 moveto +P$3b +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +52165 37746 moveto +P$3c +52084 37746 moveto +P$3d +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +52665 37746 moveto +P$3c +52584 37746 moveto +P$3e +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +59525 38007 moveto +P$3b +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +60165 37746 moveto +P$3c +60084 37746 moveto +P$3d +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +60665 37746 moveto +P$3c +60584 37746 moveto +P$3e +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +67525 38007 moveto +P$3b +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +68165 37746 moveto +P$3c +68084 37746 moveto +P$3d +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +68665 37746 moveto +P$3c +68584 37746 moveto +P$3e +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +75525 38007 moveto +P$3b +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +76165 37746 moveto +P$3c +76084 37746 moveto +P$3d +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +76665 37746 moveto +P$3c +76584 37746 moveto +P$3e +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +83525 38007 moveto +P$3b +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +84165 37746 moveto +P$3c +84084 37746 moveto +P$3d +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +84665 37746 moveto +P$3c +84584 37746 moveto +P$3e +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +91525 38007 moveto +P$3b +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +92165 37746 moveto +P$3c +92084 37746 moveto +P$3d +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +92665 37746 moveto +P$3c +92584 37746 moveto +P$3e +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +99525 38007 moveto +P$3b +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +100165 37746 moveto +P$3c +100084 37746 moveto +P$3d +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +100665 37746 moveto +P$3c +100584 37746 moveto +P$3e +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +107525 38007 moveto +P$3b +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +108165 37746 moveto +P$3c +108084 37746 moveto +P$3d +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +108665 37746 moveto +P$3c +108584 37746 moveto +P$3e +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +115525 38007 moveto +P$3b +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +116165 37746 moveto +P$3c +116084 37746 moveto +P$3d +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +116665 37746 moveto +P$3c +116584 37746 moveto +P$3e +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +123525 38007 moveto +P$3b +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +124165 37746 moveto +P$3c +124084 37746 moveto +P$3d +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +124665 37746 moveto +P$3c +124584 37746 moveto +P$3e +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +131525 38007 moveto +P$3b +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +132165 37746 moveto +P$3c +132084 37746 moveto +P$3d +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +132665 37746 moveto +P$3c +132584 37746 moveto +P$3e +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +139525 38007 moveto +P$3b +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +140165 37746 moveto +P$3c +140084 37746 moveto +P$3d +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +140665 37746 moveto +P$3c +140584 37746 moveto +P$3e +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +147525 38007 moveto +P$3b +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +148165 37746 moveto +P$3c +148084 37746 moveto +P$3d +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +148665 37746 moveto +P$3c +148584 37746 moveto +P$3e +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +155525 38007 moveto +P$3b +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +156165 37746 moveto +P$3c +156084 37746 moveto +P$3d +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +156665 37746 moveto +P$3c +156584 37746 moveto +P$3e +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +163525 38007 moveto +P$3b +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +164165 37746 moveto +P$3c +164084 37746 moveto +P$3d +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +164665 37746 moveto +P$3c +164584 37746 moveto +P$3e +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +171525 38007 moveto +P$3b +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +172165 37746 moveto +P$3c +172084 37746 moveto +P$3d +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +172665 37746 moveto +P$3c +172584 37746 moveto +P$3e +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +179525 38007 moveto +P$3b +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +180165 37746 moveto +P$3c +180084 37746 moveto +P$3d +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +180665 37746 moveto +P$3c +180584 37746 moveto +P$3e +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +187525 38007 moveto +P$3b +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +188165 37746 moveto +P$3c +188084 37746 moveto +P$3d +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +188665 37746 moveto +P$3c +188584 37746 moveto +P$3e +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +195525 38007 moveto +P$3b +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +196165 37746 moveto +P$3c +196084 37746 moveto +P$3d +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +196665 37746 moveto +P$3c +196584 37746 moveto +P$3e +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +754 39585 moveto +P$3f +423 39165 moveto +P$3g +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +1195 39407 moveto +P$34 +1279 39712 moveto +P$35 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +1706 39468 moveto +P$36 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +2440 39224 moveto +P$3h +2434 39973 moveto +P$3i +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +2765 39716 moveto +P$38 +2903 39454 moveto +P$39 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +3432 39973 moveto +P$3a +fill +100 setlinewidth +0 0 0 setrgbcolor +newpath +0 40298 moveto +208000 0 rlineto +stroke +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +3525 41207 moveto +P$3b +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +4165 40946 moveto +P$3c +4084 40946 moveto +P$3d +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +4665 40946 moveto +P$3c +4584 40946 moveto +P$3e +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +11525 41207 moveto +P$3b +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +12165 40946 moveto +P$3c +12084 40946 moveto +P$3d +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +12665 40946 moveto +P$3c +12584 40946 moveto +P$3e +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +19525 41207 moveto +P$3b +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +20165 40946 moveto +P$3c +20084 40946 moveto +P$3d +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +20665 40946 moveto +P$3c +20584 40946 moveto +P$3e +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +75525 41207 moveto +P$3b +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +76165 40946 moveto +P$3c +76084 40946 moveto +P$3d +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +76665 40946 moveto +P$3c +76584 40946 moveto +P$3e +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +83525 41207 moveto +P$3b +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +84165 40946 moveto +P$3c +84084 40946 moveto +P$3d +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +84665 40946 moveto +P$3c +84584 40946 moveto +P$3e +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +91525 41207 moveto +P$3b +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +92165 40946 moveto +P$3c +92084 40946 moveto +P$3d +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +92665 40946 moveto +P$3c +92584 40946 moveto +P$3e +fill +0 setlinewidth +[] 0 setdash +1 0 0 setrgbcolor +newpath +99650 41243 moveto +P$3j +fill +0 setlinewidth +[] 0 setdash +1 0 0 setrgbcolor +newpath +100165 40946 moveto +P$3c +100084 40946 moveto +P$3d +fill +0 setlinewidth +[] 0 setdash +1 0 0 setrgbcolor +newpath +100665 40946 moveto +P$3c +100584 40946 moveto +P$3e +fill +0 setlinewidth +[] 0 setdash +1 0 0 setrgbcolor +newpath +107650 41243 moveto +P$3j +fill +0 setlinewidth +[] 0 setdash +1 0 0 setrgbcolor +newpath +108165 40946 moveto +P$3c +108084 40946 moveto +P$3d +fill +0 setlinewidth +[] 0 setdash +1 0 0 setrgbcolor +newpath +108665 40946 moveto +P$3c +108584 40946 moveto +P$3e +fill +0 setlinewidth +[] 0 setdash +1 0 0 setrgbcolor +newpath +115650 41243 moveto +P$3j +fill +0 setlinewidth +[] 0 setdash +1 0 0 setrgbcolor +newpath +116165 40946 moveto +P$3c +116084 40946 moveto +P$3d +fill +0 setlinewidth +[] 0 setdash +1 0 0 setrgbcolor +newpath +116665 40946 moveto +P$3c +116584 40946 moveto +P$3e +fill +0 setlinewidth +[] 0 setdash +1 0 0 setrgbcolor +newpath +123650 41243 moveto +P$3j +fill +0 setlinewidth +[] 0 setdash +1 0 0 setrgbcolor +newpath +124165 40946 moveto +P$3c +124084 40946 moveto +P$3d +fill +0 setlinewidth +[] 0 setdash +1 0 0 setrgbcolor +newpath +124665 40946 moveto +P$3c +124584 40946 moveto +P$3e +fill +0 setlinewidth +[] 0 setdash +1 0 0 setrgbcolor +newpath +131650 41243 moveto +P$3j +fill +0 setlinewidth +[] 0 setdash +1 0 0 setrgbcolor +newpath +132165 40946 moveto +P$3c +132084 40946 moveto +P$3d +fill +0 setlinewidth +[] 0 setdash +1 0 0 setrgbcolor +newpath +132665 40946 moveto +P$3c +132584 40946 moveto +P$3e +fill +0 setlinewidth +[] 0 setdash +1 0 0 setrgbcolor +newpath +139650 41243 moveto +P$3j +fill +0 setlinewidth +[] 0 setdash +1 0 0 setrgbcolor +newpath +140165 40946 moveto +P$3c +140084 40946 moveto +P$3d +fill +0 setlinewidth +[] 0 setdash +1 0 0 setrgbcolor +newpath +140665 40946 moveto +P$3c +140584 40946 moveto +P$3e +fill +0 setlinewidth +[] 0 setdash +1 0 0 setrgbcolor +newpath +147650 41243 moveto +P$3j +fill +0 setlinewidth +[] 0 setdash +1 0 0 setrgbcolor +newpath +148165 40946 moveto +P$3c +148084 40946 moveto +P$3d +fill +0 setlinewidth +[] 0 setdash +1 0 0 setrgbcolor +newpath +148665 40946 moveto +P$3c +148584 40946 moveto +P$3e +fill +0 setlinewidth +[] 0 setdash +1 0 0 setrgbcolor +newpath +155650 41243 moveto +P$3j +fill +0 setlinewidth +[] 0 setdash +1 0 0 setrgbcolor +newpath +156165 40946 moveto +P$3c +156084 40946 moveto +P$3d +fill +0 setlinewidth +[] 0 setdash +1 0 0 setrgbcolor +newpath +156665 40946 moveto +P$3c +156584 40946 moveto +P$3e +fill +0 setlinewidth +[] 0 setdash +1 0 0 setrgbcolor +newpath +163650 41243 moveto +P$3j +fill +0 setlinewidth +[] 0 setdash +1 0 0 setrgbcolor +newpath +164165 40946 moveto +P$3c +164084 40946 moveto +P$3d +fill +0 setlinewidth +[] 0 setdash +1 0 0 setrgbcolor +newpath +164665 40946 moveto +P$3c +164584 40946 moveto +P$3e +fill +0 setlinewidth +[] 0 setdash +1 0 0 setrgbcolor +newpath +171650 41243 moveto +P$3j +fill +0 setlinewidth +[] 0 setdash +1 0 0 setrgbcolor +newpath +172165 40946 moveto +P$3c +172084 40946 moveto +P$3d +fill +0 setlinewidth +[] 0 setdash +1 0 0 setrgbcolor +newpath +172665 40946 moveto +P$3c +172584 40946 moveto +P$3e +fill +0 setlinewidth +[] 0 setdash +1 0 0 setrgbcolor +newpath +179650 41243 moveto +P$3j +fill +0 setlinewidth +[] 0 setdash +1 0 0 setrgbcolor +newpath +180165 40946 moveto +P$3c +180084 40946 moveto +P$3d +fill +0 setlinewidth +[] 0 setdash +1 0 0 setrgbcolor +newpath +180665 40946 moveto +P$3c +180584 40946 moveto +P$3e +fill +0 setlinewidth +[] 0 setdash +1 0 0 setrgbcolor +newpath +187650 41243 moveto +P$3j +fill +0 setlinewidth +[] 0 setdash +1 0 0 setrgbcolor +newpath +188165 40946 moveto +P$3c +188084 40946 moveto +P$3d +fill +0 setlinewidth +[] 0 setdash +1 0 0 setrgbcolor +newpath +188665 40946 moveto +P$3c +188584 40946 moveto +P$3e +fill +0 setlinewidth +[] 0 setdash +1 0 0 setrgbcolor +newpath +195650 41243 moveto +P$3j +fill +0 setlinewidth +[] 0 setdash +1 0 0 setrgbcolor +newpath +196165 40946 moveto +P$3c +196084 40946 moveto +P$3d +fill +0 setlinewidth +[] 0 setdash +1 0 0 setrgbcolor +newpath +196665 40946 moveto +P$3c +196584 40946 moveto +P$3e +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +203525 41207 moveto +P$3b +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +204165 40946 moveto +P$3c +204084 40946 moveto +P$3d +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +204665 40946 moveto +P$3c +204584 40946 moveto +P$3e +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +400 42399 moveto +P$3k +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +998 43046 moveto +P$37 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +1718 42929 moveto +P$3l +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +2495 42607 moveto +P$34 +2579 42912 moveto +P$35 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +3259 43049 moveto +P$3m +fill +100 setlinewidth +0 0 0 setrgbcolor +newpath +0 43498 moveto +208000 0 rlineto +stroke +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +19525 44407 moveto +P$3b +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +20165 44146 moveto +P$3c +20084 44146 moveto +P$3d +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +20665 44146 moveto +P$3c +20584 44146 moveto +P$3e +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +27525 44407 moveto +P$3b +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +28165 44146 moveto +P$3c +28084 44146 moveto +P$3d +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +28665 44146 moveto +P$3c +28584 44146 moveto +P$3e +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +35525 44407 moveto +P$3b +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +36165 44146 moveto +P$3c +36084 44146 moveto +P$3d +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +36665 44146 moveto +P$3c +36584 44146 moveto +P$3e +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +43525 44407 moveto +P$3b +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +44165 44146 moveto +P$3c +44084 44146 moveto +P$3d +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +44665 44146 moveto +P$3c +44584 44146 moveto +P$3e +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +51525 44407 moveto +P$3b +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +52165 44146 moveto +P$3c +52084 44146 moveto +P$3d +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +52665 44146 moveto +P$3c +52584 44146 moveto +P$3e +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +67525 44407 moveto +P$3b +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +68165 44146 moveto +P$3c +68084 44146 moveto +P$3d +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +68665 44146 moveto +P$3c +68584 44146 moveto +P$3e +fill +0 setlinewidth +[] 0 setdash +1 0 0 setrgbcolor +newpath +75650 44443 moveto +P$3j +fill +0 setlinewidth +[] 0 setdash +1 0 0 setrgbcolor +newpath +76165 44146 moveto +P$3c +76084 44146 moveto +P$3d +fill +0 setlinewidth +[] 0 setdash +1 0 0 setrgbcolor +newpath +76665 44146 moveto +P$3c +76584 44146 moveto +P$3e +fill +0 setlinewidth +[] 0 setdash +1 0 0 setrgbcolor +newpath +83650 44443 moveto +P$3j +fill +0 setlinewidth +[] 0 setdash +1 0 0 setrgbcolor +newpath +84165 44146 moveto +P$3c +84084 44146 moveto +P$3d +fill +0 setlinewidth +[] 0 setdash +1 0 0 setrgbcolor +newpath +84665 44146 moveto +P$3c +84584 44146 moveto +P$3e +fill +0 setlinewidth +[] 0 setdash +1 0 0 setrgbcolor +newpath +91650 44443 moveto +P$3j +fill +0 setlinewidth +[] 0 setdash +1 0 0 setrgbcolor +newpath +92165 44146 moveto +P$3c +92084 44146 moveto +P$3d +fill +0 setlinewidth +[] 0 setdash +1 0 0 setrgbcolor +newpath +92665 44146 moveto +P$3c +92584 44146 moveto +P$3e +fill +0 setlinewidth +[] 0 setdash +1 0 0 setrgbcolor +newpath +99650 44443 moveto +P$3j +fill +0 setlinewidth +[] 0 setdash +1 0 0 setrgbcolor +newpath +100165 44146 moveto +P$3c +100084 44146 moveto +P$3d +fill +0 setlinewidth +[] 0 setdash +1 0 0 setrgbcolor +newpath +100665 44146 moveto +P$3c +100584 44146 moveto +P$3e +fill +0 setlinewidth +[] 0 setdash +1 0 0 setrgbcolor +newpath +107650 44443 moveto +P$3j +fill +0 setlinewidth +[] 0 setdash +1 0 0 setrgbcolor +newpath +108165 44146 moveto +P$3c +108084 44146 moveto +P$3d +fill +0 setlinewidth +[] 0 setdash +1 0 0 setrgbcolor +newpath +108665 44146 moveto +P$3c +108584 44146 moveto +P$3e +fill +0 setlinewidth +[] 0 setdash +1 0 0 setrgbcolor +newpath +115650 44443 moveto +P$3j +fill +0 setlinewidth +[] 0 setdash +1 0 0 setrgbcolor +newpath +116165 44146 moveto +P$3c +116084 44146 moveto +P$3d +fill +0 setlinewidth +[] 0 setdash +1 0 0 setrgbcolor +newpath +116665 44146 moveto +P$3c +116584 44146 moveto +P$3e +fill +0 setlinewidth +[] 0 setdash +1 0 0 setrgbcolor +newpath +123650 44443 moveto +P$3j +fill +0 setlinewidth +[] 0 setdash +1 0 0 setrgbcolor +newpath +124165 44146 moveto +P$3c +124084 44146 moveto +P$3d +fill +0 setlinewidth +[] 0 setdash +1 0 0 setrgbcolor +newpath +124665 44146 moveto +P$3c +124584 44146 moveto +P$3e +fill +0 setlinewidth +[] 0 setdash +1 0 0 setrgbcolor +newpath +131650 44443 moveto +P$3j +fill +0 setlinewidth +[] 0 setdash +1 0 0 setrgbcolor +newpath +132165 44146 moveto +P$3c +132084 44146 moveto +P$3d +fill +0 setlinewidth +[] 0 setdash +1 0 0 setrgbcolor +newpath +132665 44146 moveto +P$3c +132584 44146 moveto +P$3e +fill +0 setlinewidth +[] 0 setdash +1 0 0 setrgbcolor +newpath +139650 44443 moveto +P$3j +fill +0 setlinewidth +[] 0 setdash +1 0 0 setrgbcolor +newpath +140165 44146 moveto +P$3c +140084 44146 moveto +P$3d +fill +0 setlinewidth +[] 0 setdash +1 0 0 setrgbcolor +newpath +140665 44146 moveto +P$3c +140584 44146 moveto +P$3e +fill +0 setlinewidth +[] 0 setdash +1 0 0 setrgbcolor +newpath +147650 44443 moveto +P$3j +fill +0 setlinewidth +[] 0 setdash +1 0 0 setrgbcolor +newpath +148165 44146 moveto +P$3c +148084 44146 moveto +P$3d +fill +0 setlinewidth +[] 0 setdash +1 0 0 setrgbcolor +newpath +148665 44146 moveto +P$3c +148584 44146 moveto +P$3e +fill +0 setlinewidth +[] 0 setdash +1 0 0 setrgbcolor +newpath +155650 44443 moveto +P$3j +fill +0 setlinewidth +[] 0 setdash +1 0 0 setrgbcolor +newpath +156165 44146 moveto +P$3c +156084 44146 moveto +P$3d +fill +0 setlinewidth +[] 0 setdash +1 0 0 setrgbcolor +newpath +156665 44146 moveto +P$3c +156584 44146 moveto +P$3e +fill +0 setlinewidth +[] 0 setdash +1 0 0 setrgbcolor +newpath +163650 44443 moveto +P$3j +fill +0 setlinewidth +[] 0 setdash +1 0 0 setrgbcolor +newpath +164165 44146 moveto +P$3c +164084 44146 moveto +P$3d +fill +0 setlinewidth +[] 0 setdash +1 0 0 setrgbcolor +newpath +164665 44146 moveto +P$3c +164584 44146 moveto +P$3e +fill +0 setlinewidth +[] 0 setdash +1 0 0 setrgbcolor +newpath +171650 44443 moveto +P$3j +fill +0 setlinewidth +[] 0 setdash +1 0 0 setrgbcolor +newpath +172165 44146 moveto +P$3c +172084 44146 moveto +P$3d +fill +0 setlinewidth +[] 0 setdash +1 0 0 setrgbcolor +newpath +172665 44146 moveto +P$3c +172584 44146 moveto +P$3e +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +179525 44407 moveto +P$3b +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +180165 44146 moveto +P$3c +180084 44146 moveto +P$3d +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +180665 44146 moveto +P$3c +180584 44146 moveto +P$3e +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +187525 44407 moveto +P$3b +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +188165 44146 moveto +P$3c +188084 44146 moveto +P$3d +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +188665 44146 moveto +P$3c +188584 44146 moveto +P$3e +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +195525 44407 moveto +P$3b +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +196165 44146 moveto +P$3c +196084 44146 moveto +P$3d +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +196665 44146 moveto +P$3c +196584 44146 moveto +P$3e +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +260 45615 moveto +P$3n +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +698 46246 moveto +P$37 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +1432 46373 moveto +P$3o +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +1840 45624 moveto +P$3h +1834 46373 moveto +P$3i +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +2198 46246 moveto +P$37 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +3159 46249 moveto +P$3m +fill +100 setlinewidth +0 0 0 setrgbcolor +newpath +0 46698 moveto +208000 0 rlineto +stroke +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +3525 47607 moveto +P$3b +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +4165 47346 moveto +P$3c +4084 47346 moveto +P$3d +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +4665 47346 moveto +P$3c +4584 47346 moveto +P$3e +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +11525 47607 moveto +P$3b +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +12165 47346 moveto +P$3c +12084 47346 moveto +P$3d +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +12665 47346 moveto +P$3c +12584 47346 moveto +P$3e +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +19525 47607 moveto +P$3b +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +20165 47346 moveto +P$3c +20084 47346 moveto +P$3d +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +20665 47346 moveto +P$3c +20584 47346 moveto +P$3e +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +27525 47607 moveto +P$3b +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +28165 47346 moveto +P$3c +28084 47346 moveto +P$3d +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +28665 47346 moveto +P$3c +28584 47346 moveto +P$3e +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +35525 47607 moveto +P$3b +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +36165 47346 moveto +P$3c +36084 47346 moveto +P$3d +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +36665 47346 moveto +P$3c +36584 47346 moveto +P$3e +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +43525 47607 moveto +P$3b +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +44165 47346 moveto +P$3c +44084 47346 moveto +P$3d +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +44665 47346 moveto +P$3c +44584 47346 moveto +P$3e +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +51525 47607 moveto +P$3b +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +52165 47346 moveto +P$3c +52084 47346 moveto +P$3d +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +52665 47346 moveto +P$3c +52584 47346 moveto +P$3e +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +59525 47607 moveto +P$3b +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +60165 47346 moveto +P$3c +60084 47346 moveto +P$3d +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +60665 47346 moveto +P$3c +60584 47346 moveto +P$3e +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +75525 47607 moveto +P$3b +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +76165 47346 moveto +P$3c +76084 47346 moveto +P$3d +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +76665 47346 moveto +P$3c +76584 47346 moveto +P$3e +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +83525 47607 moveto +P$3b +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +84165 47346 moveto +P$3c +84084 47346 moveto +P$3d +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +84665 47346 moveto +P$3c +84584 47346 moveto +P$3e +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +91525 47607 moveto +P$3b +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +92165 47346 moveto +P$3c +92084 47346 moveto +P$3d +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +92665 47346 moveto +P$3c +92584 47346 moveto +P$3e +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +99525 47607 moveto +P$3b +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +100165 47346 moveto +P$3c +100084 47346 moveto +P$3d +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +100665 47346 moveto +P$3c +100584 47346 moveto +P$3e +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +107525 47607 moveto +P$3b +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +108165 47346 moveto +P$3c +108084 47346 moveto +P$3d +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +108665 47346 moveto +P$3c +108584 47346 moveto +P$3e +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +115525 47607 moveto +P$3b +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +116165 47346 moveto +P$3c +116084 47346 moveto +P$3d +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +116665 47346 moveto +P$3c +116584 47346 moveto +P$3e +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +123525 47607 moveto +P$3b +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +124165 47346 moveto +P$3c +124084 47346 moveto +P$3d +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +124665 47346 moveto +P$3c +124584 47346 moveto +P$3e +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +131525 47607 moveto +P$3b +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +132165 47346 moveto +P$3c +132084 47346 moveto +P$3d +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +132665 47346 moveto +P$3c +132584 47346 moveto +P$3e +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +139525 47607 moveto +P$3b +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +140165 47346 moveto +P$3c +140084 47346 moveto +P$3d +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +140665 47346 moveto +P$3c +140584 47346 moveto +P$3e +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +147525 47607 moveto +P$3b +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +148165 47346 moveto +P$3c +148084 47346 moveto +P$3d +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +148665 47346 moveto +P$3c +148584 47346 moveto +P$3e +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +155525 47607 moveto +P$3b +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +156165 47346 moveto +P$3c +156084 47346 moveto +P$3d +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +156665 47346 moveto +P$3c +156584 47346 moveto +P$3e +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +163525 47607 moveto +P$3b +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +164165 47346 moveto +P$3c +164084 47346 moveto +P$3d +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +164665 47346 moveto +P$3c +164584 47346 moveto +P$3e +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +171525 47607 moveto +P$3b +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +172165 47346 moveto +P$3c +172084 47346 moveto +P$3d +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +172665 47346 moveto +P$3c +172584 47346 moveto +P$3e +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +179525 47607 moveto +P$3b +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +180165 47346 moveto +P$3c +180084 47346 moveto +P$3d +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +180665 47346 moveto +P$3c +180584 47346 moveto +P$3e +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +187525 47607 moveto +P$3b +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +188165 47346 moveto +P$3c +188084 47346 moveto +P$3d +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +188665 47346 moveto +P$3c +188584 47346 moveto +P$3e +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +195525 47607 moveto +P$3b +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +196165 47346 moveto +P$3c +196084 47346 moveto +P$3d +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +196665 47346 moveto +P$3c +196584 47346 moveto +P$3e +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +203525 47607 moveto +P$3b +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +204165 47346 moveto +P$3c +204084 47346 moveto +P$3d +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +204665 47346 moveto +P$3c +204584 47346 moveto +P$3e +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +815 49573 moveto +P$3p +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +1065 49316 moveto +P$38 +1203 49054 moveto +P$39 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +1921 49004 moveto +P$3q +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +2500 49315 moveto +P$3r +2385 49315 moveto +P$3s +fill +100 setlinewidth +0 0 0 setrgbcolor +newpath +0 49898 moveto +208000 0 rlineto +stroke +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +3525 50807 moveto +P$3b +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +4165 50546 moveto +P$3c +4084 50546 moveto +P$3d +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +4665 50546 moveto +P$3c +4584 50546 moveto +P$3e +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +11525 50807 moveto +P$3b +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +12165 50546 moveto +P$3c +12084 50546 moveto +P$3d +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +12665 50546 moveto +P$3c +12584 50546 moveto +P$3e +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +19525 50807 moveto +P$3b +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +20165 50546 moveto +P$3c +20084 50546 moveto +P$3d +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +20665 50546 moveto +P$3c +20584 50546 moveto +P$3e +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +27525 50807 moveto +P$3b +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +28165 50546 moveto +P$3c +28084 50546 moveto +P$3d +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +28665 50546 moveto +P$3c +28584 50546 moveto +P$3e +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +35525 50807 moveto +P$3b +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +36165 50546 moveto +P$3c +36084 50546 moveto +P$3d +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +36665 50546 moveto +P$3c +36584 50546 moveto +P$3e +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +43525 50807 moveto +P$3b +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +44165 50546 moveto +P$3c +44084 50546 moveto +P$3d +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +44665 50546 moveto +P$3c +44584 50546 moveto +P$3e +fill +0 setlinewidth +[] 0 setdash +1 0 0 setrgbcolor +newpath +51650 50843 moveto +P$3j +fill +0 setlinewidth +[] 0 setdash +1 0 0 setrgbcolor +newpath +52165 50546 moveto +P$3c +52084 50546 moveto +P$3d +fill +0 setlinewidth +[] 0 setdash +1 0 0 setrgbcolor +newpath +52665 50546 moveto +P$3c +52584 50546 moveto +P$3e +fill +0 setlinewidth +[] 0 setdash +1 0 0 setrgbcolor +newpath +59665 50682 moveto +P$3t +fill +0 setlinewidth +[] 0 setdash +1 0 0 setrgbcolor +newpath +60165 50546 moveto +P$3c +60084 50546 moveto +P$3d +fill +0 setlinewidth +[] 0 setdash +1 0 0 setrgbcolor +newpath +60665 50546 moveto +P$3c +60584 50546 moveto +P$3e +fill +0 setlinewidth +[] 0 setdash +1 0 0 setrgbcolor +newpath +67650 50843 moveto +P$3j +fill +0 setlinewidth +[] 0 setdash +1 0 0 setrgbcolor +newpath +68165 50546 moveto +P$3c +68084 50546 moveto +P$3d +fill +0 setlinewidth +[] 0 setdash +1 0 0 setrgbcolor +newpath +68665 50546 moveto +P$3c +68584 50546 moveto +P$3e +fill +0 setlinewidth +[] 0 setdash +1 0 0 setrgbcolor +newpath +75650 50843 moveto +P$3j +fill +0 setlinewidth +[] 0 setdash +1 0 0 setrgbcolor +newpath +76165 50546 moveto +P$3c +76084 50546 moveto +P$3d +fill +0 setlinewidth +[] 0 setdash +1 0 0 setrgbcolor +newpath +76665 50546 moveto +P$3c +76584 50546 moveto +P$3e +fill +0 setlinewidth +[] 0 setdash +1 0 0 setrgbcolor +newpath +83650 50843 moveto +P$3j +fill +0 setlinewidth +[] 0 setdash +1 0 0 setrgbcolor +newpath +84165 50546 moveto +P$3c +84084 50546 moveto +P$3d +fill +0 setlinewidth +[] 0 setdash +1 0 0 setrgbcolor +newpath +84665 50546 moveto +P$3c +84584 50546 moveto +P$3e +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +91525 50807 moveto +P$3b +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +92165 50546 moveto +P$3c +92084 50546 moveto +P$3d +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +92665 50546 moveto +P$3c +92584 50546 moveto +P$3e +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +99525 50807 moveto +P$3b +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +100165 50546 moveto +P$3c +100084 50546 moveto +P$3d +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +100665 50546 moveto +P$3c +100584 50546 moveto +P$3e +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +107525 50807 moveto +P$3b +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +108165 50546 moveto +P$3c +108084 50546 moveto +P$3d +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +108665 50546 moveto +P$3c +108584 50546 moveto +P$3e +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +115525 50807 moveto +P$3b +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +116165 50546 moveto +P$3c +116084 50546 moveto +P$3d +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +116665 50546 moveto +P$3c +116584 50546 moveto +P$3e +fill +0 setlinewidth +[] 0 setdash +1 0 0 setrgbcolor +newpath +123650 50843 moveto +P$3j +fill +0 setlinewidth +[] 0 setdash +1 0 0 setrgbcolor +newpath +124165 50546 moveto +P$3c +124084 50546 moveto +P$3d +fill +0 setlinewidth +[] 0 setdash +1 0 0 setrgbcolor +newpath +124665 50546 moveto +P$3c +124584 50546 moveto +P$3e +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +131525 50807 moveto +P$3b +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +132165 50546 moveto +P$3c +132084 50546 moveto +P$3d +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +132665 50546 moveto +P$3c +132584 50546 moveto +P$3e +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +139525 50807 moveto +P$3b +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +140165 50546 moveto +P$3c +140084 50546 moveto +P$3d +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +140665 50546 moveto +P$3c +140584 50546 moveto +P$3e +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +147525 50807 moveto +P$3b +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +148165 50546 moveto +P$3c +148084 50546 moveto +P$3d +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +148665 50546 moveto +P$3c +148584 50546 moveto +P$3e +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +155525 50807 moveto +P$3b +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +156165 50546 moveto +P$3c +156084 50546 moveto +P$3d +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +156665 50546 moveto +P$3c +156584 50546 moveto +P$3e +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +163525 50807 moveto +P$3b +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +164165 50546 moveto +P$3c +164084 50546 moveto +P$3d +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +164665 50546 moveto +P$3c +164584 50546 moveto +P$3e +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +171525 50807 moveto +P$3b +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +172165 50546 moveto +P$3c +172084 50546 moveto +P$3d +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +172665 50546 moveto +P$3c +172584 50546 moveto +P$3e +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +3960 53558 moveto +P$e +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +4462 53407 moveto +P$f +4371 53407 moveto +P$g +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +11960 53558 moveto +P$e +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +12306 53697 moveto +P$h +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +20056 53697 moveto +P$h +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +28195 53736 moveto +P$i +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +36210 53558 moveto +P$j +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +44145 53593 moveto +P$k +44060 53193 moveto +P$l +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +51987 53353 moveto +P$m +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +60220 53533 moveto +P$n +60018 53708 moveto +P$o +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +67848 53236 moveto +P$p +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +76192 53241 moveto +P$q +76123 53560 moveto +P$r +76104 53241 moveto +P$s +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +83782 53282 moveto +P$t +83993 53113 moveto +P$u +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +91806 53697 moveto +P$v +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +92462 53407 moveto +P$f +92371 53407 moveto +P$g +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +99806 53697 moveto +P$v +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +100306 53697 moveto +P$h +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +107806 53697 moveto +P$v +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +108445 53736 moveto +P$i +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +115806 53697 moveto +P$v +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +116460 53558 moveto +P$j +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +123806 53697 moveto +P$v +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +124395 53593 moveto +P$k +124310 53193 moveto +P$l +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +131806 53697 moveto +P$v +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +132237 53353 moveto +P$m +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +139806 53697 moveto +P$v +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +140470 53533 moveto +P$n +140268 53708 moveto +P$o +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +147806 53697 moveto +P$v +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +148098 53236 moveto +P$p +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +155806 53697 moveto +P$v +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +156442 53241 moveto +P$q +156373 53560 moveto +P$r +156354 53241 moveto +P$s +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +163806 53697 moveto +P$v +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +164032 53282 moveto +P$t +164243 53113 moveto +P$u +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +171945 53736 moveto +P$w +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +172462 53407 moveto +P$f +172371 53407 moveto +P$g +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +179945 53736 moveto +P$w +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +180306 53697 moveto +P$h +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +187945 53736 moveto +P$w +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +188445 53736 moveto +P$i +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +195945 53736 moveto +P$w +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +196460 53558 moveto +P$j +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +203945 53736 moveto +P$w +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +204395 53593 moveto +P$k +204310 53193 moveto +P$l +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +3720 52536 moveto +P$0 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +4662 52305 moveto +P$1 +4573 52305 moveto +P$2 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +11603 52536 moveto +P$3 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +12203 52405 moveto +P$4 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +20020 52552 moveto +P$5 +20376 52305 moveto +P$6 +20482 52103 moveto +P$7 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +27603 52536 moveto +P$3 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +28209 52041 moveto +P$8 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +35757 52243 moveto +P$9 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +36475 52064 moveto +P$a +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +43567 52360 moveto +P$b +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +44326 52068 moveto +P$c +44392 52302 moveto +P$d +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +51517 52360 moveto +P$b +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +52203 52405 moveto +P$4 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +59720 52536 moveto +P$0 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +60662 52305 moveto +P$1 +60573 52305 moveto +P$2 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +67603 52536 moveto +P$3 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +68203 52405 moveto +P$4 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +76020 52552 moveto +P$5 +76376 52305 moveto +P$6 +76482 52103 moveto +P$7 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +83603 52536 moveto +P$3 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +84209 52041 moveto +P$8 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +91757 52243 moveto +P$9 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +92475 52064 moveto +P$a +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +99567 52360 moveto +P$b +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +100326 52068 moveto +P$c +100392 52302 moveto +P$d +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +107517 52360 moveto +P$b +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +108203 52405 moveto +P$4 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +115720 52536 moveto +P$0 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +116662 52305 moveto +P$1 +116573 52305 moveto +P$2 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +123603 52536 moveto +P$3 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +124203 52405 moveto +P$4 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +132020 52552 moveto +P$5 +132376 52305 moveto +P$6 +132482 52103 moveto +P$7 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +139603 52536 moveto +P$3 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +140209 52041 moveto +P$8 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +147757 52243 moveto +P$9 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +148475 52064 moveto +P$a +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +155567 52360 moveto +P$b +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +156326 52068 moveto +P$c +156392 52302 moveto +P$d +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +163517 52360 moveto +P$b +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +164203 52405 moveto +P$4 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +171720 52536 moveto +P$0 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +172662 52305 moveto +P$1 +172573 52305 moveto +P$2 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +179603 52536 moveto +P$3 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +180203 52405 moveto +P$4 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +188020 52552 moveto +P$5 +188376 52305 moveto +P$6 +188482 52103 moveto +P$7 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +195603 52536 moveto +P$3 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +196209 52041 moveto +P$8 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +203757 52243 moveto +P$9 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +204475 52064 moveto +P$a +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +4793 54395 moveto +P$x +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +5420 54560 moveto +P$y +5457 54846 moveto +P$z +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +5954 54620 moveto +P$10 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +6812 55075 moveto +P$11 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +7420 54560 moveto +P$y +7457 54846 moveto +P$z +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +7970 54687 moveto +P$12 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +8470 55140 moveto +P$13 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +9648 55123 moveto +P$14 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +10254 54726 moveto +P$15 +10082 54726 moveto +P$16 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +10848 55123 moveto +P$14 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +11459 54909 moveto +P$17 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +108556 54787 moveto +P$18 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +109239 54559 moveto +P$19 +109232 54618 moveto +P$1a +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +109896 54832 moveto +P$1b +109521 54342 moveto +P$1c +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +110420 54687 moveto +P$12 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +111062 55075 moveto +P$11 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +111670 54560 moveto +P$y +111707 54846 moveto +P$z +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +112220 54687 moveto +P$12 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +112720 55140 moveto +P$13 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +113898 55123 moveto +P$14 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +114504 54726 moveto +P$15 +114332 54726 moveto +P$16 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +115098 55123 moveto +P$14 +fill +0 setlinewidth +[] 0 setdash +0 0 0 setrgbcolor +newpath +115709 54909 moveto +P$17 +fill +grestore +showpage +%plantuml done +%%EOF diff --git a/20-implementierungsheft/assets/gantt.png b/20-implementierungsheft/assets/gantt.png new file mode 100644 index 0000000..b1b7e17 Binary files /dev/null and b/20-implementierungsheft/assets/gantt.png differ diff --git a/20-implementierungsheft/assets/help.png b/20-implementierungsheft/assets/help.png new file mode 100644 index 0000000..39a1b84 Binary files /dev/null and b/20-implementierungsheft/assets/help.png differ diff --git a/20-implementierungsheft/assets/lastupdate.png b/20-implementierungsheft/assets/lastupdate.png new file mode 100644 index 0000000..e9b7f5c Binary files /dev/null and b/20-implementierungsheft/assets/lastupdate.png differ diff --git a/20-implementierungsheft/assets/loading.png b/20-implementierungsheft/assets/loading.png new file mode 100644 index 0000000..cc95763 Binary files /dev/null and b/20-implementierungsheft/assets/loading.png differ diff --git a/20-implementierungsheft/assets/logo.pdf b/20-implementierungsheft/assets/logo.pdf new file mode 100644 index 0000000..91fd334 Binary files /dev/null and b/20-implementierungsheft/assets/logo.pdf differ diff --git a/20-implementierungsheft/assets/logo.svg b/20-implementierungsheft/assets/logo.svg new file mode 100644 index 0000000..1609066 --- /dev/null +++ b/20-implementierungsheft/assets/logo.svg @@ -0,0 +1,211 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Podcast Synchronisation made Efficient + + + + diff --git a/20-implementierungsheft/assets/navbar.png b/20-implementierungsheft/assets/navbar.png new file mode 100644 index 0000000..dd9f8e8 Binary files /dev/null and b/20-implementierungsheft/assets/navbar.png differ diff --git a/20-implementierungsheft/assets/password-margin.png b/20-implementierungsheft/assets/password-margin.png new file mode 100644 index 0000000..d9d4fa3 Binary files /dev/null and b/20-implementierungsheft/assets/password-margin.png differ diff --git a/20-implementierungsheft/assets/password.png b/20-implementierungsheft/assets/password.png new file mode 100644 index 0000000..68248a0 Binary files /dev/null and b/20-implementierungsheft/assets/password.png differ diff --git a/20-implementierungsheft/assets/passwordinput.png b/20-implementierungsheft/assets/passwordinput.png new file mode 100644 index 0000000..2419828 Binary files /dev/null and b/20-implementierungsheft/assets/passwordinput.png differ diff --git a/20-implementierungsheft/assets/passwordvalidator.png b/20-implementierungsheft/assets/passwordvalidator.png new file mode 100644 index 0000000..b089f7c Binary files /dev/null and b/20-implementierungsheft/assets/passwordvalidator.png differ diff --git a/20-implementierungsheft/assets/progresstime.png b/20-implementierungsheft/assets/progresstime.png new file mode 100644 index 0000000..6877a97 Binary files /dev/null and b/20-implementierungsheft/assets/progresstime.png differ diff --git a/20-implementierungsheft/assets/subscription.png b/20-implementierungsheft/assets/subscription.png new file mode 100644 index 0000000..58a84f9 Binary files /dev/null and b/20-implementierungsheft/assets/subscription.png differ diff --git a/20-implementierungsheft/implementierungsheft.tex b/20-implementierungsheft/implementierungsheft.tex new file mode 100644 index 0000000..ed54d59 --- /dev/null +++ b/20-implementierungsheft/implementierungsheft.tex @@ -0,0 +1,98 @@ +% \documentclass[a4paper, UTF8, 12pt]{article} +% \documentclass[a4paper, UTF8, 12pt]{scrbook} +\documentclass[parskip=half, a4paper, 12pt]{scrartcl} + +\usepackage[german]{babel} +\usepackage[dvipsnames]{xcolor} +% \usepackage{tikz} +% \usetikzlibrary{positioning} +% \usetikzlibrary{calc} +% \usetikzlibrary{arrows} +% \usetikzlibrary{intersections} +% \usepackage{tikz-uml} +% \usepackage{pgf-umlsd} +% \usepgflibrary{arrows} % for pgf-umlsd +% \tikzumlset{fill usecase=white} +\usepackage[margin=2.5cm]{geometry} +\usepackage{csquotes} +\usepackage[T1]{fontenc} +\usepackage{amsmath} +\usepackage{pdflscape} +\usepackage{graphicx} +\usepackage{caption} +\usepackage{subcaption} +\usepackage{float} +\usepackage{enumitem} +% \usepackage{textpos} +\usepackage{hyperref} +\usepackage{fancyhdr} +% \usepackage{multicol} +\usepackage{rest-api} +\usepackage{wrapfig} +\usepackage{textcomp} +\usepackage{ulem} +\usepackage{fourier} +\usepackage{array} +\usepackage{makecell} + + +\hypersetup{ +% ‘texdoc hyperref‘ for options + pdftitle={ + PSE\textsuperscript{2} + - Podcast Synchronisation made Efficient Pflichtenheft + }, + bookmarks=true, +} +\usepackage{csquotes} +\usepackage[toc]{glossaries} +\usepackage{lastpage} + +\renewcommand\theadalign{bc} +\renewcommand\theadfont{\bfseries} +\renewcommand\theadgape{\Gape[4pt]} +\renewcommand\cellgape{\Gape[4pt]} + +\include{sections/glossar} + +\title{Pflichtenheft} +\author{KIT Students et al} +\date{2.12.2022} + +\pagestyle{fancy} +\setkomafont{pageheadfoot}{\footnotesize\scshape} +\fancyhead{} % clear all header fields +% \fancyhead[L]{Pflichtenheft} +\fancyhead[L]{PSE\textsuperscript{2} - Podcast Synchronisation made Efficient} +% \fancyhead[R]{2.12.2022} +\fancyfoot{} % clear all footer fields +\fancyfoot[R]{\thepage{} / \pageref{LastPage}} +\fancyfoot[L]{Praxis der Softwareentwicklung} +\fancyfoot[C]{} + +\begin{document} + +\include{titlepage} +\setcounter{page}{1} + +\tableofcontents + +\include{sections/einleitung} + +\include{sections/methodology} + +\include{sections/frontend} +\newpage + +\include{sections/changes} + +\include{sections/timeline} + +\include{sections/features} + +\include{sections/tests} + +\printglossaries +% \glsaddall + +\end{document} diff --git a/20-implementierungsheft/sections/anforderungsanalyse.tex b/20-implementierungsheft/sections/anforderungsanalyse.tex new file mode 100644 index 0000000..9af8d4d --- /dev/null +++ b/20-implementierungsheft/sections/anforderungsanalyse.tex @@ -0,0 +1,185 @@ +\newcommand{\newrequirementlist}[1]{ + % https://www.overleaf.com/learn/latex/Lists#Creating_a_new_list_with_enumitem + \newlist{#1list}{enumerate}{1} + \setlist[#1list, 1] + { + before=\leavevmode, + label=\upshape\textlangle #1\arabic*\textrangle, + ref=\upshape\textlangle #1\arabic*\textrangle, + resume=#1list + } +} + +\newrequirementlist{RM} +\newrequirementlist{RS} +\newrequirementlist{RC} +\newrequirementlist{RW} + +\section{Anforderungsanalyse} + +\subsection{ Musskriterien }\label{r:musskriterien} + +Musskriterien: unabdingbare Leistungen der Software. + +\subsubsection*{ Funktionale Anforderungen } + +\begin{RMlist} + \item\label{r:login} Der Benutzer kann sich im Webfrontend mit einer + E-Mail-Adresse und einem Pass\-wort erstmalig registrieren und bei + abgeschlossener Registrierung fortan mit denselben Informationen + anmelden. + Für eine erfolgreiche Registrierung muss die angegebene + E-Mail-Adresse zuerst verifiziert werden.\\ + \item\label{r:store} Die Liste der \Glspl{abo} eines Benutzers sowie + der zeitliche Fortschritt beim Anhören(/Ansehen) von jeder begonnen + \Gls{episode} werden für jeden Benutzer gespeichert. + \item\label{r:sync} Die Liste der \Glspl{abo} eines Benutzers + sowie der zeitliche Fortschritt beim Anhören(/Ansehen) von jeder + begonnenen \Gls{episode} wird automatisch über alle von einem Benutzer + verknüpften \Gls{podcatcher} aktualisiert. + Dabei sind der zu synchronisierende Stand der \Glspl{abo} und der + zeitliche Fortschritt jeweils definiert als derjenige Stand, der + zeitlich am kürzesten zurückliegt.\\ + \item\label{r:ui} Das Webfrontend bietet dem Benutzer eine graphische + Benutzeroberfläche zur Navigation und zur Ansteuerung einzelner + Funktionalitäten.\\ + \item\label{r:reset-pw} Der Benutzer kann sein Passwort ändern und es + gibt eine ,,Passwort vergessen''-Funktion. + So kann ein angemeldeter Benutzer sein Passwort direkt im Webfrontend + ändern. + Im nicht angemeldeten Zustand kann der Benutzer sein Passwort über + die registrierte E-Mail-Adresse zurücksetzen.\\ + \item\label{r:show-podcasts} Das Webfrontend bietet dem Benutzer die + Möglichkeit, sich die Liste seiner \Glspl{abo} anzeigen zu + lassen. + Die dabei dargestellten Informationen beinhalten den Namen des + \Glspl{abo} und eine gerundete Angabe darüber, wie lange es her ist, + dass der Benutzer das letzte Mal eine \Gls{episode} dieses \Glspl{abo} + konsumiert hat.\\ +\end{RMlist} + +\subsubsection*{ Nicht-funktionale Anforderungen } + +\begin{RMlist} + \item\label{r:requests} Der Synchronisations-Server kann mindestens + 50 Anfragen pro Sekunde verarbeiten.\\ + \item\label{r:desktop-first} Das Webfrontend ist primär für + Desktop-Benutzer ausgelegt. + \item\label{r:gpodder} Der Datenaustausch erfolgt über die Gpodder + \Gls{rest-api} unter Verwendung des Datenformats \Gls{json}. + \item\label{r:persistent-storage} Die Speicherung der Daten eines + Benutzers \ref{r:store} über den Synchronisations-Server erfolgt + persistent in einer \Gls{db}. Diese Daten des Benutzers sind die + Liste seiner \Glspl{abo} und der zeitliche Fortschritt beim Anhören + (/Ansehen) aller begonnenen \Glspl{episode}. + \item\label{r:api-extension} Die \Gls{gpodder} wird um Funktionalitäten + zur verbesserten Kommunikation zwischen Front- und Backend erweitert + (siehe \ref{r:login}).\\ +\end{RMlist} + + +\subsection{ Sollkriterien }\label{r:sollkriterien} + +Sollkriterien: erstrebenswerte Leistungen. + +\subsubsection*{ Funktionale Anforderungen } + +\begin{RSlist} + \item\label{r:man} Das Webfrontend bietet die Möglichkeit eine nicht + ausgefüllte Benutzeranleitung für das Synchronisieren von \Glspl{podcast} + anzuzeigen.\\ + \item\label{r:delete-acc} Der Benutzer kann seinen Account löschen. + Daraufhin werden alle auf diesen Benutzer bezogenen Daten gelöscht.\\ +\end{RSlist} + +\subsubsection*{ Nicht-funktionale Anforderungen } + +\begin{RSlist} + \item\label{r:backend-libs} Das Backend wird in \Gls{java} unter Verwendung des + quelloffenen Frameworks \Gls{spring} implementiert. Weiter wird für die + \Gls{db} das relationale Open-Source Datenbankverwaltungssystem MariaDB + eingesetzt. + \item\label{r:ui-libs} Die Weboberfläche wird mithilfe der + \Gls{ui-lib} React oder des Webframeworks Vue in JavaScript und + mit dem Frontend-CSS-Framework Bootstrap entwickelt. + \item\label{r:ui-source} + Verwendete \Glspl{ui-lib} werden von einem \Gls{packagemanager}, wie dem Node + Package Manager (npm) bezogen. Ein \Gls{bundler}, wie vite oder Webpack, + stellt ein minimiertes Skript von den Teilen der Bibliotheken zusammen, + die vom Code verwendet werden. Das minimierte Skript wird dann auf einem + eigenen Server für die Weboberfläche bereitgestellt. + \item\label{r:spa} Die Weboberfläche wird als \Gls{spa} + entworfen und aktualisiert dynamisch ihren Inhalt, sobald es eine + Antwort auf eine Anfrage an die \Gls{gpodder} \ref{r:api-compat} erhält. + \item\label{r:parse-metadata} Das Backend kann die Metadaten von + \Gls{podcast} aus + deren \Gls{rss}-Feeds (XML-Dateien) für die Anzeige im Webfrontend + \ref{r:show-podcasts} parsen. + \item\label{r:pw-requirements} Verwendete Passwörter müssen mindestens 8 Zeichen, + ein Sonderzeichen, eine Zahl sowie einen Klein- und einen Großbuchstaben + enthalten. + Diese Anforderungen gelten also insbesondere für über die Funktionen + \ref{r:login} und \ref{r:reset-pw} neu gewählte Passwörter.\\ + \item\label{r:save-pw} Passwörter werden sicher mittels \Gls{salt-hash} + gespeichert. + \item\label{r:session} Im Webfrontend angemeldete Benutzer bleiben dort + angemeldet. Hierfür wird eine Kombination aus einem \Gls{session-token} und + einem \Gls{cookie} verwendet.\\ +\end{RSlist} + +\subsection{ Kannkriterien } + +Kannkriterien: Leistungen, die enthalten sein können. + +\subsubsection*{ Funktionale Anforderungen } + +\begin{RClist} + \item\label{r:subscribe} Ein Benutzer kann über die Weboberfläche einen + abonnierten \Gls{podcast} über einen generierten Link teilen. + Öffnet nun ein anderer Nutzer den Link wird dieser zur Weboberfläche + weitergeleitet und mit einem Pop-up gefragt, ob dieser den + \Gls{podcast} abonnieren + möchte, falls noch nicht geschehen. + Akzeptiert der Nutzer, so wird der \Gls{podcast} zur Liste der + \Glspl{abo} des + Nutzers hinzugefügt. + Gegebenenfalls muss sich der Benutzer dafür zuerst anmelden. + Der Link setzt sich dabei unter anderem aus der URL des Webfrontends oder + einem \Gls{pseudoprotocol} und dem \Gls{podcast}-Link des Providers zusammen. + \item\label{r:unsubscribe} Das Webfrontend bietet dem Benutzer die Möglichkeit, + \Glspl{abo} zu entfernen beziehungsweise \Glspl{podcast} zu deabonnieren. + \item\label{r:import} Das Importieren und Exportieren aller benutzerbezogenen + Daten wird unterstützt (siehe \ref{r:dsgvo}). + \item\label{r:import-other} Das Umsiedeln von anderen \Gls{gpodder}-Plattformen und + damit insbesondere der damit verbundene Datenimport wird unterstützt. + \item\label{r:api-compat} Die Weboberfläche ist kompatibel mit beliebigen + \Glspl{gpodder}. + \item\label{r:responsive} Die Weboberfläche ist \gls{responsive}. + \item\label{r:admin} Es gibt Administrator Benutzerkonten. Eine angestrebte + Funktionalität dieser privilegierten Konten ist das Einsehen von Statistiken, + wie der Anzahl von Benutzern, und dem Abruf der Metadaten eines + \Glspl{abo}. +\end{RClist} + +\subsubsection*{ Nicht-funktionale Anforderungen } + +\begin{RClist} + \item\label{r:login-provider} Die Anmeldung im Webfrontend kann mit dem + offenen Protokoll \Gls{oauth} 2.0 über Google, Apple oder Facebook erfolgen. + Die bei der Verknüpfung eines \Gls{podcatcher}s mit dem Synchronisationsserver + geforderten Anmeldedaten werden dann automatisch für den betreffenden + Benutzer generiert. + Diese kann er im Webfrontend einsehen. + \item\label{r:live-update} Im Webfrontend angemeldete Benutzer bleiben dort + angemeldet, wenn das Backend ein Update bekommt. + \item\label{r:language} Die Benutzeroberfläche kann in mehreren Sprachen + angezeigt werden, wobei neben der standardmäßig deutschen + Benutzeroberfläche die zusätzliche Bereitstellung einer englischen + Version gegenüber anderen Fremdsprachen priorisiert angestrebt wird. + \item\label{r:dsgvo} Der Umgang mit personenbezogenen Daten ist konform mit + der \\\Gls{dsgvo} der Europäischen Union. + \item\label{r:docker} Die Benutzung von \Gls{docker} vereinfacht das Deployment auf + einen Server, da Abhängigkeiten bereits im \Gls{docker}-Image enthalten sind. + Außerdem bleibt bei einer Kompromittierung der Software das Host-System + durch Virtualisierung der Container sicher.\\ +\end{RClist} diff --git a/20-implementierungsheft/sections/apidoc.tex b/20-implementierungsheft/sections/apidoc.tex new file mode 100644 index 0000000..0cce48c --- /dev/null +++ b/20-implementierungsheft/sections/apidoc.tex @@ -0,0 +1,191 @@ +\newenvironment{urlParameter} +{ + \newcommand{\urlParamItem}[2] + { + \rowcolor{\methodLightColor} ##1 & ##2 \\ + } + \newcommand{\noUrlParameter}[1] + { + \small{\textit{##1}} + } + %\vspace{-0.61em} + + \arrayrulecolor{\methodColor} + + \begin{tabularx}{\textwidth}{X} + \rowcolor{\methodLightColor!20} + \textbf{URL-Parameter} \\ \hline + \end{tabularx} + + \tabularx{\textwidth}{l X} +} + +\newenvironment{pathParameter} +{ + \newcommand{\pathParamItem}[2] + { + \rowcolor{\methodLightColor} ##1 & ##2 \\ + } + \newcommand{\noPathParameter}[1] + { + \small{\textit{##1}} + } + %\vspace{-0.61em} + + \arrayrulecolor{\methodColor} + + \begin{tabularx}{\textwidth}{X} + \rowcolor{\methodLightColor!20} + \textbf{Pfad-Parameter} \\ \hline + \end{tabularx} + + \tabularx{\textwidth}{l X} +} + +\newenvironment{jsonKeys} +{ + \newcommand{\jsonKeyItem}[2] + { + \rowcolor{\methodLightColor} ##1 & ##2 \\ + } + \newcommand{\nojsonKeys}[1] + { + \small{\textit{##1}} + } + %\vspace{-0.61em} + + \arrayrulecolor{\methodColor} + + \begin{tabularx}{\textwidth}{X} + \rowcolor{\methodLightColor!20} + \textbf{Json-Keys} \\ \hline + \end{tabularx} + + \tabularx{\textwidth}{l X} +} + +\section{\Gls{api}} + +\subsection{Authentication API}\label{a:auth} + +\subsubsection{Registrierung}\label{a:register} + +\begin{apiRoute}{post}{/api/2/auth/register.json} + {Registriert einen Nutzer mit einer E-Mail-Adresse und Passwort. + + Versendet E-Mail mit Bestätigungslink an die angegebene E-Mail-Adresse.} + \begin{routeRequest}{application/json} + \begin{routeRequestBody} +{ + username: "jeff" + email: "jeff@example.com", + password: "MyNameIsJeff" +} + \end{routeRequestBody} + \end{routeRequest} + \begin{routeResponse}{application/json} + \begin{routeResponseItem}{200} + {OK: Nutzer wurde erfolgreich angelegt und E-Mail versendet.} + \end{routeResponseItem} + + \begin{routeResponseItem}{400} + {Bad Request: Fehler beim Parsen oder Eingabe nicht anforderungsgemäß.} + \end{routeResponseItem} + \end{routeResponse} +\end{apiRoute} + +Es wird nun sowohl ein Benutzername als auch eine E-Mail-Adresse für einen Nutzer gespeichert. +Der Benutzername wird in der Folge für die Authentifizierung und die Zuordnung der Anfragen verwendet. +Die E-Mail-Adresse wird vor dem Speichern mit einem festen Geheimschlüssel gesalted und gehashed. +Sie ist zum Ableich der bei der bei (ref an forgot) anzugebenden E-Mail-Adresse + +\newpage +\subsubsection{E-Mail verifizieren}\label{a:resetpassword} + +\begin{apiRoute}{get}{/api/2/auth/\{username\}/verify.json} + {Verifiziere die bei der Registrierung angegebene E-Mail-Adresse durch diese, per E-Mail versendete, URL.} + \begin{pathParameter} + \pathParamItem{username}{Nutzername des betreffenden Nutzers} + \end{pathParameter} + \begin{urlParameter} + \urlParamItem{token}{JSON-Web-Token (24h gültig)} + \end{urlParameter} + \begin{routeResponse}{application/json} + \begin{routeResponseItem}{200} + {OK: Der Benutzer wurde erfolgreich aktiviert und kann sich nun anmelden.} + \end{routeResponseItem} + + \begin{routeResponseItem}{400} + {Bad Request: Der Nutzer mit dem angegebenen Namen ist bereits verifiziert. } + \end{routeResponseItem} + + \begin{routeResponseItem}{401} + {Unauthorized: Der JWT ist ungültig. } + \end{routeResponseItem} + + \begin{routeResponseItem}{404} + {Not Found: Es exisitiert kein Nutzer mit dem angegebenen Benutzernamen. } + \end{routeResponseItem} + \end{routeResponse} +\end{apiRoute} + +Dieser Endpunkt wurde zur Verifizierung der bei der Registrierung angegebenen E-Mail-Adresse hinzugefügt. +Nach der Registrierung wird dem Benutzer eine E-Mail mit der URL dieses Endpunkts (inklusive Benutzernamen und JWT) +zugesendet. Klickt der Benutzer auf den Link wird die Anfrage im Backend verarbeitet und der Nutzer automatisch +zum Webfrontend weitergeleitet. Erst nach der Verifizierung der E-Mail-Adresse ist die Registrierung vollständig +abgeschlossen und der Account aktiviert - nun kann sich der Nutzer anmelden. + +\newpage +\subsubsection{Passwort vergessen}\label{a:forgot} + +\begin{apiRoute}{post}{/api/2/auth/\{email\}/forgot.json} + {Sende eine E-Mail zum Zurücksetzen des Passworts.} + \begin{pathParameter} + \pathParamItem{email}{E-Mail-Adresse des betreffenden Nutzers} + \end{pathParameter} + \begin{routeResponse}{application/json} + \begin{routeResponseItem}{200} + {OK: E-Mail wurde erfolgreich versendet.} + \end{routeResponseItem} + + \begin{routeResponseItem}{404} + {Not Found: Es exisitiert kein Nutzer mit der angegeben E-Mail-Adresse.} + \end{routeResponseItem} + \end{routeResponse} +\end{apiRoute} + +Die E-Mail-Adresse des Benutzers, der sein Passwort vergessen hat, wird nicht mehr im Request-Body +als \GLS{json}-Payload, sondern als Pfadvariable in der URL übergeben. + +\subsubsection{Passwort zurücksetzen}\label{a:resetpassword} + +\begin{apiRoute}{put}{/api/2/auth/\{username\}/resetpassword.json} + {Passwort des gegebenen Nutzers ändern nachdem dieser sein Passwort + vergessen hat. } + \begin{pathParameter} + \pathParamItem{username}{Nutzername des betreffenden Nutzers} + \end{pathParameter} + \begin{urlParameter} + \urlParamItem{token}{JSON-Web-Token} + \end{urlParameter} + \begin{routeRequest}{application/json} + \begin{routeRequestBody} +{ + password: "APasswordIWontForgetAgain" +} + \end{routeRequestBody} + \end{routeRequest} + \begin{routeResponse}{application/json} + \begin{routeResponseItem}{200} + {OK: Das Passwort wurde erfolgreich geändert.} + \end{routeResponseItem} + + \begin{routeResponseItem}{400} + {Bad Request: Fehler beim Parsen der Anfragen. } + \end{routeResponseItem} + + \begin{routeResponseItem}{401} + {Unauthorized: JWT ist ungültig. } + \end{routeResponseItem} + \end{routeResponse} +\end{apiRoute} \ No newline at end of file diff --git a/20-implementierungsheft/sections/aufbau.tex b/20-implementierungsheft/sections/aufbau.tex new file mode 100644 index 0000000..41a5839 --- /dev/null +++ b/20-implementierungsheft/sections/aufbau.tex @@ -0,0 +1,47 @@ +\section{Aufbau} + +\begin{landscape} + +\subsection{Klassendiagramm Backend} +Das Klassendiagramm zeigt alle für den Entwurf relevanten Klassen des Backends mit ihren öffentlichen Methoden. +Weiter zeigt das Diagramm die Aufteilung der Klassen in Pakete sowie schemenhaft dargestellte Verbindungen zu \Gls{db} und Webserver. + +% \input{assets/diagrams/classdiagram.latex} +\includegraphics[width=\linewidth]{assets/diagrams/classdiagram} +\end{landscape} + +\subsection{Sequenzdiagramme} + +\subsubsection{Authentication \Gls{api}} +\subsubsection*{Registrierung \scriptsize{(\ref{a:register})}} +\includegraphics[width=\textwidth]{assets/diagrams/sequencediagram-register} +\subsubsection*{Passwort vergessen und zurücksetzen \scriptsize{(\ref{a:forgot}, \ref{a:resetpassword})}} +\includegraphics[width=\textwidth]{assets/diagrams/sequencediagram-forgotAndResetPW} + +\subsubsection{Subscriptions \Gls{api}} +\subsubsection*{Abonnements hochladen \scriptsize{(\ref{a:uploadSubs})}} +\includegraphics[width=\textwidth]{assets/diagrams/sequencediagram-uploadSubscriptions} + +\subsubsection*{Abrufen aller Abonnements \scriptsize{(\ref{a:getSubs})}} +\includegraphics[width=\textwidth]{assets/diagrams/sequencediagram-getSubscriptions} + +\subsubsection{Episode Actions \Gls{api}} +\subsubsection*{Episode Actions hochladen \scriptsize{(\ref{a:uploadEpisodeActions})}} +\includegraphics[width=\textwidth]{assets/diagrams/sequencediagram-uploadEpisodeActions} + +\subsubsection*{Abrufen aller Episode Actions seit einem Zeitpunkt \scriptsize{(\ref{a:getEpisodeActions})}} +\includegraphics[width=\textwidth]{assets/diagrams/sequencediagram-getEpisodeActionsOfPodcastSince} + +\subsubsection*{Abrufen aller Episode Actions \scriptsize{(\ref{a:getEpisodeActions})}} +\includegraphics[width=\textwidth]{assets/diagrams/sequencediagram-getEpisodeActions} + +\subsection{Komponentendiagramm Backend} +\includegraphics[width=\textwidth]{assets/diagrams/backendComponentDiagram} + +\subsection{Verteilungsdiagram} +\includegraphics[width=\textwidth]{assets/diagrams/deployment} + +\subsection{\Gls{db}-Modell} + +\includegraphics[width=\textwidth]{assets/diagrams/db} + diff --git a/20-implementierungsheft/sections/backend.tex b/20-implementierungsheft/sections/backend.tex new file mode 100644 index 0000000..19efd65 --- /dev/null +++ b/20-implementierungsheft/sections/backend.tex @@ -0,0 +1,28 @@ +\section{Backend} + +Für das Backend wird das \Gls{java}-Framework \Gls{spring} verwendet. Neben seiner Modularität, +bietet es viele Funktionen, die unter +anderem die Entwicklung von Backend-Anwendungen vereinfachen. +Darunter fällt zum Beispiel die Unterstützung von Dependency Injection, dessen +Vorteile bereits beschrieben wurden. +Außerdem unterstützt \Gls{java} \Gls{spring} intern eine Verwaltung von \Glspl{db}, sodass +sich nicht mit dem \Gls{SQL}-Code an sich befasst werden muss. +Auch bezüglich Authentifizierung und Sicherheit bietet \Gls{spring} eigene Funktionalitäten. + +Zusätzlich dazu wird als Build-System für das \Gls{java}-\Gls{spring}-Backend Maven verwendet. +Maven hilft dabei alle Abhängigkeiten des Projekts zu verwalten und automatisiert +den Build Prozess. +Durch die zusätzliche Unterstützung von Versionskontrollsystemen und der Kompatibilität +zu vielen Continuous Integration-Tools wird außerdem die Entwicklung in einem Team erleichtert +und optimiert. Denn dadurch können Build- und Deployment-Prozesse automatisiert werden. +Außerdem bietet Maven Bibliotheken zum Testen sowie zur Generierung von Dokumentationen an. + +Vom Backend benötigte Abhängigkeiten: +\begin{itemize} + \item \Gls{spring} Web + \item \Gls{spring} Security + \item \Gls{spring} Mail Sender + \item \Gls{spring} Data JPA + \item Lombok + \item Rome (\Gls{rss} parsing/fetching) +\end{itemize} \ No newline at end of file diff --git a/20-implementierungsheft/sections/changes.tex b/20-implementierungsheft/sections/changes.tex new file mode 100644 index 0000000..20df017 --- /dev/null +++ b/20-implementierungsheft/sections/changes.tex @@ -0,0 +1,241 @@ +\newenvironment{urlParameter} +{ + \newcommand{\urlParamItem}[2] + { + \rowcolor{\methodLightColor} ##1 & ##2 \\ + } + \newcommand{\noUrlParameter}[1] + { + \small{\textit{##1}} + } + %\vspace{-0.61em} + + \arrayrulecolor{\methodColor} + + \begin{tabularx}{\textwidth}{X} + \rowcolor{\methodLightColor!20} + \textbf{URL-Parameter} \\ \hline + \end{tabularx} + + \tabularx{\textwidth}{l X} +} + +\newenvironment{pathParameter} +{ + \newcommand{\pathParamItem}[2] + { + \rowcolor{\methodLightColor} ##1 & ##2 \\ + } + \newcommand{\noPathParameter}[1] + { + \small{\textit{##1}} + } + %\vspace{-0.61em} + + \arrayrulecolor{\methodColor} + + \begin{tabularx}{\textwidth}{X} + \rowcolor{\methodLightColor!20} + \textbf{Pfad-Parameter} \\ \hline + \end{tabularx} + + \tabularx{\textwidth}{l X} +} + +\newenvironment{jsonKeys} +{ + \newcommand{\jsonKeyItem}[2] + { + \rowcolor{\methodLightColor} ##1 & ##2 \\ + } + \newcommand{\nojsonKeys}[1] + { + \small{\textit{##1}} + } + %\vspace{-0.61em} + + \arrayrulecolor{\methodColor} + + \begin{tabularx}{\textwidth}{X} + \rowcolor{\methodLightColor!20} + \textbf{Json-Keys} \\ \hline + \end{tabularx} + + \tabularx{\textwidth}{l X} +} + + +\section{Änderungen zum Entwurfsheft} + +Im Folgenden werden die Änderungen zum Entwurfsheft aufgelistet und erläutert warum diese +Änderungen gemacht wurden. + + +\subsection{Kompatibilität mit \Glspl{podcatcher}} + +Um die Kompatibilität mit \Glspl{podcatcher} - insbesondere AntennaPod und Kasts - sicherzustellen, mussten einige Änderungen vorgenommen werden. + +\subsubsection*{Speicherung von Benutzernamen und E-Mail-Adressen} + +AntennaPod erlaubt bei der Verknüpfung mit einem Synchronisations-Server nur die Anmeldung mit einem Benutzername, der keine E-Mail-Adresse ist. +Deshalb werden nun sowohl ein Benutzername als auch eine E-Mail-Adresse für jeden Nutzer gespeichert. +Der Benutzername wird dabei unverschlüsselt gespeichert. +Er wird zur Authentifizierung sowie für die Zuordnung der Anfragen verwendet. +Die E-Mail-Adresse hingegen, wird vor der Speicherung mit einem festen Geheimschlüssel gesalted und anschließend gehashed. +Sie existiert lediglich um bei einer Passwort-vergessen-Anfrage denjenigen Nutzer zu finden, zu dem die übergebene E-Mail-Adresse gehört, beziehungsweise dessen Existenz zu überprüfen. +Diese Änderung impliziert eine Anpassung des Formats, in dem Registrierungsanfragen als \GLS{json}-Payload übergeben werden. +Wie in der folgenden aktualisierten Spezifikation zu sehen existiert nun ein zusätzliches \GLS{json}-Attribut namens \enquote{username}. +\newline + +\begin{apiRoute}{post}{/api/2/auth/register.json} + {Registriert einen Nutzer mit einer E-Mail-Adresse und Passwort. + + Versendet E-Mail mit Bestätigungslink an die angegebene E-Mail-Adresse.} + \begin{routeRequest}{application/json} + \begin{routeRequestBody} +{ + "username": "jeff", + "email": "jeff@example.com", + "password": "MyNameIsJeff" +} + \end{routeRequestBody} + \end{routeRequest} + \begin{routeResponse}{application/json} + \begin{routeResponseItem}{200} + {OK: Nutzer wurde erfolgreich angelegt und E-Mail versendet.} + \end{routeResponseItem} + + \begin{routeResponseItem}{400} + {Bad Request: Fehler beim Parsen oder Eingabe nicht anforderungsgemäß.} + \end{routeResponseItem} + \end{routeResponse} +\end{apiRoute} + +\subsubsection*{Device API} + +Beim Einrichten eines Synchronisations-Servers rufen sowohl AntennaPod als auch Kasts statt einem anfänglichen Login sofort den List-Devices-Endpunkt der \gls{gpodder}.net API auf. +Dieser Endpunkt wurde entsprechend hinzugefügt. +Da jedoch explizit keine Unterscheidung von Geräten bei der Synchronisation unterstützt wird, wird dieser intern wie ein Login-Aufruf behandelt. +\newline + +\begin{apiRoute}{get}{/api/2/devices/\{username\}.json} + {Gegebenen Nutzer des gegebenen Geräts mithilfe HTTP Basic Auth einloggen oder Gültigkeit des im \enquote{sessionid} \Gls{cookie} gespeicherten JWTs bestätigen. + + Gibt außerdem eine Liste mit einem Dummy-Device zurück, damit die Einrichtung der Synchronisation mit AntennaPod und Kasts möglich ist.} + \begin{pathParameter} + \pathParamItem{username}{Nutzername des einzuloggenden Nutzers} + \end{pathParameter} + \begin{routeResponse}{application/json} + \begin{routeResponseItem}{200} + {OK: Der Benutzer wurde erfolgreich mittles HTTP Basic Auth oder JWT eingeloggt und das \enquote{sessionid} Cookie wurde auf ein gültiges JWT gesetzt.} + \newline + \begin{routeResponseItemBody} +[ + { + "id": "dummy", + "caption": "device", + "type": "other", + "subscriptions": 0 + } +] + \end{routeResponseItemBody} + \end{routeResponseItem} + + \begin{routeResponseItem}{401} + {Unauthorized: Es liegen falsche Anmeldedaten oder ein ungültiges JWT vor.} + \end{routeResponseItem} + \end{routeResponse} + \begin{jsonKeys} + \jsonKeyItem{id}{Geräte-ID} + \jsonKeyItem{caption}{Ein für Menschen lesbarer Name für das Gerät} + \jsonKeyItem{type}{Typ des Geräts - mögliche Typen: desktop, laptop, mobile, server, other} + \jsonKeyItem{subscriptions}{Anzahl der Subscriptions auf dem Gerät} + \end{jsonKeys} +\end{apiRoute} + +\newpage +\subsection{Verifizierung der E-Mail-Adresse} + +Dieser Endpunkt wurde zur Verifizierung der bei der Registrierung angegebenen E-Mail-Adresse hinzugefügt. +Nach der Registrierung wird dem Benutzer eine E-Mail mit der URL dieses Endpunkts (inklusive Benutzernamen und JWT) +zugesendet. Klickt der Benutzer auf den Link wird die Anfrage im Backend verarbeitet und der Nutzer automatisch +zum Webfrontend weitergeleitet. Erst nach der Verifizierung der E-Mail-Adresse ist die Registrierung vollständig +abgeschlossen und der Account aktiviert - nun kann sich der Nutzer anmelden. +\newline + +\begin{apiRoute}{get}{/api/2/auth/\{username\}/verify.json} + {Verifiziere die bei der Registrierung angegebene E-Mail-Adresse durch diese, per E-Mail versendete, URL.} + \begin{pathParameter} + \pathParamItem{username}{Nutzername des betreffenden Nutzers} + \end{pathParameter} + \begin{urlParameter} + \urlParamItem{token}{JSON-Web-Token (24h gültig)} + \end{urlParameter} + \begin{routeResponse}{application/json} + \begin{routeResponseItem}{200} + {OK: Der Benutzer wurde erfolgreich aktiviert und kann sich nun anmelden.} + \end{routeResponseItem} + + \begin{routeResponseItem}{400} + {Bad Request: Der Nutzer mit dem angegebenen Namen ist bereits verifiziert. } + \end{routeResponseItem} + + \begin{routeResponseItem}{401} + {Unauthorized: Der JWT ist ungültig. } + \end{routeResponseItem} + + \begin{routeResponseItem}{404} + {Not Found: Es exisitiert kein Nutzer mit dem angegebenen Benutzernamen. } + \end{routeResponseItem} + \end{routeResponse} +\end{apiRoute} + + +\newpage +\subsection{RSSParser} + +Das primäre Ziel beim Entwurf des RSSParsers war es den Rest der Anwendung nicht +aufzuhalten, da das fetchen und parsen länger dauert. Daher soll der RSSParser asynchron +ausgeführt werden. +Damit das für die Implementierung verwendete Framework \Gls{spring} dies unterstützt müssen +mehr Bedingungen erfüllt sein als im Entwurf berücksichtigt wurden, weshalb dieser abgeändert werden musste. + +Unter anderem können nur Komponenten die von \Gls{spring} verwaltet werden asynchron ausgeführt werden. +Weiter können nur öffentliche Methoden von \Gls{spring} als asynchron erkannt werden und bei +dem Aufruf einer asynchronen Methode muss die Klasse gewechselt werden damit diese asynchron +ausgeführt wird. + +Um diese Bedingungen zu erfüllen ruft der SubscriptionService oder der EpisodeActionService +die Validate Methode des RSSParsers mit der Subscription die überprüft werden +soll auf. Dies geschieht asynchron nach dem \enquote{Fire and Forget} Prinzip. Daher können +die Services unmittelbar weiter arbeiten und dem Nutzer so möglichst schnell eine Antwort +liefern. + +Die Validate Methode fragt den aktuellen \GLS{rss}-Feed der Subscription ab und parsed diesen. +Erfüllt der Feed die von Apple und Google für \Glspl{podcast} definierten Anforderungen +werden die Informationen aus dem Feed gespeichert. Dabei werden nur die \Glspl{episode} gespeichert, +die von mindestens einem Nutzer gehört wurden. Ist der Feed der Subscription nicht +valide wird die Subscription gelöscht. Um das Löschen und Speichern ausführen zu können +hält der Parser Referenzen auf das SubscriptionDao und EpisodeDao. + +Die im Entwurfsheft definierten Getter werden nicht mehr oder nur +noch als private Hilfsmethoden benötigt. + +\subsection{Reduzierte Datenzugriffsschicht} + +Mithilfe von JPA Repositories können Datenbankzugriffe und Abfragen ohne weitere Logik in den DAO-Schnittstellen einfach implementiert werden. +Wird beispielsweise nach einem \Gls{abo} anhand einer URL gesucht, so wird eine Methode der Form \textit{Optional findByUrl(String url)} in der SubscriptionDao deklariert. +Um die Implementierung dieser Abfrage kümmert sich JPA. + +Damit fallen alle ursprünglich geplanten DAO-Implementierungen weg, was für eine bessere Übersicht in der Datenzugriffsschicht sorgt. +\newpage + +\begin{landscape} + +\subsection{Überarbeitetes Klassendiagramm des Backends} +Das Klassendiagramm zeigt alle den überarbeiteten Entwurf des Backends. +Weiter zeigt das Diagramm die Aufteilung der Klassen in Pakete sowie schemenhaft dargestellte Verbindungen zu \Gls{db} und Webserver. + +% \input{assets/diagrams/classdiagram.latex} +\includegraphics[width=\linewidth]{assets/diagrams/class_after} +\end{landscape} \ No newline at end of file diff --git a/20-implementierungsheft/sections/einleitung.tex b/20-implementierungsheft/sections/einleitung.tex new file mode 100644 index 0000000..5f6080d --- /dev/null +++ b/20-implementierungsheft/sections/einleitung.tex @@ -0,0 +1,11 @@ +\section{Einleitung} + +Im vorangegangenen Pflichtenheft und Entwurfsheft wurde zuerst abstrahiert +die Idee des Podcast Synchronisationsservers beschrieben und die Ziele in Form von +Anforderungen definiert. Dabei soll sowohl ein Backend mit \Gls{db} als auch ein Frontend +zur Dateneinsicht und Verwaltung entstehen. Dann wurde die Implementierung diese Projekts +mittels eines Entwurfs geplant. + +In diesem Dokument wird die Implementierung des entworfenen Produkts dokumentiert. +Dabei wird auf Probleme und Änderungen gegenüber dem Pflichtenheft und Entwurfsheft eingegangen, +die Testfälle erläutert und der geplante und tatsächliche zeitliche Ablauf der Implementierung dokumentiert. \ No newline at end of file diff --git a/20-implementierungsheft/sections/features.tex b/20-implementierungsheft/sections/features.tex new file mode 100644 index 0000000..0ccf47e --- /dev/null +++ b/20-implementierungsheft/sections/features.tex @@ -0,0 +1,43 @@ +\section{Implementierte Muss-, Soll- und Kannkriterien} + +Nachdem nun die vorgenommenen Änderungen des Entwurfs behandelt wurden, geht es nun zu der eigentlichen Frage: +Was wurde umgesetzt? + +In der folgenden Tabelle werden alle im Pflichtenheft vorgestellten und im Entwurfsheft überarbeiteten Kriterien mit der Kriterienbezeichnung, einer kleinen Zusammenfassung und einer Info, ob die Kriterien implementiert wurden, aufgelistet. + +\begin{tabular}{l|c|c} + Bezeichner & Zusammenfassung & Implementiert\\\hline + RM1 & \makecell{Registrierung, Bestätigung und Anmeldung\\ eines Accounts über das Frontend} & Ja\\ + RM2 & Speichern von \Glspl{abo} und \Glspl{episode} auf dem Server & Ja\\ + RM3 & Synchronisierung zwischen \Glspl{podcatcher} & Ja\\ + RM4 & Eine Weboberfläche & Ja\\ + RM5 & Möglichkeit das eigene Passwort zu ändern / zurückzusetzen & Ja\\ + RM6 & \Gls{abo}liste in der Weboberfläche & Ja\\ + RM7 & 50 Anfragen / Sekunde bearbeiten & Zu testen\\ + RM8 & Primäre Auslegung des Webfrontends für Desktop-Nutzer & Ja\\ + RM9 & Unterstützung der \gls{gpodder}.net \Gls{rest-api} & Ja\\ + RM10 & Das Nutzen einer \Gls{db} zur Speicherung von Daten & Ja\\ + RM11 & Erweiterte \GLS{api} für Kommunikation zwischen Front- und Backend & Ja\\\hline + RS1 & Eine Anleitung (Platzhalter) & Ja\\ + RS2 & Die Möglichkeit einen Account zu löschen & Ja\\ + RS3 & \Gls{spring} für das Backend und MariaDB als \Gls{db} & Ja\\ + RS4 & Vue.js und Bootstrap für das Frontend & Ja\\ + RS5 & npm und vite für das Frontend & Ja\\ + RS6 & \makecell{Eine \Gls{spa} als Frontend\\mit dynamischer Aktualisierung} & Ja\\ + RS7 & Ein \GLS{rss}-Parser um Daten aus einem \GLS{rss}-Feed zu lesen & Ja\\ + RS8 & Mindestanforderungen an ein Passwort & Ja\\ + RS9 & \Gls{salt-hash} für Verschlüsselung der Personenbezogenen Daten & Ja\\ + RS10 & Nutzer bleibt über JWT angemeldet und authentifiziert & Ja\\\hline + RC1 & Abonnierten \Gls{podcast} in Weboberfläche über Link teilen und hinzufügen & Ja\\ + RC2 & \Glspl{abo} in Weboberfläche deabonnieren & Nein\\ + RC3 & Importieren und Exportieren aller benutzerbezogenen Daten & Ja\\ + RC4 & Umsiedeln von anderen \Gls{gpodder}-Plattformen & Ja\\ + RC5 & Kompatible Weboberfläche für beliebige \gls{gpodder}.net APIs & Entfernt\\ + RC6 & \Gls{responsive} designte Weboberfläche & Ja\\ + RC7 & Administratorkonten mit privilegierten Funktionen & Nein\\ + RC8 & \Gls{oauth} 2.0 im Webfrontend & Nein\\ + RC9 & Bei Ausfall des Backends im Frontend angemeldet bleiben & Ja\\ + RC10 & Mehrsprachige Weboberfläche & Ja\\ + RC11 & Erfüllung der DSGVO & Teils\\ + RC12 & \Gls{docker} für einfaches Deployment und Sicherheit & Ja +\end{tabular} \ No newline at end of file diff --git a/20-implementierungsheft/sections/features.tex.orig b/20-implementierungsheft/sections/features.tex.orig new file mode 100644 index 0000000..78839cf --- /dev/null +++ b/20-implementierungsheft/sections/features.tex.orig @@ -0,0 +1,82 @@ +\section{Implementierte Muss-, Soll- und Kannkriterien} + +Nachdem nun die vorgenommenen Änderungen des Entwurfs behandelt wurden, geht es nun zu der eigentlichen Frage: +Was wurde umgesetzt? + +In der folgenden Tabelle werden alle im Pflichtenheft vorgestellten und im Entwurfsheft überarbeiteten Kriterien mit der Kriterienbezeichnung, einer kleinen Zusammenfassung und einer Info, ob die Kriterien implementiert wurden, aufgelistet. + +\hspace{-1cm} +\begin{tabular}{l|c|c} +<<<<<<< HEAD + Bezeichner & Zusammenfassung & Implementiert\\\hline + \textlangle RM1 \textrangle & \makecell{Registrierung, Bestätigung und Anmeldung\\ eines Accounts über das Frontend} & Ja\\ + \textlangle RM2 \textrangle & Speichern von Abonnements und Episoden auf dem Server & Ja\\ + \textlangle RM3 \textrangle & Synchronisierung zwischen Podcatchern & Ja\\ + \textlangle RM4 \textrangle & Eine Weboberfläche & Ja\\ + \textlangle RM5 \textrangle & Möglichkeit das eigene Passwort zu ändern / zurückzusetzen & Ja\\ + \textlangle RM6 \textrangle & Abonnementliste in der Weboberfläche & Ja\\ + \textlangle RM7 \textrangle & 50 Anfragen / Sekunde bearbeiten & Zu testen\\ + \textlangle RM8 \textrangle & Primäre Auslegung des Webfrontends für Desktop-Nutzer & Ja\\ + \textlangle RM9 \textrangle & Unterstützung der gpodder.net RESTful-API & Ja\\ + \textlangle RM10\textrangle & Das Nutzen einer Datenbank zur Speicherung von Daten & Ja\\ + \textlangle RM11\textrangle & Erweiterte API für Kommunikation zwischen Front- und Backend & Ja\\\hline\hline + \textlangle RS1 \textrangle & Eine Anleitung (Platzhalter) & Ja\\ + \textlangle RS2 \textrangle & Die Möglichkeit einen Account zu löschen & Ja\\ + \textlangle RS3 \textrangle & Spring für das Backend und MariaDB als Datenbank & Ja\\ + \textlangle RS4 \textrangle & Vue.js und Bootstrap für das Frontend & Ja\\ + \textlangle RS5 \textrangle & npm und vite für das Frontend & Ja\\ + \textlangle RS6 \textrangle & \makecell{Eine Single-Page-Application als Frontend\\mit dynamischer Aktualisierung} & Ja\\ + \textlangle RS7 \textrangle & Ein RSS-Parser um Daten aus einem RSS-Feed zu lesen & Ja\\ + \textlangle RS8 \textrangle & Mindestanforderungen an ein Passwort & Ja\\ + \textlangle RS9 \textrangle & Salting und Hashing für Verschlüsselung der Personenbezogenen Daten & Ja\\ + \textlangle RS10\textrangle & Nutzer bleibt über JWT angemeldet und authentifiziert & Ja\\\hline\hline + \textlangle RC1 \textrangle & Abonnierten Podcast in Weboberfläche über Link teilen und hinzufügen & Ja\\ + \textlangle RC2 \textrangle & Abonnements in Weboberfläche deabonnieren & Nein\\ + \textlangle RC3 \textrangle & Importieren und Exportieren aller benutzerbezogenen Daten & Ja\\ + \textlangle RC4 \textrangle & Umsiedeln von anderen Gpodder-Plattformen & Ja\\ + \textlangle RC5 \textrangle & Kompatible Weboberfläche für beliebige gpodder.net APIs & Entfernt\\ + \textlangle RC6 \textrangle & Responsive designte Weboberfläche & Ja\\ + \textlangle RC7 \textrangle & Administratorkonten mit privilegierten Funktionen & Nein\\ + \textlangle RC8 \textrangle & OAuth 2.0 im Webfrontend & Nein\\ + \textlangle RC9 \textrangle & Bei Ausfall des Backends im Frontend angemeldet bleiben & Ja\\ + \textlangle RC10\textrangle & Mehrsprachige Weboberfläche & Ja\\ + \textlangle RC11\textrangle & Erfüllung der DSGVO & Teils\\ + \textlangle RC12\textrangle & Docker für einfaches Deployment und Sicherheit & Ja +\end{tabular} +======= + Bezeichner & Zusammenfassung & Implementiert\\\hline + RM1 & \makecell{Registrierung, Bestätigung und Anmeldung\\ eines Accounts über das Frontend} & Ja\\ + RM2 & Speichern von \Glspl{abo} und \Glspl{episode} auf dem Server & Ja\\ + RM3 & Synchronisierung zwischen \Glspl{podcatcher} & Ja\\ + RM4 & Eine Weboberfläche & Ja\\ + RM5 & Möglichkeit das eigene Passwort zu ändern / zurückzusetzen & Ja\\ + RM6 & \Gls{abo}liste in der Weboberfläche & Ja\\ + RM7 & 50 Anfragen / Sekunde bearbeiten & Zu testen\\ + RM8 & Primäre Auslegung des Webfrontends für Desktop-Nutzer & Ja\\ + RM9 & Unterstützung der \gls{gpodder}.net \Gls{rest-api} & Ja\\ + RM10 & Das Nutzen einer \Gls{db} zur Speicherung von Daten & Ja\\ + RM11 & Erweiterte \GLS{api} für Kommunikation zwischen Front- und Backend & Ja\\\hline + RS1 & Eine Anleitung (Platzhalter) & Ja\\ + RS2 & Die Möglichkeit einen Account zu löschen & Ja\\ + RS3 & \Gls{spring} für das Backend und MariaDB als \Gls{db} & Ja\\ + RS4 & Vue.js und Bootstrap für das Frontend & Ja\\ + RS5 & npm und vite für das Frontend & Ja\\ + RS6 & \makecell{Eine \Gls{spa} als Frontend\\mit dynamischer Aktualisierung} & Ja\\ + RS7 & Ein \GLS{rss}-Parser um Daten aus einem \GLS{rss}-Feed zu lesen & Ja\\ + RS8 & Mindestanforderungen an ein Passwort & Ja\\ + RS9 & \Gls{salt-hash} für Verschlüsselung der Personenbezogenen Daten & Ja\\ + RS10 & Nutzer bleibt über JWT angemeldet und authentifiziert & Ja\\\hline + RC1 & Abonnierten \Gls{podcast} in Weboberfläche über Link teilen und hinzufügen & Ja\\ + RC2 & \Glspl{abo} in Weboberfläche deabonnieren & Nein\\ + RC3 & Importieren und Exportieren aller benutzerbezogenen Daten & Ja\\ + RC4 & Umsiedeln von anderen \Gls{gpodder}-Plattformen & Ja\\ + RC5 & Kompatible Weboberfläche für beliebige \gls{gpodder}.net APIs & Entfernt\\ + RC6 & \Gls{responsive} designte Weboberfläche & Ja\\ + RC7 & Administratorkonten mit privilegierten Funktionen & Nein\\ + RC8 & \Gls{oauth} 2.0 im Webfrontend & Nein\\ + RC9 & Bei Ausfall des Backends im Frontend angemeldet bleiben & Ja\\ + RC10 & Mehrsprachige Weboberfläche & Ja\\ + RC11 & Erfüllung der DSGVO & Teils\\ + RC12 & \Gls{docker} für einfaches Deployment und Sicherheit & Ja +\end{tabular} +>>>>>>> main diff --git a/20-implementierungsheft/sections/frontend.tex b/20-implementierungsheft/sections/frontend.tex new file mode 100644 index 0000000..ae5be0e --- /dev/null +++ b/20-implementierungsheft/sections/frontend.tex @@ -0,0 +1,196 @@ +\section{Weboberfläche} + +Die Weboberfläche wird mit dem Frontend-Web-Framework Vue.js erstellt. Mit Vue +werden wiederverwendbare, auf Datenänderungen reagierende Komponenten erstellt. +Die Komponenten nutzen ein fertiges Aussehen von dem Frontend-CSS-Framework +Bootstrap. Außerdem werden Icons der freien Schriftart fontawesome +verwendet. + +An der generellen Struktur und wichtigen Komponenten hat sich in der +Implementierung nichts großes zum Entwurf geändert. Einige Komponenten konnten +in der Implementierung allerdings abstrahiert werden, sodass weniger Code +dupliziert werden musste. Dazu gehören zum Beispiel die Komponenten, welche ein +Eingabefeld bereit stellen. + +Durch eine gute Arbeitsteilung blieb genug Zeit, um auch einige Kann-Kriterien +zu implementieren, weshalb ein paar Abhängigkeiten hinzugekommen sind. So werden +jszip und file-saver benötigt, um das Kann-Kriterium RC3 umzusetzen, durch +welches man Nutzerdaten im- und exportieren kann. + +Pinia konnten wir als Abhängigkeit entfernen, weil Vue von sich aus genügend +Funktionalität bietet die Nutzerdaten global allen Komponenten bereit zu +stellen. + +Anfangs überlegten wir den Wrapper \texttt{bootstrap-vue} zu verwenden, welcher +Bootstrap-HTML-Strukturen als Vue-Komponenten bereit stellt. Dies hätte des +Vorteil, dass man nur jene Komponenten importieren müsste, die man auch +benötigt. Es stellte sich heraus, dass \texttt{vue-bootstrap} nicht mit neusten +Version Vue3 kompatibel ist, weshalb wir uns entschieden Bootstrap wie gewohnt +komplett einzubinden. + +Während der Implementierung sind wir auf die Bibliothek \texttt{VueUse} +gestoßen, mit der man die Abhängigkeit \texttt{file-saver} und mehrere eigene +Wrapper für Browser-\Gls{api}s ersetzen könnte. So kann man das \Gls{dashboard} auch in +Zukunft noch weiter verbessern. + +Vom Frontend benötigte Abhängigkeiten: +\begin{itemize} + \item vite + \item vue + \item vue-router + \item \textcolor{red}{\sout{Pinia} wird durch globale Vue-Interne \texttt{ref} ersetzt} + \item bootstrap + \item fontawesome + \item vue-i18n (Support für mehrere Sprachen) + \item \textcolor{Green}{dayjs (Rechnen und formatieren von Zeiten)} + \item \textcolor{Green}{jszip (erstellen von Im-/Exports)} + \item \textcolor{Green}{file-saver (Abstraktion für Datei-Speichern-Dialog)} +\end{itemize} + +\subsection{Komponentendiagramm Web-Frontend} +\includegraphics[width=\textwidth]{assets/diagrams/componentdiagram} + +\subsection{Hinzugefügte Komponenten} + +% PasswordInput +% PasswordValidator +% FloatingLabelInput +% Loading +% ErrorLog +% ProgressTime + +\begin{minipage}{.7\linewidth} + +\subsubsection*{FloatingLabelInput} + +\begin{description} +\item[Tag] \texttt{} +\item[Props] \mbox{} \\ + \emph{type} \mbox{} Beschreibt den Inhalt des Eingabefelder. Handelt es sich + um eine E-Mail, ein Passwort oder nur Text? \\ + \emph{label} \mbox{} Das Label wird als Platzhalter und kleine Überschrift + angezeigt. +\item[Beschreibung] Abstrahiert eine HTML-Struktur von Bootstrap, um ein schönes + Eingabefeld anzuzeigen. +\end{description} +\end{minipage} +\begin{minipage}{.3\linewidth} + \begin{figure}[H] + \includegraphics[width=\textwidth]{assets/floatinglabelinput.png} + \end{figure} +\end{minipage} + + +\vspace{.5cm} + +\begin{minipage}{.7\linewidth} + +\subsubsection*{PasswordInput} + +\begin{description} +\item[Tag] \texttt{} +\item[Props] \mbox{} \\ + \emph{label} \mbox{} Das Label wird als Platzhalter und kleine Überschrift + angezeigt. +\item[Beschreibung] Erweitert den FloatingLabelInput um einen Knopf mit dem die + Sichtbarkeit des Eingabefeldes gewechselt werden kann. Dabei wird intern der + Typ des Eingabefeldes zwischen Text and Passwort gewechselt. +\end{description} +\end{minipage} +\begin{minipage}{.3\linewidth} + \begin{figure}[H] + \includegraphics[width=\textwidth]{assets/passwordinput.png} + \end{figure} +\end{minipage} + + +\vspace{.5cm} + +\begin{minipage}{.7\linewidth} + +\subsubsection*{PasswordValidator} + +\begin{description} +\item[Tag] \texttt{} +\item[Beschreibung] Besteht aus zwei PasswordInputs und überprüft diese auf + Gleichheit und Kriterien für eine gutes Passwort. +\end{description} +\end{minipage} +\begin{minipage}{.3\linewidth} + \begin{figure}[H] + \includegraphics[width=\textwidth]{assets/passwordvalidator.png} + \end{figure} +\end{minipage} + + +\vspace{.5cm} + +\begin{minipage}{.7\linewidth} + +\subsubsection*{Loading} + +\begin{description} +\item[Tag] \texttt{...} +\item[Props] \mbox{} \\ + \emph{waitingFor} \mbox{} Eine Referenz auf eine Bedingung wann die Kinder + der Komponente gezeigt werden sollen. +\item[Beschreibung] Zeigt solange ein Ladesymbol an, bis die Bedingung in + \texttt{waitingFor} erfüllt ist und stattdessen die Kinder der Komponente + gezeigt werden. +\end{description} +\end{minipage} +\begin{minipage}{.3\linewidth} + \begin{figure}[H] + \includegraphics[width=\textwidth]{assets/loading.png} + \end{figure} +\end{minipage} + + +\vspace{.5cm} + +\begin{minipage}{.7\linewidth} + +\subsubsection*{ProgressTime} + +\begin{description} +\item[Tag] \texttt{} +\item[Props] \mbox{} \\ + \emph{unix} \mbox{} Anzahl an Sekunden. +\item[Beschreibung] Nimmt eine Anzahl an Sekunden und gibt an wie viele Stunden, + Minuten und Sekunden die Anzahl entspricht. +\end{description} +\end{minipage} +\begin{minipage}{.3\linewidth} + \begin{figure}[H] + \includegraphics[width=\textwidth]{assets/progresstime.png} + \end{figure} +\end{minipage} + + +\vspace{.5cm} + +\begin{minipage}{.7\linewidth} + +\subsubsection*{ErrorLog} + +\begin{description} +\item[Tag] \texttt{} +\item[Beschreibung] Zeigt eine Liste von Warnungen an, welche aus einem globalen + Zustandsspeicher geladen werden. +\end{description} +\end{minipage} +\begin{minipage}{.3\linewidth} + \begin{figure}[H] + \includegraphics[width=\textwidth]{assets/errorlog.png} + \end{figure} +\end{minipage} + + +\vspace{.5cm} + +\subsubsection*{Dashboard/FormLayout} + +\begin{description} +\item[Tag] \texttt{ } +\item[Beschreibung] Fügt den Kindern einen Seitenabstand hinzu. +\end{description} diff --git a/20-implementierungsheft/sections/glossar.tex b/20-implementierungsheft/sections/glossar.tex new file mode 100644 index 0000000..9caad87 --- /dev/null +++ b/20-implementierungsheft/sections/glossar.tex @@ -0,0 +1,371 @@ +\makeglossaries + +\newglossaryentry{spa} +{ + name=Single-Page-Application, + description={ + ist ein Webseiten-Modell, bei welchem dem Nutzer nur ein Webdokument + bereitgestellt wird. Mit einem Skript wird der Inhalt der Seite + dynamisch mit Daten einer API befüllt. Außerdem verwaltet die Seite + (nicht der Server), welcher Inhalt bei welchem Pfad angezeigt wird. Dies + erzeugt geringere Serverlast und eine bessere Nutzererfahrung, da die + Seitenstruktur beim Laden von neuen Inhalten erhalten bleibt} +} + +\newglossaryentry{packagemanager} +{ + name=Paketmanager, + description={ + ist ein Programm, welches Pakete und dessen Abhängigkeiten verwaltet, + installiert, entfernt und aktualisiert. Pakete können andere Programme, + Plugins oder Software-Bibliotheken sein} +} + +\newglossaryentry{bundler} +{ + name=Bundler, + description={ + ist ein Programm, welches genutzte Teile von Abhängigkeiten eines + Software-Projekts in passender Reihenfolge zusammensucht und daraus + Dateien erstellt, die für den Nutzer bereitgestellt werden können. Dabei + kann der Bundler mit zusätzlichen Modulen Dateien erzeugen, die + rückwärtskompatibel oder für den Nutzer schwerer einsehbar sind} +} + +\newglossaryentry{java} +{ + name=Java, + description={ + ist eine objekt-orientierte interpretierte kompilierte + Programmiersprache, welche plattformunabhängig auf einer virtuellen + Maschine ausgeführt wird} +} + +\newglossaryentry{db} +{ + name=Datenbank, + plural=Datenbanken, + description={ + ist ein System um Daten persistent zu speichern und effizient zu + verwalten. Am meisten verbreitet sind relationale Datenbanken, welche + Daten in Tabellen mit Referenzen zu Einträgen anderer Tabellen + speichern. Programme können dann über eine Anfragesprache (Structured + Query Language - \Gls{SQL}) komplexe Operationen auf den Daten ausführen} +} + +\newglossaryentry{docker} +{ + name=Docker, + description={ + ist ein Programm, das virtualisierte Container ausführt. Ein Programm in + so einem Container läuft in seiner eigenen virtuellen Umgebung, wodurch + das Host-System sicher bleibt. Zudem lassen sich die Container leicht + auf andere Systeme verteilen} +} + +% RESTfull-API, JSON, RSS-Feed, Salting and Hasing, OAuth, Cookie, Garbage +% Collection, DSGVO, Podcast, Podcatcher, Episode, Gpodder, + +\newglossaryentry{podcatcher} +{ + name=Podcatcher, + plural=Podcatchern, + description={ + ist ein Programm, über welches man Podcasts entdecken, abonnieren und + Episoden von Podcasts hören kann. Mit einem Account auf einer Plattform, + welche eine Gpodder-API zur Verfügung stellt, können Ereignisse, die von + einem Nutzer ausgehen, auf anderen Podcatchern des Nutzers + synchronisiert werden} +} + +\newglossaryentry{podcast} +{ + name=Podcast, + description={ + ist ein RSS-Feed, dessen Einträge die Episoden darstellen} +} + +\newglossaryentry{episode} +{ + name=Episode, + plural=Episoden, + description={ + ist ein Eintrag in einem Podcast. Eine URL in dem Eintrag zeigt auf eine + Medien-Datei, welche vom Podcatcher abgespielt werden kann} +} + +\newglossaryentry{rest-api} +{ + name=RESTful-API, + description={ + ist ein Schnittstellenentwurf über das Hypertext Transfer Protocol + (HTTP), bei dem die Schnittstellen strukturiert als Pfad an einem + Endpunkt erreichbar sind. Mittels verschiedener HTTP-Methoden können an + der Schnittstelle Daten abgefragt (GET), gesendet (PUT), gelöscht + (DELETE) oder geändert (POST) werden. Die Daten, die über die + Schnittstelle gesendet werden liegen meist im JSON-Format vor} +} + + +\newglossaryentry{gpodder} +{ + name=Gpodder-API, + description={ + wird von gpodder.net benutzt und entwickelt. Die API wird als + Schnittstelle zwischen Podcatchern und Podcast Synchronisationsservern + verwendet. Weitere Details sind unter + "https://gpoddernet.readthedocs.io/en/latest/api/" zu finden} +} + +\newglossaryentry{json} +{ + name=JSON, + description={ + (JavaScript Object Notation) ist ein Datenformat und wird zur + Übertragung von Strukturen und Daten eingesetzt. JSON besteht dabei aus + grundlegenden Datentypen sowie Objekten mit Schlüssel-Wert Paaren und + Listen} +} + +\newglossaryentry{oauth} +{ + name=OAuth, + description={ + (Open Authorization) ist ein offenes Protokoll, welches es Nutzern + ermöglicht, sich mit bereits bestehenden Accounts bei anderen Diensten + zu registrieren. Dabei werden benötigte Daten für die Registrierung über + die bereitgestellte Schnittstelle zur Verfügung gestellt} +} + +\newglossaryentry{garbage-collection} +{ + name=Garbage Collection, + description={ + ist eine automatische Speicherbereinigung, welche nicht mehr benötigten + Speicherplatz wieder freigibt. Die Bereinigung kann dabei in determinierten + Zeitintervallen erfolgen oder durch bestimmte Ereignisse ausgelöst + werden} +} + +\newglossaryentry{salt-hash} +{ + name=Salting und Hashing, + description={ + ist eine Methode um Passwörter so zu kodieren, dass sie nicht als + Klartext gespeichert werden und auch sicher vor Hash-Wörterbüchern sind. + Dafür wird dem Passwort ein bekanntes Wort, der Salt, angefügt, bevor + aus dem kompletten Wort eine Prüfsumme, ein Hash, generiert wird. Beim + Anmelden wird die Prüfsumme der Anmeldung mit der bekannten + Prüfsumme des Passworts verglichen} +} + +\newglossaryentry{rss} +{ + name=RSS, + description={ + (Really Simple Syndication) zeigt strukturiert Listen von Nachrichten + an. Die Änderungen werden im XML-Format in sogenannte RSS-Dateien + geschrieben, welche über einen Link abgerufen werden können} +} + +\newglossaryentry{dsgvo} +{ + name=Datenschutz-Grundverordnung, + description={ + (DSGVO) ist eine im europäischen Wirtschaftsraum + geltende Verordnung. Sie sorgt für eine Reglementierung bei der + Verarbeitung personenbezogener Daten. Unter anderem muss einsehbar sein, + welche Daten von Nutzern erhoben werden. Außerdem muss für einen Nutzer + die Möglichkeit bestehen, seine erhobenen Daten abrufen zu können} +} + +\newglossaryentry{push-pull} +{ + name=Push und Pull, + description={ + sind Methoden, um Daten auszutauschen. Bei der Pull-Methode + stellt Akteur A einem Akteur B eine Anfrage auf Daten und erhält diese + als Antwort. Damit Akteur A und B immer auf dem selben Stand sind, muss + Akteur A chronisch Anfragen an Akteur B stellen. Im Gegensatz dazu steht + die Push-Methode, bei der Akteur B den Akteuren mitteilt, dass er neue + Änderungen hat. Dafür muss Akteur B allerdings wissen mit welchen + anderen Akteuren er in Verbindung steht und diese Verbindung aufrecht + erhalten} +} + +\newglossaryentry{ui-lib} +{ + name=UI-Bibliothek, + plural=UI-Bibliotheken, + description={ + kümmert sich um das Layout einer Webseite. Dabei unterscheidet man + zwischen Design-Bibliotheken (wie Bootstrap), welche fertige + UI-Komponenten bereitstellen, und Layout-Bibliotheken (wie Vue oder + React.js), welche die Komponenten basierend auf Daten dynamisch + anzeigen} +} + +\newglossaryentry{responsive} +{ + name=Responsive, + description={ + Design ist ein Design-Prinzip für Webseiten, bei dem die selbe Webseite ihre + Komponenten dynamisch der Bildschirmbreite anpasst} +} + +\newglossaryentry{pseudoprotocol} +{ + name=Pseudoprotokoll, + description={ + ist ein URL-Schema, auf das Webseiten hören können, wenn sie sich das + URL-Schema im Browser anmelden. Bekannt Pseudoprotokolle sind: + ,,mailto:'', ,,tel:'' oder ,,irc:''} +} + +\newglossaryentry{dashboard} +{ + name=Dashboard, + description={ + ist die erste Seite auf der man landet, wenn man angemeldet ist} +} + +\newglossaryentry{abo} +{ + name=Abonnement, + description={ + ist ein abonnierter Podcast} +} + +\newglossaryentry{discovery} +{ + name=Discovery, + description={ + ist ein Feature der Gpodder-API, welches dem Nutzer eine Reihe von + Podcasts zum abonnieren anbietet} +} + +\newglossaryentry{session-token} +{ + name=Session-Token, + description={ + ist ein Wort, dass vom Client gespeichert wird solange der Nutzer + eingeloggt ist und bei jeder Anfrage an den Server mitgeschickt wird. + Der Server kann den Session-Token einem Nutzer zuordnen und so mit + nutzerspezifischen Daten antworten} +} + +\newglossaryentry{cookie} +{ + name=Cookie, + description={ + ist ein kleiner webseitenspezifischer Speicher im Browser, welcher vom + Server und von der Webseite gesetzt werden kann und bei jeder weiteren + Anfrage an den Server mitgesendet wird. Cookies bleiben entweder + temporär im Browserspeicher, bis der Browser geschlossen wird oder + permanent, bis ein optionales Verfallsdatum erreicht ist} +} + +\newglossaryentry{uiComponent} +{ + name=UI-Komponente, + plural=UI-Komponenten, + description={ + In Vue.js werden die grafischen Elemente einer Webseite in einzelne + Komponenten zerteilt. + Diese reagieren automatisch auf Änderungen und können ohne Neuladen + der Seite ihr Aussehen verändern und somit Änderungen direkt anzeigen} +} + +\newglossaryentry{spring} +{ + name=Spring, + description={ + Ein Java-Framework, welches die Entwicklung von Web-Applikationen erleichtert. + Dazu wird eine Reihe von Werkzeugsets zur Verfügung gestellt. + Unter anderem sind das Spring Web für das Erstellen von Webanwendungen, + Spring Security für die Verwaltung von Benutzerauthentifizierungen und + Spring Data JPA für die Arbeit mit relationalen Datenbanken + } +} + +\newglossaryentry{api} +{ + name=API, + plural=APIs, + description={ + Eine Schnittstelle, welche es ermöglicht auf Funktionalitäten einer Anwendung + zuzugreifen. APIs für Webanwendungen heißen WebAPIs. + Ein Beispiel für eine WebAPI ist die REST-API + } +} + +\newglossaryentry{business} +{ + name=Geschäftslogik, + description={ + Eine Schicht in der Anwendungsentwicklung, in der die Art und Weise, wie das + Programm auf Eingaben reagiert, wie Daten verarbeitet und wie sie gespeichert + werden sollen, festgelegt ist + } +} + +\newglossaryentry{solid} +{ + name=SOLID, + description={ + Eine Sammlung an Prinzipien, welche zu gutem objektorientierten Design führen soll. + Jedes Prinzip steht für einen Buchstaben in SOLID: + \textbf{S}ingle-Responsibility Prinzip, + \textbf{O}pen-Closed Prinzip, + \textbf{L}iskovsches Substitutionsprinzip, + \textbf{I}nterface Segregation Prinzip und + \textbf{D}ependency Inversion Prinzip + } +} + +\newglossaryentry{crud} +{ + name=CRUD, + description={ + CRUD steht für \textbf{C}reate, \textbf{R}ead, \textbf{U}pdate und \textbf{D}elete. + Hierbei handelt es sich um die grundlegenden Funktionen einer Anwendung, + die mit einer Datenbank arbeitet. + Hierbei können Daten angelegt, abgerufen, aktualisiert und gelöscht werden. + Auch in Web-Applikationen ist CRUD mit HTTP über die Anfragen POST, GET, PUT und DELETE + vertreten + } +} + +\newglossaryentry{SQL} +{ + name=SQL, + description={ + SQL (Structured Query Language) ist eine Sprache, die einen strukturierten Zugriff auf Datenbanken ermöglicht. + Daten können hierbei hinzugefügt, abgefragt, geändert und gelöscht werden. + Das besondere hierbei ist der strukturierte Zugriff auf Daten, indem explizit Daten mit bestimmten Kriterien und + Relationen ausgewählt und bearbeitet werden können. + SQL wird fast von allen verbreiteten Datenbanksystemen unterstützt + } +} + +\newglossaryentry{Base64} +{ + name=Base64, + description={ + Mithilfe von Base64 können 8-Bit-Binärdaten in eine ASCII-Zeichenkette + kodiert werden. So werden zum Beispiel E-Mail-Anhänge versendet + } +} + +\newglossaryentry{JSONP} +{ + name=JSONP, + description={ + JSONP ermöglicht die Übertragung von JSON-Daten zwischen verschiedenen Domains. + Dies wäre durch die Same-Origin-Policy nicht möglich. + JSONP nutzt allerdings die Tatsache aus, + dass sich Skripte domainübergreifend übertragen lassen. + Dazu werden die JSON-Daten als Argument einer übergebenen Funktion über + ein Skript-Element eingebunden + } +} diff --git a/20-implementierungsheft/sections/methodology.tex b/20-implementierungsheft/sections/methodology.tex new file mode 100644 index 0000000..87b8176 --- /dev/null +++ b/20-implementierungsheft/sections/methodology.tex @@ -0,0 +1,16 @@ +\section{Integrationsstrategie} + +Bei der Implementierung wurde nach dem Outside-In Prinzip vorangegangenen. +Dabei handelt es sich um eine inkrementelle und vorgehensorientierte Integrationsstrategie. +Sie versucht die Vorteile des Top-Down und des Bottom-Up Prinzips zu vereinen und die jeweiligen +Nachteile zu mindern. + +Bei der Outside-In Integration wird gleichzeitig auf höchster und niedrigster logischer +Ebene mit der Implementation begonnen. Das passt gut zu der gewählten Systemarchitektur. +Es wird sowohl frühzeitig die \Gls{db} eingebunden als auch das Frontend und die +Controller implementiert. Dafür wird erst später an der Service-Schicht gearbeitet. +Dieses Vorgehen wurde gewählt um die frühe Verfügbarkeit von testbaren Endpunkten zu haben. +Somit können das Front- und Backend schon nach kurzer Zeit miteinander Integriert werden, +wenn auch ohne funktionierende Serviceschicht. Außerdem wird für lange Zeit an verschiedenen +Punkten gearbeitet, sodass der Prozess dank des vorrausgegangenen Entwurfs gut auf das ganze +Team parallelisierbar ist. diff --git a/20-implementierungsheft/sections/tests.tex b/20-implementierungsheft/sections/tests.tex new file mode 100644 index 0000000..cdbf163 --- /dev/null +++ b/20-implementierungsheft/sections/tests.tex @@ -0,0 +1,21 @@ +\section{Testfälle} + +Für die Erstellung der Testfälle wurde die SpringBoot Testumgebung verwendet und +diese durch JUnit Testfälle ergänzt. + +Für das Testen wurde eine BaseTest Klasse erstellt, die grundlegende Funktionalität +einiger Kern Komponenten sicherstellt. Weiter Initialisiert sie die Anwendungsumgebung auf +der alle weiteren Tests aufbauen. + +Die Unit Tests erweitern diese BaseTest Klasse und können daher von gewisser Grundfunktionalität +ausgehen, die durch den BaseTest abgedeckt ist. Dadurch müssen nicht große Teile der +Anwendung durch Mock Objekte simuliert werden. + +Grundsätzlich wurde beim Entwurf der Testfälle nach dem Inside-Out-Prinzip vorangegangenen. +Die bisher geschriebenen Testfälle haben dabei nicht das Ziel Korrektheit zu garantieren, +sondern einen gewissen Qualitätsstandart zu sichern und gleichzeitig genug +Arbeitspunkte bereit zu stellen um im ganzen Team an diesem Projekt parallel arbeite zu können. + +Zusätzlich wurden bei der Implementierung auf ausgiebig getestete Annotationen von Spring und +Lombok zurückgegriffen die helfen die Anzahl an Fehlern bei standardisierten +\enquote{Boilerplater-Code} Aufgaben zu vermeiden. \ No newline at end of file diff --git a/20-implementierungsheft/sections/timeline.tex b/20-implementierungsheft/sections/timeline.tex new file mode 100644 index 0000000..7fa084d --- /dev/null +++ b/20-implementierungsheft/sections/timeline.tex @@ -0,0 +1,7 @@ +\section{Zeitlicher Verlauf} + +\subsection{Plan} +\includegraphics[width=\textwidth]{assets/diagrams/gantt-plan} + +\subsection{Realität} +\includegraphics[width=\textwidth]{assets/diagrams/gantt-reality} diff --git a/20-implementierungsheft/titlepage.tex b/20-implementierungsheft/titlepage.tex new file mode 100644 index 0000000..4e7e707 --- /dev/null +++ b/20-implementierungsheft/titlepage.tex @@ -0,0 +1,75 @@ +%% Encoding: UTF-8 %% + +%% titlepage.tex + +\def\usesf{} +\let\usesf\sffamily % diese Zeile auskommentieren für normalen TeX Font + +\begin{titlepage} + +\setlength{\unitlength}{1pt} +\begin{picture}(00,0)(70,770) + \includegraphics[width=\paperwidth]{assets/KIT_Deckblatt.pdf} +\end{picture} + +\thispagestyle{empty} + +\begin{center} +\hbox{} +\vfill +\includegraphics[width=.5\textwidth]{assets/logo.pdf} +\vskip 1cm +{\usesf + {\huge\bfseries PSE\textsuperscript{2} - Podcast Synchronisation \\ + made Efficient\\ + Implementierungsheft \par} +\vskip 1.8cm +{\Large Wintersemester 2022/2023\\} +%von\\[2mm] +\vskip 1.5cm + +% {\large\bfseries Vorname Nachname\\} +% \vskip 1.2cm +Praxis der Softwareentwicklung \\ +Prof. Dr.-Ing. Gregor Snelting \\ +Fakultät für Informatik\\ +Karlsruher Institut für Technologie\\ +\vskip 1.5cm +\begin{tabular}{p{20mm}l} +Autoren: +& Daniel Hönlinger \\ +& Gero Beckmann \\ +& Immanuel Reitz \\ +& Julius Friesen \\ +& Lukas Schmidheissler \\ +\\ +Betreuer: & M.Sc. Hans-Peter Lehmann \\ + & M.Sc. Daniel Seemaier +\end{tabular} +} +\end{center} +\vfill + +%\begin{textblock}{10}[0,0](4,15) +% \includegraphics[width=.3\textwidth]{logos/logo.pdf} +%\end{textblock} + +% \begin{textblock}{8}[0,0](14,14) +% \includegraphics[width=.3\textwidth]{logos/KASTEL_logo.pdf} +% \end{textblock} + +\end{titlepage} + +% \thispagestyle{empty} +% \ \vfill +% \begin{flushleft} +% Copyright $\copyright$ ITI und Verfasser 201?\\ +% \ \\ +% Institut für Theoretische Informatik +% Fakultät für Informatik\\ +% Karlsruher Institut für Technologie\\ +% Am Fasanengarten 5\\ +% 76131 Karlsruhe +% \end{flushleft} +% \newpage + diff --git a/21-implementierungsheft-kolloquium/.gitignore b/21-implementierungsheft-kolloquium/.gitignore new file mode 100644 index 0000000..e20d538 --- /dev/null +++ b/21-implementierungsheft-kolloquium/.gitignore @@ -0,0 +1,302 @@ +## Core latex/pdflatex auxiliary files: +*.aux +*.lof +*.log +*.lot +*.fls +*.out +*.toc +*.fmt +*.fot +*.cb +*.cb2 +.*.lb + +## Intermediate documents: +*.dvi +*.xdv +*-converted-to.* +# these rules might exclude image files for figures etc. +# *.ps +*.eps +*.pdf +# !assets/*.pdf + +## Generated if empty string is given at "Please type another file name for output:" +.pdf + +## Bibliography auxiliary files (bibtex/biblatex/biber): +*.bbl +*.bcf +*.blg +*-blx.aux +*-blx.bib +*.run.xml + +## Build tool auxiliary files: +*.fdb_latexmk +*.synctex +*.synctex(busy) +*.synctex.gz +*.synctex.gz(busy) +*.pdfsync + +## Build tool directories for auxiliary files +# latexrun +latex.out/ + +## Auxiliary and intermediate files from other packages: +# algorithms +*.alg +*.loa + +# achemso +acs-*.bib + +# amsthm +*.thm + +# beamer +*.nav +*.pre +*.snm +*.vrb + +# changes +*.soc + +# comment +*.cut + +# cprotect +*.cpt + +# elsarticle (documentclass of Elsevier journals) +*.spl + +# endnotes +*.ent + +# fixme +*.lox + +# feynmf/feynmp +*.mf +*.mp +*.t[1-9] +*.t[1-9][0-9] +*.tfm + +#(r)(e)ledmac/(r)(e)ledpar +*.end +*.?end +*.[1-9] +*.[1-9][0-9] +*.[1-9][0-9][0-9] +*.[1-9]R +*.[1-9][0-9]R +*.[1-9][0-9][0-9]R +*.eledsec[1-9] +*.eledsec[1-9]R +*.eledsec[1-9][0-9] +*.eledsec[1-9][0-9]R +*.eledsec[1-9][0-9][0-9] +*.eledsec[1-9][0-9][0-9]R + +# glossaries +*.acn +*.acr +*.glg +*.glo +*.gls +*.glsdefs +*.lzo +*.lzs +*.slg +*.slo +*.sls + +# uncomment this for glossaries-extra (will ignore makeindex's style files!) +*.ist + +# gnuplot +*.gnuplot +*.table + +# gnuplottex +*-gnuplottex-* + +# gregoriotex +*.gaux +*.glog +*.gtex + +# htlatex +*.4ct +*.4tc +*.idv +*.lg +*.trc +*.xref + +# hyperref +*.brf + +# knitr +*-concordance.tex +# TODO Uncomment the next line if you use knitr and want to ignore its generated tikz files +# *.tikz +*-tikzDictionary + +# listings +*.lol + +# luatexja-ruby +*.ltjruby + +# makeidx +*.idx +*.ilg +*.ind + +# minitoc +*.maf +*.mlf +*.mlt +*.mtc[0-9]* +*.slf[0-9]* +*.slt[0-9]* +*.stc[0-9]* + +# minted +_minted* +*.pyg + +# morewrites +*.mw + +# newpax +*.newpax + +# nomencl +*.nlg +*.nlo +*.nls + +# pax +*.pax + +# pdfpcnotes +*.pdfpc + +# sagetex +*.sagetex.sage +*.sagetex.py +*.sagetex.scmd + +# scrwfile +*.wrt + +# svg +svg-inkscape/ + +# sympy +*.sout +*.sympy +sympy-plots-for-*.tex/ + +# pdfcomment +*.upa +*.upb + +# pythontex +*.pytxcode +pythontex-files-*/ + +# tcolorbox +*.listing + +# thmtools +*.loe + +# TikZ & PGF +*.dpth +*.md5 +*.auxlock + +# titletoc +*.ptc + +# todonotes +*.tdo + +# vhistory +*.hst +*.ver + +# easy-todo +*.lod + +# xcolor +*.xcp + +# xmpincl +*.xmpi + +# xindy +*.xdy + +# xypic precompiled matrices and outlines +*.xyc +*.xyd + +# endfloat +*.ttt +*.fff + +# Latexian +TSWLatexianTemp* + +## Editors: +# WinEdt +*.bak +*.sav + +# Texpad +.texpadtmp + +# LyX +*.lyx~ + +# Kile +*.backup + +# gummi +.*.swp + +# KBibTeX +*~[0-9]* + +# TeXnicCenter +*.tps + +# auto folder when using emacs and auctex +./auto/* +*.el + +# expex forward references with \gathertags +*-tags.tex + +# standalone packages +*.sta + +# Makeindex log files +*.lpz + +# xwatermark package +*.xwm + +# REVTeX puts footnotes in the bibliography by default, unless the nofootinbib +# option is specified. Footnotes are the stored in a file with suffix Notes.bib. +# Uncomment the next line to have this generated file ignored. +#*Notes.bib diff --git a/21-implementierungsheft-kolloquium/.gitlab-ci.yml b/21-implementierungsheft-kolloquium/.gitlab-ci.yml new file mode 100644 index 0000000..c4ad5e0 --- /dev/null +++ b/21-implementierungsheft-kolloquium/.gitlab-ci.yml @@ -0,0 +1,37 @@ +plantuml: + stage: .pre + image: + name: eclipse-temurin:17-alpine + entrypoint: [""] + script: + - java -jar plantuml.jar -tpdf assets/diagrams/*.puml + - java -jar plantuml.jar -teps assets/*.puml + artifacts: + paths: + - assets + +tex: + stage: build + image: texlive/texlive + script: + - mkdir public + - make tex + - mv *.pdf public + artifacts: + paths: + - public + dependencies: + - plantuml + +pages: + stage: deploy + script: + - echo Hello, World! + artifacts: + paths: + - public + rules: + - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH + dependencies: + - tex + diff --git a/21-implementierungsheft-kolloquium/CHANGELOG.md b/21-implementierungsheft-kolloquium/CHANGELOG.md new file mode 100644 index 0000000..7671a41 --- /dev/null +++ b/21-implementierungsheft-kolloquium/CHANGELOG.md @@ -0,0 +1,34 @@ +# Changelog +Alle Änderungen an diesem Projekt werden in dieser Datei dokumentiert. +Die Versionsnummern folgt der Syntax in `sdqbeamer.cls`. + +## [2022-05-03 v3.1.3] +- Die Breite des Gruppennamens in der Fußzeile kann nun über `\groupnamewidth{}` gesteuert werden +- FIX: zweizeilige Fußzeilen haben nun gleichmäßigen vertikalen Abstand (Issue #16 in Gitlab) + +## [2021-08-10 v3.1.2] +- FIX: framesubtitle wird nun angezeigt (Issue #6 in Gitlab) + +## [2020-12-08 v3.1.1] +- FIX: Titelbild (Issue #4 in Gitlab) + +## [2020-12-07 v3.1] +- Umgebung ``contentblock`` (farbloser Block mit fetter Überschrift) hinzugefügt +- Farbboxen (``greenblock``, ``blueblock``, …) hinzugefügt +- Abstufungen der KIT-Farben in 10er-Schritten entsprechend der Gestaltungsrichtlinien eingeführt +- FIX: Navigationspunkte für Subsections in eine Zeile gesetzt, um vertikal Platz zu sparen +- FIX: ``inputenc`` an den Anfang von ``sdqbeamer.cls`` verschoben + +## [2020-11-16 v3.0] +- Seitenformat 16:10 hinzugefügt +- Umstellung auf KIT-Design vom 1. August 2020 + - Anpassung auf neues Farbschema und Maße + - neues Titelbild aus der KIT-Bildwelt +- Neue Optionen: + - durch `smallfoot` und `bigfoot` kann die Schriftgröße der Fußzeile gesteuert werden + - durch `navbarkit` kann eine Fußzeile nach KIT-Vorgaben erzwungen werden +- Deutsch (`de`) ist nun die Standard-Option +- Ordner `templates` wurde gelöscht und die Inhalte in `sdqbeamer.cls` integriert +- Globale Größe auf 10 pt verringert (vorher: 11 pt), da der beschreibbare Bereich im Vergleich zur 2009er Version kleiner geworden ist +- SDQ-spezifische Logos und Titelbilder entfernt. Diese sind ab sofort im Branch »sdq« verfügbar. +- Fix: Zeilenumbruch bei Titel in der Fußzeile repariert \ No newline at end of file diff --git a/21-implementierungsheft-kolloquium/Makefile b/21-implementierungsheft-kolloquium/Makefile new file mode 100644 index 0000000..3e1e58a --- /dev/null +++ b/21-implementierungsheft-kolloquium/Makefile @@ -0,0 +1,19 @@ +MAIN = presentation +FLAGS = -pdf + +all: clean compile +compile: diagram tex +clean: clean-diagram clean-tex + +dev: + latexmk $(FLAGS) -pvc $(MAIN) +tex: + latexmk $(FLAGS) $(MAIN) +diagram: + java -jar plantuml.jar -tpdf assets/diagrams/*.puml + java -jar plantuml.jar -teps assets/*.puml +clean-tex: + latexmk -C +clean-diagram: + find assets/diagrams -type f -not -name '*.puml' -delete + diff --git a/21-implementierungsheft-kolloquium/README.md b/21-implementierungsheft-kolloquium/README.md new file mode 100644 index 0000000..6a5e9ea --- /dev/null +++ b/21-implementierungsheft-kolloquium/README.md @@ -0,0 +1,118 @@ +LaTeX-Vorlage für Präsentationen +================================ + +Das vorliegende Paket dient als Vorlage für Präsentationen im [Corporate Design des KIT](https://intranet.kit.edu/gestaltungsrichtlinien.php) (Fassung vom 1. August 2020). + +Es wird an der Forschungsgruppe [DSiS](https://dsis.kastel.kit.edu) an der KIT-Fakultät für Informatik entwickelt und basiert auf [LaTeX Beamer](https://ctan.org/pkg/beamer). + +Autor: [Dr.-Ing. Erik Burger](https://dsis.kastel.kit.edu/staff_erik_burger.php) +mit Beiträgen von Christian Hammer, Klaus Krogmann und Maximilian Schambach + +Siehe https://sdqweb.ipd.kit.edu/wiki/Dokumentvorlagen + +Hinweise, Verbesserungsvorschläge +================================= + +Bitte verwenden Sie das [Issue-Tracking-System von Gitlab](https://git.scc.kit.edu/i43/dokumentvorlagen/praesentationen/beamer/-/issues), um auf Probleme mit der Vorlage hinzuweisen oder Erweiterungswünsche zu äußern. Sie können gerne auch eine Änderung per Merge-Request vorschlagen. + +Verwendung +========== + +Optionen der Dokumentklasse `sdqbeamer` +----------------------------------------- +Durch die folgenden Optionen kann das Seitenverhältnis der Folien bestimmt werden: + +| Seitenverhältnis | Option | +| ---------------- | ------------------- | +| 16:9 | `16:9` (Standard) | +| 16:10 | `16:10` | +| 4:3 | `4:3` | + +Die Schriftgröße in der Fußzeile ist standardmäßig größer gewählt, als in den Gestaltungsrichtlinien vorgegeben. Diese Vorgabe kann durch die Option `smallfoot` erzwungen werden. + +| Schriftgröße Fußzeile | Option | +| ----------------------| -------------------- | +| etwas größer (12pt) | `bigfoot` (Standard) | +| KIT-Vorgabe (9pt) | `smallfoot` | + +Die Plazierung der Navigationsleiste kann durch folgende Optionen beeinflußt werden: + +| Position | Option | Bemerkung | +| ------------------------ | ---------------- | ------------------------------------------ | +| oberhalb der Trennlinie | `navbarinline` | Standard | +| unterhalb der Trennlinie | `navbarinfooter` | keine Subsection-Punkte, Größe `smallfoot` | +| Seitenleiste links | `navbarside` | keine Subsection-Punkte | +| keine Navigationsleiste | `navbaroff` | | +| KIT-Vorgabe | `navbarkit` | entspricht `navbaroff` und `smallfoot` | + +Als Sprache sind Deutsch und Englisch verfügbar. Durch die Sprachwahl werden automatisch die passenden Logos und Formate (z.B. Datum) gewählt. + +| Sprache | | +| -------- |---------------- | +| Deutsch | `de` (Standard) | +| Englisch | `en` | + +Beispiel: `\documentclass[de,16:9,navbarinline]{sdqbeamer}` + +Titelbild +--------- + +Das Bild auf der Titelfolie kann mit dem Befehl + +`\titleimage{myimage}` (ohne Dateiendung) + +gesetzt werden. Um ein eigenes Bild zu verwenden, bitte die Datei (z.B. `myimage.jpg`) ins `logos/`-Verzeichnis legen und den Befehl anpassen. Mitgeliefert wird ein generisches Bild aus der KIT-Bildwelt (https://intranet.kit.edu/gestaltungsrichtlinien.php) in der Datei `logos/banner_2020_kit.jpg`. Falls kein Titelbild eingefügt werden soll, bitte `\titleimage{}` setzen. + +Für 16:9-Folien sollte das Verhältnis des Bildes 160:37 betragen, für 4:3-Folien 63:20. Es können auch breitere Bilder verwendet werden, da das Titelbild auf die Höhe des Rahmens skaliert und zentriert wird. + +Logo und Name Abteilung/KIT-Fakultät/Institut +--------------------------------------------- + +Das Logo rechts oben auf der Titelfolie kann mit dem folgenden Befehl gesetzt werden: + +`\grouplogo{mylogo}` (ohne Dateiendung) + +Um ein eigenes Logo zu verwenden, bitte die Datei (z.B. `mylogo.pdf`) in das Verzeichnis `logos/` legen und den Befehl anpassen. Falls kein Logo eingefügt werden soll, bitte `\grouplogo{}` setzen. + +Der Gruppenname kann mit folgendem Befehl gesetzt werden: + +`\groupname{Software Design and Quality}` + +Der Gruppenname erscheint in der Fußzeile rechts unten. Lange Namen werden in zwei Zeilen umgebrochen. Falls der Gruppenname leer gelassen wird (`\groupname{}`), wird die volle Breite der Fußzeile für Autornamen und Titel verwendet. + +Die Standardbreite des Gruppennamens sind 50 mm. Sie kann mit + +`\groupnamewidth{80mm}` + +verändert werden, wodurch sich auch die Breite des Textfeldes mit Autor und Titel entsprechend ändert. Umbrüche sind mit `\\` möglich. Statt zweizeiliger Fußzeilen empfiehlt sich eventuell die Option `smallfoot`. + +LaTeX allgemein +--------------- +Siehe https://sdqweb.ipd.kit.edu/wiki/LaTeX + +Dateistruktur +============ +`presentation.tex` +------------------ +Hauptdatei des LaTeX-Dokuments. + +`presentation.bib` +------------- +Beispieldatei für BibTeX-Referenzen +https://sdqweb.ipd.kit.edu/wiki/BibTeX-Literaturlisten + +`sdqbeamer.cls` +----------------- +Dokumentklasse für Präsentationen im KIT-Design. + +`logos/` +-------- +In diesem Verzeichnis befinden das KIT-Logo als PDF sowie das Hintergrundbild der Titelfolie als JPG. + +`CHANGELOG.md` +-------------- +Dokumentation der Änderungen in den jeweiligen Versionen. + +`README.md` +----------- +Dieser Text. diff --git a/21-implementierungsheft-kolloquium/assets/.gitignore b/21-implementierungsheft-kolloquium/assets/.gitignore new file mode 100644 index 0000000..16252a4 --- /dev/null +++ b/21-implementierungsheft-kolloquium/assets/.gitignore @@ -0,0 +1,3 @@ +diagrams/* +!diagrams/*.puml + diff --git a/21-implementierungsheft-kolloquium/assets/.gitingnore b/21-implementierungsheft-kolloquium/assets/.gitingnore new file mode 100644 index 0000000..16252a4 --- /dev/null +++ b/21-implementierungsheft-kolloquium/assets/.gitingnore @@ -0,0 +1,3 @@ +diagrams/* +!diagrams/*.puml + diff --git a/21-implementierungsheft-kolloquium/assets/commits-dashboard.png b/21-implementierungsheft-kolloquium/assets/commits-dashboard.png new file mode 100644 index 0000000..89672d6 Binary files /dev/null and b/21-implementierungsheft-kolloquium/assets/commits-dashboard.png differ diff --git a/21-implementierungsheft-kolloquium/assets/commits-server.png b/21-implementierungsheft-kolloquium/assets/commits-server.png new file mode 100644 index 0000000..a9d1a5c Binary files /dev/null and b/21-implementierungsheft-kolloquium/assets/commits-server.png differ diff --git a/21-implementierungsheft-kolloquium/assets/contributors-dashboard.png b/21-implementierungsheft-kolloquium/assets/contributors-dashboard.png new file mode 100644 index 0000000..3621c31 Binary files /dev/null and b/21-implementierungsheft-kolloquium/assets/contributors-dashboard.png differ diff --git a/21-implementierungsheft-kolloquium/assets/contributors-server.png b/21-implementierungsheft-kolloquium/assets/contributors-server.png new file mode 100644 index 0000000..500bb43 Binary files /dev/null and b/21-implementierungsheft-kolloquium/assets/contributors-server.png differ diff --git a/21-implementierungsheft-kolloquium/assets/diagrams/backendComponentDiagram.puml b/21-implementierungsheft-kolloquium/assets/diagrams/backendComponentDiagram.puml new file mode 100644 index 0000000..e382351 --- /dev/null +++ b/21-implementierungsheft-kolloquium/assets/diagrams/backendComponentDiagram.puml @@ -0,0 +1,61 @@ +@startuml +' skinparam linetype ortho + +'######################################################################### +'SubscriptionsAPI +component SubscriptionsAPI { + + component SubscriptionService + component SubscriptionController + component SubscriptionDataAccessLayer + + portout "Webserver" as wSub + portin "Database" as dSub + } + +dSub --0)- SubscriptionDataAccessLayer +SubscriptionDataAccessLayer --0)- SubscriptionService +SubscriptionService --0)- SubscriptionController +SubscriptionController --0)- wSub + +'######################################################################### + + +'######################################################################### +'EpisodeActionsAPI + +component EpisodeActionsAPI { + component EpisodeActionService + component EpisodeActionController + component EpisodeActionDataAccessLayer + + portout "Webserver" as wEpisode + portin "Database" as dEpisode +} + +dEpisode --0)- EpisodeActionDataAccessLayer +EpisodeActionController --0)- wEpisode +EpisodeActionDataAccessLayer --0)- EpisodeActionService +EpisodeActionService --0)- EpisodeActionController + +'######################################################################### + + +'######################################################################### +'AuthenticationAPI + +component AuthenticationAPI { + component AuthenticationService + component AuthenticationController + component AuthenticationDataAccessLayer + + portout "Webserver" as wAuth + portin "Database" as dAuth +} + +dAuth --0)- AuthenticationDataAccessLayer +AuthenticationController --0)- wAuth +AuthenticationDataAccessLayer --0)- AuthenticationService +AuthenticationService --0)- AuthenticationController + +@enduml diff --git a/21-implementierungsheft-kolloquium/assets/diagrams/classdiagram-authentication.puml b/21-implementierungsheft-kolloquium/assets/diagrams/classdiagram-authentication.puml new file mode 100644 index 0000000..a2b3518 --- /dev/null +++ b/21-implementierungsheft-kolloquium/assets/diagrams/classdiagram-authentication.puml @@ -0,0 +1,112 @@ +@startuml + +package authenticationAPI <> { + package authenticationDataAccessLayer <> { + ' interface AuthenticationDao { + ' String login(String username) + ' int logout(String username) + ' } + + ' class AuthenticationDataAccessService <<@Respository>> { + ' <> AuthenticationDataAccessService(JpaTemplate jpaTemplate) + ' String login(String username) + ' int logout(String username) + ' } + + interface UserDetailsManager { + void createUser(UserDetails userDetails) + void changePassword(String oldPassword, String newPassword) + void deleteUser(String username) + void updateUser(UserDetails user) + boolean userExists(String username) + } + note left + Aus org.springframework.security.provisioning + - liefert Methoden zum Erstellen neuer User + und zum Aktualisieren bestehender. + end note + + class JdbcUserDetailsManager <<@Repository>> { + <> JdbcUserDetailsManager(DataSource dataSource) + void createUser(UserDetails user) + void changePassword(String oldPassword, String newPassword) + void deleteUser(String username) + void updateUser(UserDetails user) + boolean userExists(String username) + } + note right + User Management Service aus dem Paket + org.springframework.security.provisioning + der CRUD Operationen für User bereitstellt. + Hier sind nur die relevanten Methoden modelliert. + end note + } + + package authenticationService <> { + class AuthenticationService <<@Service>> { + -- + <> AuthenticationService(UserDetailsManager userDetailsManager) + List verifyLogin(String username) + int logout(String username) + int forgotPassword(ForgotPasswordRequest forgotPasswordRequest) + .. via JdbcUserDetailsManager .. + int resetPassword(String username, RequestWithPassword requestWithPassword) + int registerUser(UserDetails user) + int changePassword(String username, ChangePasswordRequest changePasswordRequest) + int deleteUser(String username, RequestWithPassword requestWithPassword) + } + + class JavaMailSenderImpl {} + note left + Aus org.springframework.mail.javamail. + Implementierung des JavaMailSender Interfaces, + welches das MailSender Interface durch Unterstützung + von MIME Nachrichten erweitert. + Das MailSender Interface definiert dabei eine + Strategie zum Versenden einfacher Mails. + Unterstützt sowohl JavaMail MimeMessages und + Spring SimpleMailMessages. + end note + } + + package authenticationController <> { + class AuthenticationController <<@Controller>> { + <> AuthenticationController(AuthenticationService authenticationService) + ResponseEntity> verifyLogin(String username) + ResponseEntity logout(String username) + ResponseEntity forgotPassword(ForgotPasswordRequest forgotPasswordRequest) + ResponseEntity resetPassword(String username, RequestWithPassword requestWithPassword) + ResponseEntity registerUser(UserDetails user) + ResponseEntity changePassword(String username, ChangePasswordRequest changePasswordRequest) + ResponseEntity deleteUser(String username, RequestWithPassword requestWithPassword) + } + + class ChangePasswordRequest { + <> ChangePasswordRequest(String oldPassword, String newPassword) + String getOldPassword() + String getNewPassword() + } + + class ForgotPasswordRequest { + <> ForgotPasswordRequest(String email) + String getEmail() + } + + class RequestWithPassword { + <> ResetPasswordRequest(String password) + String getPassword() + } + } +} + +' User <.. AuthenticationDataAccessService: DB +' User <.. JdbcUserDetailsManager: DB +UserDetailsManager <.. AuthenticationService: <> +' AuthenticationDao <.. AuthenticationService: <> +AuthenticationService --o AuthenticationController +' AuthenticationDao <|. AuthenticationDataAccessService: <> +UserDetailsManager <|. JdbcUserDetailsManager: <> +JavaMailSenderImpl <. AuthenticationService: <> + +@enduml + diff --git a/21-implementierungsheft-kolloquium/assets/diagrams/classdiagram-episode-actions.puml b/21-implementierungsheft-kolloquium/assets/diagrams/classdiagram-episode-actions.puml new file mode 100644 index 0000000..7a4530e --- /dev/null +++ b/21-implementierungsheft-kolloquium/assets/diagrams/classdiagram-episode-actions.puml @@ -0,0 +1,84 @@ +@startuml + +package episodeActionsAPI <> { + package episodeActionDataAccessLayer <> { + class EpisodeActionDataAccessService <<@Repository>> { + <> EpisodeActionDataAccessService (JpaTemplate jpaTemplate) + long addEpisodeActions(String username, List episodeActionPosts) + List getEpisodeActions(String username) + List getEpisodeActionsOfPodcast(String username, String podcastURL) + List getEpisodeActionsSince(String username, LocalDateTime since) + List getEpisodeActionsOfPodcastSince(String username, String podcastURL, LocalDateTime since) + } + + interface EpisodeActionDao { + long addEpisodeActions(String username, List episodeActionPosts) + List getEpisodeActions(String username) + List getEpisodeActionsOfPodcast(String username, String podcastURL) + List getEpisodeActionsSince(String username, LocalDateTime since) + List getEpisodeActionsOfPodcastSince(String username, String podcastURL, LocalDateTime since) + } + } + + package episodeActionService <> { + class EpisodeActionService <<@Service>> { + <> EpisodeActionService (EpisodeActionDao episodeActionDao) + LocalDateTime addEpisodeActions(String username, List episodeActionPosts) + List getEpisodeActions(String username) + List getEpisodeActionsOfPodcast(String username, String podcastURL) + List getEpisodeActionsSince(String username, LocalDateTime since) + List getEpisodeActionsOfPodcastSince(String username, String podcastURL, LocalDateTime since) + } + } + + package episodeActionController <> { + class EpisodeActionController <<@Controller>>{ + <> EpisodeActionController (EpisodeActionService episodeActionService) + ResponseEntity addEpisodeActions(String username, EpisodeActionPostRequest episodeActionPostRequest) + ResponseEntity getEpisodeActions(String username, String deviceID, boolean aggregated) + ResponseEntity getEpisodeActionsOfPodcast(String username, String podcastURL, String deviceID, boolean aggregated) + ResponseEntity getEpisodeActionsSince(String username, String deviceID, long since, boolean aggregated) + ResponseEntity getEpisodeActionsOfPodcastSince(String username, String podcastURL, String deviceID, long since, boolean aggregated) + } + + class EpisodeActionPostResponse { + <> EpisodeActionPostResponse(List> updateURLs) + long getTimestamp() + List> getUpdatedURLs() + } + + class EpisodeActionPost { + <> EpisodeActionPost(String podcastURL, String episodeURL, Action action, LocalDateTime timestamp, int started, int position) + String getPodcastURL() + String getEpisodeURL() + int getGUID() + Action getAction() + LocalDateTime getTimestamp() + int getStarted() + int getPosition() + EpisodeAction getEpisodeAction() + } + + class EpisodeActionPostRequest { + <> EpisodeActionPostRequest(List episodeActionPosts) + List getEpisodeActionPosts() + } + + class EpisodeActionGetResponse { + <> EpisodeActionGetResponse(List episodeActionPosts) + List getEpisodeActionPosts() + long getTimestamp() + } + } +} + +EpisodeActionPost -o EpisodeActionGetResponse +EpisodeActionPost -o EpisodeActionPostRequest +' EpisodeAction <.. EpisodeActionDataAccessService: DB +' Episode <.. EpisodeActionDataAccessService: DB +EpisodeActionDao <.. EpisodeActionService: <> +EpisodeActionService --o EpisodeActionController +EpisodeActionDao <|. EpisodeActionDataAccessService: <> + +@enduml + diff --git a/21-implementierungsheft-kolloquium/assets/diagrams/classdiagram-model.puml b/21-implementierungsheft-kolloquium/assets/diagrams/classdiagram-model.puml new file mode 100644 index 0000000..72ad49f --- /dev/null +++ b/21-implementierungsheft-kolloquium/assets/diagrams/classdiagram-model.puml @@ -0,0 +1,109 @@ +@startuml + +package model <> { + class Subscription { + <> Subscription(String url, String title) + int getID() + String getURL() + long getLastActionTimestamp() + String getTitle() + } + + class SubscriptionAction { + <> SubscriptionAction(int userID, int subscriptionID) + int getID() + int getUserID() + int getSubscriptionID() + long getTimestamp() + boolean getAdded() + } + + class Episode { + <> Episode(int subscriptionID, int id, String url, String title, String thumbnailURL, int total) + int getSubscriptionID() + int getID() + int getGUID() + String getURL() + String getTitle() + int getTotal() + } + + enum Action { + Download + Play + Delete + New + Flattr + String getJsonProperty() + } + + class EpisodeAction { + <> EpisodeAction(Action action, LocalDateTime timestamp, int started, int position) + int getEpisodeID() + Action getAction() + long getTimestamp() + int getStarted() + int getPosition() + void setEpisodeID() + EpisodeActionPost getEpisodeActionPost(String podcastURL, String episodeURL) + } + + interface UserDetails { + String getUsername() + String getPassword() + Collection getAuthorities() + boolean isAccountExpired() + boolean isAccountLocked() + boolean isCredentialsNonExpired() + boolean isEnabled() + } + note left + Aus org.springframework.security.core.userdetails. + Wird für die Schnittstelle UserDetailsManager benötigt. + Stellt wichtige Informationen eines Users bereit. + Diese werden nur indirekt von Spring Security + benutzt, indem sie vorher in Authentication Objekten + gekapselt werden. + end note + + class User { + -- + <> User(String username, String password) + int getID() + String getSessionToken() + boolean getEmailIsValidated() + .. interface methods .. + String getUsername() + String getPassword() + Collection getAuthorities() + boolean isAccountExpired() + boolean isAccountLocked() + boolean isCredentialsNonExpired() + boolean isEnabled() + } + + interface GrantedAuthority { + String getAuthority() + } + note right + Aus org.springframework.security.core. + Wird für die Schnittstelle UserDetails benötigt. + Repräsentiert eine Autorisierung, die einem + Authentication Objekt gewährt wird. + end note + + class Authority { + <> Authority() + String getAuthority() + } +} + +Subscription <. SubscriptionAction: ID +Action <-- EpisodeAction +EpisodeAction .> Episode: ID +UserDetails <|.. User: <> +User -> Authority +GrantedAuthority <|.. Authority: <> + +@enduml + diff --git a/21-implementierungsheft-kolloquium/assets/diagrams/classdiagram-subscriptions.puml b/21-implementierungsheft-kolloquium/assets/diagrams/classdiagram-subscriptions.puml new file mode 100644 index 0000000..432f185 --- /dev/null +++ b/21-implementierungsheft-kolloquium/assets/diagrams/classdiagram-subscriptions.puml @@ -0,0 +1,75 @@ +@startuml + +package subscriptionsAPI <> { + package subscriptionDataAccessLayer <> { + class SubscriptionDataAccessService <<@Repository>> { + <> SubscriptionDataAccessService(JpaTemplate jpaTemplate) + int uploadSubscriptions(String username, List subscriptions) + List getSubscriptions(String username) + List getSubscriptionsSince(String username, LocalDateTime time) + int addSubscriptions(String username, List addedSubscriptions) + int removeSubscriptions(String username, List removedSubscriptions) + List getTitles(String username) + } + + interface SubscriptionDao { + int uploadSubscriptions(String username, List subscriptions) + List getSubscriptions(String username) + List getSubscriptionsSince(String username, LocalDateTime time) + int addSubscriptions(String username, List addedSubscriptions) + int removeSubscriptions(String username, List removedSubscriptions) + List getTitles(String username) + } + } + + package subscriptionService <> { + class SubscriptionService <<@Service>> { + <> SubscriptionService(SubscriptionDao subscriptionDao) + int uploadSubscriptions(String username, List subscriptions) + List getSubscriptions(String username) + List getSubscriptionsSince(String username, LocalDateTime time) + int addSubscriptions(String username, List addedSubscriptions) + int removeSubscriptions(String username, List removedSubscriptions) + List getTitles(String username) + } + } + + package subscriptionController <> { + class SubscriptionController <<@Controller>>{ + ' @Autowired + <> SubscriptionController(SubscriptionService subscriptionService) + ' @GetMapping + ResponseEntity> getSubscriptions(String username, String deviceID, String functionJSONP) + ' @PutMapping + ResponseEntity uploadSubscriptions(String username, String deviceID, List subscriptions) + ' @PostMapping + ResponseEntity applySubscriptionDelta(String username, String deviceID, SubscriptionDelta delta) + ' @GetMapping + ResponseEntity getSubscriptionDelta(String username, String deviceID, long since) + ResponseEntity> getTitles(String username, String deviceID) + } + + class SubscriptionTitles { + <> SubscriptionTitles(Subscription subscription, List episodeTitles) + Subscription getSubscription() + List getEpisodesTitles() + } + + class SubscriptionDelta { + <> SubscriptionDelta(List add, List remove) + List getRemove() + LocalDate getTimestamp() + List> getUpdate_urls() + } + } + +} + +' Subscription <.. SubscriptionDataAccessService: DB +' SubscriptionAction <.. SubscriptionDataAccessService: DB +SubscriptionService --o SubscriptionController +SubscriptionDao <.. SubscriptionService: <> +SubscriptionDao <|. SubscriptionDataAccessService: <> + +@enduml + diff --git a/21-implementierungsheft-kolloquium/assets/diagrams/classdiagram-util.puml b/21-implementierungsheft-kolloquium/assets/diagrams/classdiagram-util.puml new file mode 100644 index 0000000..03dfc9a --- /dev/null +++ b/21-implementierungsheft-kolloquium/assets/diagrams/classdiagram-util.puml @@ -0,0 +1,43 @@ +@startuml + +package util <> { + class RSSParser { + <> RSSParser(String subscriptionURL) + String getSubscriptionTitle() + List getEpisodes() + Episode getEpisodeForURL(String episodeURL) + } + note bottom + Verwendet intern Spring um + HTTP-Anfragen zu erstellen. + end note + + class CleanCronJob { + <> CleanCronJob(JdbcUserDetailsManager jdbcUserDetailsManager) + void cleanInvalidUsers() + } + note bottom + Hintergrundservice, der in periodischen Abständen + Nutzer, die ihre E-Mail-Adresse nicht nach 24 Stunden + bestätigt haben, wieder aus der Datenbank löscht. + (Auf die Assoziation zu JdbcUserDetailsManager wird + im Sinne der Übersichtlichkeit verzichtet.) + end note + + class ResponseEntity { + <> ResponseEntity(T body, HttpStatusCode status) + T getBody() + HttpStatusCode getStatusCode() + } + note bottom + Aus org.springframework.http. + Erweitert die Klasse HttpEntity, welche + ein HTTP Anfrage- oder Antwort-Objekt + repräsentiert, durch einen HttpStatusCode. + Wird von den Controller-Methoden als + Rückgabewert verwendet. + end note +} + +@enduml + diff --git a/21-implementierungsheft-kolloquium/assets/diagrams/classdiagram.puml b/21-implementierungsheft-kolloquium/assets/diagrams/classdiagram.puml new file mode 100644 index 0000000..f833aa2 --- /dev/null +++ b/21-implementierungsheft-kolloquium/assets/diagrams/classdiagram.puml @@ -0,0 +1,68 @@ +@startuml +' skinparam linetype ortho +' skinparam groupInheritance 2 +allowmixing + +!include classdiagram-authentication.puml +!include classdiagram-episode-actions.puml +!include classdiagram-model.puml +!include classdiagram-subscriptions.puml +!include classdiagram-util.puml + +class SecurityConfigurationBasicAuth { + <> SecurityConfigurationBasicAuth() + PasswordEncoder encoder() + UserDetailsManager userDetailsService() + SecuryFilterChain fiterChain(HTTPSecurity http) throws Excpetion +} +note top + Erstellt einen Servlet Filter (springSecurityFilterChain) + der für die gesamte Sicherheit zuständig ist (Schutz der URLs, + Validierung von Anmeldedaten, Weiterleitung zur Anmeldung, etc.). +end note + +class PSEApplication { + <> PSEApplication() + void main(String[] args) +} + +database Datenbank +Datenbank <-[hidden]d- subscriptionsAPI +Datenbank <-[hidden]d- episodeActionsAPI +Datenbank <-[hidden]d- authenticationAPI +() SQL as SQLSub +() SQL as SQLAuth +() SQL as SQLEpisode + +Datenbank -- SQLSub +Datenbank -- SQLAuth +Datenbank -- SQLEpisode + +Subscription --o SubscriptionTitles +EpisodeActionPost -o SubscriptionTitles +UserDetailsManager <.. SecurityConfigurationBasicAuth: <> + +SubscriptionController ..o PSEApplication +AuthenticationController ..o PSEApplication +EpisodeActionController ..o PSEApplication +SecurityConfigurationBasicAuth ..o PSEApplication + +PSEApplication --() HTTP + +SQLSub )-- SubscriptionDataAccessService: JPA +' SQLAuth )-- AuthenticationDataAccessService: JPA +SQLAuth )-- JdbcUserDetailsManager: JDBC +SQLEpisode )-- EpisodeActionDataAccessService: JPA + +RSSParser <. SubscriptionDataAccessService: <> +RSSParser <. EpisodeActionDataAccessService: <> +' JdbcUserDetailsManager <-- CleanCronJob + +model .o Datenbank: ORM (User, SubscriptionAction, Subscription, EpisodeAction, Episode) +' Datenbank o.. Subscription: ORM +' Datenbank o.. SubscriptionAction: ORM +' Datenbank o.. Episode: ORM +' Datenbank o.. EpisodeAction: ORM +' Datenbank o.. User: ORM + +@enduml diff --git a/21-implementierungsheft-kolloquium/assets/diagrams/componentdiagram.puml b/21-implementierungsheft-kolloquium/assets/diagrams/componentdiagram.puml new file mode 100644 index 0000000..dea4a1d --- /dev/null +++ b/21-implementierungsheft-kolloquium/assets/diagrams/componentdiagram.puml @@ -0,0 +1,53 @@ +@startuml + +[App] as app +[VueRouter] as router +[NavbarComponent] as navbar +[LoginPage] as login_page +[SubscriptionsPage] as abo_page +[EpisodesPage] as episodes_page +[SettingsPage] as settings_page +[ForgotPasswordPage] as forgot_page +[ResetPasswordPage] as reset_page +note top + Wird in der E-Mail zum Zurücksetzen des Passworts mit dem JWT-Token verlinkt. + Sendet das alte und neue Passwort und den JWT an die API. +end note +[RegistrationPage] as registration_page + +[SubscriptionComponent] as sub +[EpisodeComponent] as episode +[LastUpdateComponent] as last_update +[PasswordValidatorComponent] as password + +app --> router + +app --> navbar +router --> login_page +router --> forgot_page +router --> reset_page +router --> registration_page +router --> abo_page +router --> episodes_page +router --> settings_page + +navbar -[hidden] router + +episodes_page -[hidden] abo_page +login_page -[hidden] forgot_page +registration_page -[hidden] reset_page +abo_page -[hidden] settings_page +forgot_page -[hidden] episodes_page +' forgot_page -[hidden] settings_page + +abo_page --> sub +episodes_page --> episode + +sub --> last_update +episode --> last_update + +settings_page --> password +reset_page --> password +registration_page --> password + +@enduml diff --git a/21-implementierungsheft-kolloquium/assets/diagrams/db.puml b/21-implementierungsheft-kolloquium/assets/diagrams/db.puml new file mode 100644 index 0000000..bdefaea --- /dev/null +++ b/21-implementierungsheft-kolloquium/assets/diagrams/db.puml @@ -0,0 +1,78 @@ +@startuml +' Type Symbol +' Zero or One |o-- +' Exactly One ||-- +' Zero or Many }o-- +' One or Many }|-- + +skinparam linetype ortho + +entity User { + * int id <> + * String email + * String password + * boolean verified + * long created_at +} + +entity SubscriptionAction { + * int id <> + * int user_id + * long timestamp + * int subscription_id + * boolean added +} + +entity Subscription { + * int id <> + * String url + * long timestamp + * String title +} + +entity Episode { + * int id <> + * int guid <> + * String url + * String title + * int total + * int subscription_id +} +note right + Wenn der Client eine GUID aus dem Feed mitsendet, wird + diese statt der URL verwendet um die Episode zu finden. + So wird die Episode auch noch gefunden, nachdem sich + die URL geändert hat. +end note +note bottom of Episode + Wenn für die Episoden-URL einer EpisodeAction noch keine Episode in der Datenbank steht, + dann schreibe dafür ein Dummy-Objekt in Datenbank und lade asynchron die Episoden der Subscription. + Ersetze dann die Dummy-Objekte durch die Episoden und setze den Timestamp der Subscription auf + die aktuelle Zeit. + Um DoS-Angriffe auf den Backend-Server abzuwenden, können die Episoden einer Subscription erst + nach einer Stunde erneut gefetched werden. Bis dahin werden für EpisodeActions, die sich auf noch + nicht geladene Episoden beziehen, nur Dummy-Objekte für die Episoden in die Datenbank geschrieben. + Es sei noch darauf hingewiesen, dass diese Dummy-Episoden bei Anfragen nicht mit ausgegeben werden. +end note + +entity EpisodeAction { + * int id <> + * int user_id + * int episode_id + * long timestamp + * int action + * int started + * int position +} +note right + Speichere für jede Episode + nur letzte Play-Action. +endnote + +User ||--o{ EpisodeAction +User ||--o{ SubscriptionAction +SubscriptionAction }|--|| Subscription +EpisodeAction }|--|| Episode +Subscription ||-right-|{ Episode + +@enduml diff --git a/21-implementierungsheft-kolloquium/assets/diagrams/deployment.puml b/21-implementierungsheft-kolloquium/assets/diagrams/deployment.puml new file mode 100644 index 0000000..b8d0491 --- /dev/null +++ b/21-implementierungsheft-kolloquium/assets/diagrams/deployment.puml @@ -0,0 +1,59 @@ +@startuml + +node "<> \nBackend Server" as backendServer{ + database " <> \n MariaDB Server 10.6" as database { + rectangle rectangle1 [ + <> + User + ] + rectangle rectangle2 [ + <> + SubscriptionAction + ] + rectangle rectangle3 [ + <> + EpisodeAction + ] + rectangle rectangle4 [ + <> + Subscription + ] + rectangle rectangle5 [ + <> + Episode + ] + } + + node "<> \nJava Spring" as javaSpring{ + node " <> \n Tomcat Webserver" + } +} + +node "<> \nFrontend" as frontendServer { + +} + +node "<> \nEndgerät" as terminal { + node "<> \nBrowser" as browser + node "<> \nPodcatcher" as podcatcher +} + +backendServer "1" - "*" podcatcher + +node "<> \nFrontend Server" as frontendServer{ + node "<> \nVue.js" as vuejs { + + } +} + +podcatcher -[hidden] browser + +backendServer - "1" frontendServer + +database "1" -- "1" javaSpring + +browser "*" -- frontendServer + + + +@enduml diff --git a/21-implementierungsheft-kolloquium/assets/diagrams/sequencediagram-forgotAndResetPW.puml b/21-implementierungsheft-kolloquium/assets/diagrams/sequencediagram-forgotAndResetPW.puml new file mode 100644 index 0000000..603130c --- /dev/null +++ b/21-implementierungsheft-kolloquium/assets/diagrams/sequencediagram-forgotAndResetPW.puml @@ -0,0 +1,41 @@ +@startuml + +skinparam ParticipantPadding 30 + +participant AuthenticationController << (C, #ADD1B2) @Controller >> +-> AuthenticationController: ""POST /api/2/auth/forgot.json"" \n//@RequestBody ForgotPasswordRequest forgotPasswordRequest// \n\n-> forgotPassword(//forgotPasswordRequest//) +activate AuthenticationController +participant AuthenticationService << (C, #ADD1B2) @Service >> +AuthenticationController -> AuthenticationService: forgotPassword(//forgotPasswordRequest//) +activate AuthenticationService +participant JavaMailSenderImpl << (C, #ADD1B2) >> +AuthenticationService -> JavaMailSenderImpl: create link to reset password with JWT as URL parameter \n-> send(SimpleMailMessage simpleMessage) with link +activate JavaMailSenderImpl +<<- JavaMailSenderImpl: sends email with link containing a JWT to reset password +JavaMailSenderImpl --> AuthenticationService +deactivate JavaMailSenderImpl +AuthenticationService --> AuthenticationController: int indicating status +deactivate AuthenticationService +<-- AuthenticationController: ResponseEntity indicating status \n\n-> ""HTTP status code"" +deactivate AuthenticationController +||60|| +-> AuthenticationController: ""PUT /api/2/auth/{username}/resetpassword.json"" \n//@RequestParam String jwt// \n//@RequestBody ResetPasswordRequest resetPasswordRequest// \n\n-> login user (""username"") via JWT (//jwt//) \n-> resetPassword(""username"", //resetPasswordRequest//) +activate AuthenticationController +AuthenticationController -> AuthenticationService: resetPassword(""username"", //resetPasswordRequest//) +activate AuthenticationService +participant JdbcUserDetailsManager << (C, #ADD1B2) @Repository >> +AuthenticationService -> JdbcUserDetailsManager: String oldPassword = //resetPasswordRequest//.getOldPassword() \nString newPassword = //resetPasswordRequest//.getNewPassword() \n-> changePassword(newPassword, oldPassword) +activate JdbcUserDetailsManager +database Database +JdbcUserDetailsManager -> Database: change password of logged in user +activate Database +Database --> JdbcUserDetailsManager +deactivate Database +JdbcUserDetailsManager --> AuthenticationService: int indicating status +deactivate JdbcUserDetailsManager +AuthenticationService --> AuthenticationController: int indicating status +deactivate AuthenticationService +<-- AuthenticationController: ResponseEntity indicating status \n\n-> ""HTTP status code"" +deactivate AuthenticationController + +@enduml \ No newline at end of file diff --git a/21-implementierungsheft-kolloquium/assets/diagrams/sequencediagram-getEpisodeActions.puml b/21-implementierungsheft-kolloquium/assets/diagrams/sequencediagram-getEpisodeActions.puml new file mode 100644 index 0000000..47497d5 --- /dev/null +++ b/21-implementierungsheft-kolloquium/assets/diagrams/sequencediagram-getEpisodeActions.puml @@ -0,0 +1,38 @@ +@startuml + +' title =**Get All Episode Actions** + +participant EpisodeActionController << (C, #ADD1B2) @Controller >> +-> EpisodeActionController: ""GET /api/2/episodes/{username}.json"" \n//@RequestParam("device") String deviceID// \n//@RequestParam("aggregated") boolean aggregated// \n\n-> getEpisodeActions(""username"", //deviceID//, //aggregated//) +note right + Die Parameter //deviceID// und //aggregated// werden ignoriert, + da nicht zwischen Geräten unterschieden und für jede + Episode sowieso nur die letzte Play-Action gespeichert + wird. Dies gilt für alle GET-Anfragen der Episode Actions API. +end note +activate EpisodeActionController +participant EpisodeActionService << (C, #ADD1B2) @Service >> +EpisodeActionController -> EpisodeActionService: getEpisodeActions(""username"") +activate EpisodeActionService +participant EpisodeActionDataAccessService << (C, #ADD1B2) @Repository >> +EpisodeActionService -> EpisodeActionDataAccessService: getEpisodeActions(""username"") +activate EpisodeActionDataAccessService +EpisodeActionDataAccessService -> EpisodeActionDataAccessService: getEpisodeActionsSince(""username"", \nLocalDateTime.MIN.toEpochSecond(ZoneOffset.UTC)) +database Database +activate EpisodeActionDataAccessService +EpisodeActionDataAccessService -> Database: get all EpisodeActions for all subscribed podcasts +activate Database +Database --> EpisodeActionDataAccessService: List selectedEpisodeActions \n-> then remove all older than LocalDateTime.MIN (none) +EpisodeActionDataAccessService -> Database: join EpisodeActions in selectedEpisodeActions with episodeURL of Episode +Database --> EpisodeActionDataAccessService +deactivate Database +EpisodeActionDataAccessService --> EpisodeActionDataAccessService: List episodeActionPosts +deactivate EpisodeActionDataAccessService +EpisodeActionDataAccessService --> EpisodeActionService: List episodeActionPosts +deactivate EpisodeActionDataAccessService +EpisodeActionService --> EpisodeActionController: List episodeActionPosts +deactivate EpisodeActionService +<-- EpisodeActionController: ResponseEntity response \n\n-> ""HTTP status code"" \n-> ""JSON"" +deactivate EpisodeActionController + +@enduml \ No newline at end of file diff --git a/21-implementierungsheft-kolloquium/assets/diagrams/sequencediagram-getEpisodeActionsOfPodcastSince.puml b/21-implementierungsheft-kolloquium/assets/diagrams/sequencediagram-getEpisodeActionsOfPodcastSince.puml new file mode 100644 index 0000000..d8797d1 --- /dev/null +++ b/21-implementierungsheft-kolloquium/assets/diagrams/sequencediagram-getEpisodeActionsOfPodcastSince.puml @@ -0,0 +1,32 @@ +@startuml + +' title =**Get Episode Actions of Podcast Since** + +participant EpisodeActionController << (C, #ADD1B2) @Controller >> +-> EpisodeActionController: ""GET /api/2/episodes/{username}.json"" \n//@RequestParam("podcast") String podcastURL// \n//@RequestParam("device") String deviceID// \n//@RequestParam("since") long since// \n//@RequestParam("aggregated") boolean aggregated// \n\n-> getEpisodeActionsOfPodcastSince(""username"", //podcastURL//, //deviceID//, //since//, //aggregated//) +note right + Die Parameter //deviceID// und //aggregated// werden ignoriert. + Siehe Notiz in Sequenzdiagramm **Get All Episode Actions**. +end note +activate EpisodeActionController +participant EpisodeActionService << (C, #ADD1B2) @Service >> +EpisodeActionController -> EpisodeActionService: getEpisodeActionsOfPodcastSince(""username"", //podcastURL//, //since//) +activate EpisodeActionService +participant EpisodeActionDataAccessService << (C, #ADD1B2) @Repository >> +EpisodeActionService -> EpisodeActionDataAccessService: getEpisodeActionsOfPodcastSince(""username"", //podcastURL//, //since//) +activate EpisodeActionDataAccessService +database Database +EpisodeActionDataAccessService -> Database: get all EpisodeActions the given podcast (//podcastURL//) +activate Database +Database --> EpisodeActionDataAccessService: List selectedEpisodeActions \n-> then remove all older than //since// +EpisodeActionDataAccessService -> Database: join EpisodeActions in selectedEpisodeActions with episodeURL of Episode +Database --> EpisodeActionDataAccessService +deactivate Database +EpisodeActionDataAccessService --> EpisodeActionService: List episodeActionPosts +deactivate EpisodeActionDataAccessService +EpisodeActionService --> EpisodeActionController: List episodeActionPosts +deactivate EpisodeActionService +<-- EpisodeActionController: ResponseEntity response \n\n-> ""HTTP status code"" \n-> ""JSON"" +deactivate EpisodeActionController + +@enduml \ No newline at end of file diff --git a/21-implementierungsheft-kolloquium/assets/diagrams/sequencediagram-getSubscriptions.puml b/21-implementierungsheft-kolloquium/assets/diagrams/sequencediagram-getSubscriptions.puml new file mode 100644 index 0000000..4d8ab90 --- /dev/null +++ b/21-implementierungsheft-kolloquium/assets/diagrams/sequencediagram-getSubscriptions.puml @@ -0,0 +1,38 @@ +@startuml + +' title =**Get All Subscriptions** + +participant SubscriptionController << (C, #ADD1B2) @Controller >> +-> SubscriptionController: ""GET /subscriptions/{username}.json"" \n"" /subscriptions/{username}/{deviceid}.json"" \n//@RequestParam("jsonp") String functionJSONP// \n\n-> getSubscriptions(""username"", ""deviceid"", //functionJSONP//) +activate SubscriptionController +note right + Die Parameter ""deviceid"" und + //functionJSONP// werden ignoriert, + da nicht zwischen Geräten unterschieden + und JSONP nicht unterstützt wird. +end note +participant SubscriptionService << (C, #ADD1B2) @Service >> +SubscriptionController -> SubscriptionService: getSubscriptions(""username"") +activate SubscriptionService +participant SubscriptionDataAccessService << (C, #ADD1B2) @Repository >> +SubscriptionService -> SubscriptionDataAccessService: getSubscriptions(""username"") +activate SubscriptionDataAccessService +SubscriptionDataAccessService -> SubscriptionDataAccessService: getSubscriptionsSince(""username"", LocalDateTime.MIN) +database Database +activate SubscriptionDataAccessService +SubscriptionDataAccessService -> Database: get all Subscriptions for ""username"" +activate Database +Database --> SubscriptionDataAccessService: List subscriptions +SubscriptionDataAccessService -> Database: get Podcasts from Subscriptions +Database --> SubscriptionDataAccessService: List subscribedPodcasts +deactivate Database +SubscriptionDataAccessService --> SubscriptionDataAccessService: List podcastURLs +deactivate SubscriptionDataAccessService +SubscriptionDataAccessService --> SubscriptionService: List podcastURLs +deactivate SubscriptionDataAccessService +SubscriptionService --> SubscriptionController: List podcastURLs +deactivate SubscriptionService +<-- SubscriptionController: ResponseEntity> podcastURLs \n \n-> ""HTTP status code"" \n-> ""JSON"" +deactivate SubscriptionController + +@enduml \ No newline at end of file diff --git a/21-implementierungsheft-kolloquium/assets/diagrams/sequencediagram-register.puml b/21-implementierungsheft-kolloquium/assets/diagrams/sequencediagram-register.puml new file mode 100644 index 0000000..b7b7aa1 --- /dev/null +++ b/21-implementierungsheft-kolloquium/assets/diagrams/sequencediagram-register.puml @@ -0,0 +1,26 @@ +@startuml + +' title =**Register** + +participant AuthenticationController << (C, #ADD1B2) @Controller >> +-> AuthenticationController: ""POST /api/2/auth/register.json"" \n//@RequestBody UserDetails user// \n\n-> registerUser(//user//) +activate AuthenticationController +participant AuthenticationService << (C, #ADD1B2) @Service >> +AuthenticationController -> AuthenticationService: registerUser(//user//) +activate AuthenticationService +participant JdbcUserDetailsManager << (C, #ADD1B2) @Repository >> +AuthenticationService -> JdbcUserDetailsManager: createUser(//user//) +activate JdbcUserDetailsManager +database Database +JdbcUserDetailsManager -> Database: create new User with given UserDetails (//user//) +activate Database +Database --> JdbcUserDetailsManager +deactivate Database +JdbcUserDetailsManager --> AuthenticationService: int indicating status +deactivate JdbcUserDetailsManager +AuthenticationService --> AuthenticationController: int indicating status +deactivate AuthenticationService +<-- AuthenticationController: ResponseEntity indicating status \n\n-> ""HTTP status code"" +deactivate AuthenticationController + +@enduml \ No newline at end of file diff --git a/21-implementierungsheft-kolloquium/assets/diagrams/sequencediagram-uploadEpisodeActions.puml b/21-implementierungsheft-kolloquium/assets/diagrams/sequencediagram-uploadEpisodeActions.puml new file mode 100644 index 0000000..d3dac57 --- /dev/null +++ b/21-implementierungsheft-kolloquium/assets/diagrams/sequencediagram-uploadEpisodeActions.puml @@ -0,0 +1,38 @@ +@startuml + +' title =**Upload Episode Actions** + +participant EpisodeActionController << (C, #ADD1B2) @Controller >> +-> EpisodeActionController: ""POST /api/2/episodes/{username}.json"" \n//@RequestBody EpisodeActionPostRequest episodeActionPostRequest// \n\n-> addEpisodeActions(""username"", //episodeActionPostRequest//) +activate EpisodeActionController +participant EpisodeActionService << (C, #ADD1B2) @Service >> +EpisodeActionController -> EpisodeActionService: addEpisodeActions(""username"", \nepisodeActionPosts = //episodeActionPostRequest//.getEpisodeActionPosts()) +activate EpisodeActionService +participant EpisodeActionDataAccessService << (C, #ADD1B2) @Repository >> +EpisodeActionService -> EpisodeActionDataAccessService: addEpisodeActions(""username"", episodeActionPosts) +database Database +activate EpisodeActionDataAccessService +loop for each EpisodeActionPost in episodeActionPosts -> episodeAction = episodeActionPost.getEpisodeAction() +opt episodeAction.getAction().equals(Action.PLAY) +EpisodeActionDataAccessService -> Database: set episodeID field of episodeAction for this ""username"" via podcastURL and episodeURL +activate Database +Database --> EpisodeActionDataAccessService +EpisodeActionDataAccessService -> Database: get last EpisodeAction with this episodeID if present +Database --> EpisodeActionDataAccessService: Optional lastEpisodeAction +opt lastEpisodeAction.isPresent() +EpisodeActionDataAccessService -> Database: replace lastEpisodeAction with episodeAction +else else +EpisodeActionDataAccessService -> Database: add episodeAction to DB as new entry +end +Database --> EpisodeActionDataAccessService +deactivate Database +end +end +EpisodeActionDataAccessService --> EpisodeActionService: long latestTimestamp +deactivate EpisodeActionDataAccessService +EpisodeActionService --> EpisodeActionController: LocalDateTime timestamp = LocalDateTime.ofEpochSecond(latestTimestamp, 0, ZoneOffset.UTC) +deactivate EpisodeActionService +<-- EpisodeActionController: ResponseEntity \n(with empty list for updateURLs) \n\n-> ""HTTP status code"" \n-> ""JSON"" +deactivate EpisodeActionController + +@enduml \ No newline at end of file diff --git a/21-implementierungsheft-kolloquium/assets/diagrams/sequencediagram-uploadSubscriptions.puml b/21-implementierungsheft-kolloquium/assets/diagrams/sequencediagram-uploadSubscriptions.puml new file mode 100644 index 0000000..1edc8cf --- /dev/null +++ b/21-implementierungsheft-kolloquium/assets/diagrams/sequencediagram-uploadSubscriptions.puml @@ -0,0 +1,32 @@ +@startuml + +' title =**Upload Subscriptions** + +participant SubscriptionController << (C, #ADD1B2) @Controller >> +-> SubscriptionController: ""PUT /subscriptions/{username}/{deviceid}.json"" \n//@RequestBody List subscriptions// \n\n-> uploadSubscriptions(""username"", ""deviceid"", //subscriptions//) +activate SubscriptionController +participant SubscriptionService << (C, #ADD1B2) @Service >> +SubscriptionController -> SubscriptionService: uploadSubscriptions(""username"", //subscriptions//) +activate SubscriptionService +participant SubscriptionDataAccessService << (C, #ADD1B2) @Repository >> +SubscriptionService -> SubscriptionDataAccessService: uploadSubscriptions(""username"", //subscriptions//) +activate SubscriptionDataAccessService +database Database +SubscriptionDataAccessService -> Database: delete all subsciptions of ""username"" +activate Database +Database --> SubscriptionDataAccessService +SubscriptionDataAccessService -> SubscriptionDataAccessService: addSubscriptions(""username"", //subscriptions//) +activate SubscriptionDataAccessService +SubscriptionDataAccessService -> Database: upload all subscriptions (//subscriptions//) for ""username"" +Database --> SubscriptionDataAccessService +deactivate Database +SubscriptionDataAccessService --> SubscriptionDataAccessService: int indicating status +deactivate SubscriptionDataAccessService +SubscriptionDataAccessService --> SubscriptionService: int indicating status +deactivate SubscriptionDataAccessService +SubscriptionService --> SubscriptionController: int indicating status +deactivate SubscriptionService +<-- SubscriptionController: ResponseEntity with empty String for success \n\n-> ""HTTP status code"" \n-> ""JSON"" +deactivate SubscriptionController + +@enduml \ No newline at end of file diff --git a/21-implementierungsheft-kolloquium/assets/episode.png b/21-implementierungsheft-kolloquium/assets/episode.png new file mode 100644 index 0000000..c0db4a2 Binary files /dev/null and b/21-implementierungsheft-kolloquium/assets/episode.png differ diff --git a/21-implementierungsheft-kolloquium/assets/gantt-plan.puml b/21-implementierungsheft-kolloquium/assets/gantt-plan.puml new file mode 100644 index 0000000..0e90aa2 --- /dev/null +++ b/21-implementierungsheft-kolloquium/assets/gantt-plan.puml @@ -0,0 +1,31 @@ +@startgantt + +printscale daily zoom 5 +project starts on 2023-01-30 + +-- Backend -- +[Controller-Schicht] on {Immanuel} lasts 2 days +[Service-Schicht (Daten durchreichen)] on {Daniel} lasts 2 days +[Authentifizierung] on {Gero} lasts 4 days +[Model-Paket] on {Daniel} lasts 1 days +[Datenbank aufsetzen] on {Immanuel} lasts 4 days +[Util-Paket (RSSParser)] on {Daniel} {Lukas} lasts 6 days +[DataAccess-Schicht] on {Immanuel} {Julius} lasts 8 days +[Service-Schicht (Geschäftslogik)] on {Daniel} {Immanuel} lasts 8 days +[Util-Paket (CleanCronJob)] on {Julius} lasts 2 days +-- Frontend -- +[Komponenten] on {Gero} {Julius} {Lukas} lasts 15 days +[API-Anbindung] on {Gero} {Lukas} lasts 4 days + +'Backend +[Service-Schicht (Daten durchreichen)] starts at [Controller-Schicht]'s end +[Datenbank aufsetzen] starts at [Model-Paket]'s end +[Authentifizierung] starts at [Controller-Schicht]'s end +[DataAccess-Schicht] starts at [Datenbank aufsetzen]'s end +[Util-Paket (RSSParser)] starts at [Datenbank aufsetzen]'s end +[Service-Schicht (Geschäftslogik)] starts at [DataAccess-Schicht]'s end +[Util-Paket (CleanCronJob)] starts at [DataAccess-Schicht]'s end +'Frontend +[API-Anbindung] starts at [DataAccess-Schicht]'s end + +@endgantt \ No newline at end of file diff --git a/21-implementierungsheft-kolloquium/assets/gantt-reality.puml b/21-implementierungsheft-kolloquium/assets/gantt-reality.puml new file mode 100644 index 0000000..f726c56 --- /dev/null +++ b/21-implementierungsheft-kolloquium/assets/gantt-reality.puml @@ -0,0 +1,39 @@ +@startgantt + +printscale daily zoom 5 +project starts on 2023-01-30 + +-- Backend -- +[Controller-Schicht] on {Immanuel} lasts 3 days +[Model-Paket] on {Daniel} lasts 3 days +[Datenbank aufsetzen] on {Immanuel} lasts 6 days +[Util-Paket (RSSParser)] on {Daniel} {Lukas} lasts 32 days +[DAO-Interfaces] on {Julius} {Immanuel} lasts 6 days +[Authentifizierung] on {Immanuel} lasts 13 days +[Service-Schicht mit Datenzugriff] on {Julius} lasts 14 days +[Util-Paket (CleanCronJob)] on {Daniel} lasts 2 days +[Docker] on {Daniel} lasts 12 days +[EMailService] on {Gero} lasts 1 days +-- Frontend -- +[Komponenten] on {Gero} {Julius} lasts 15 days +[Mehrsprachigkeit] on {Lukas} lasts 5 days +[Router] on {Gero} lasts 1 days +[API-Anbindung] on {Gero} {Lukas} lasts 28 days +[Error-Handling] on {Gero} lasts 5 days + +'Backend +[Datenbank aufsetzen] starts at [Model-Paket]'s end +[Util-Paket (RSSParser)] starts at [Datenbank aufsetzen]'s end +[DAO-Interfaces] starts at [Datenbank aufsetzen]'s end +[Authentifizierung] starts at [DAO-Interfaces]'s end +[Service-Schicht mit Datenzugriff] starts at [DAO-Interfaces]'s end +[Util-Paket (CleanCronJob)] starts at [DAO-Interfaces]'s end +[Docker] starts at [Util-Paket (CleanCronJob)]'s end +[EMailService] starts 2023-02-14 +'Frontend +[Mehrsprachigkeit] starts 2023-02-01 +[Router] starts at [Mehrsprachigkeit]'s end +[API-Anbindung] starts at [Router]'s end +[Error-Handling] starts 2023-02-05 + +@endgantt \ No newline at end of file diff --git a/21-implementierungsheft-kolloquium/assets/help.png b/21-implementierungsheft-kolloquium/assets/help.png new file mode 100644 index 0000000..39a1b84 Binary files /dev/null and b/21-implementierungsheft-kolloquium/assets/help.png differ diff --git a/21-implementierungsheft-kolloquium/assets/lastupdate.png b/21-implementierungsheft-kolloquium/assets/lastupdate.png new file mode 100644 index 0000000..e9b7f5c Binary files /dev/null and b/21-implementierungsheft-kolloquium/assets/lastupdate.png differ diff --git a/21-implementierungsheft-kolloquium/assets/logo.svg b/21-implementierungsheft-kolloquium/assets/logo.svg new file mode 100644 index 0000000..1609066 --- /dev/null +++ b/21-implementierungsheft-kolloquium/assets/logo.svg @@ -0,0 +1,211 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Podcast Synchronisation made Efficient + + + + diff --git a/21-implementierungsheft-kolloquium/assets/navbar.png b/21-implementierungsheft-kolloquium/assets/navbar.png new file mode 100644 index 0000000..dd9f8e8 Binary files /dev/null and b/21-implementierungsheft-kolloquium/assets/navbar.png differ diff --git a/21-implementierungsheft-kolloquium/assets/password-margin.png b/21-implementierungsheft-kolloquium/assets/password-margin.png new file mode 100644 index 0000000..d9d4fa3 Binary files /dev/null and b/21-implementierungsheft-kolloquium/assets/password-margin.png differ diff --git a/21-implementierungsheft-kolloquium/assets/password.png b/21-implementierungsheft-kolloquium/assets/password.png new file mode 100644 index 0000000..68248a0 Binary files /dev/null and b/21-implementierungsheft-kolloquium/assets/password.png differ diff --git a/21-implementierungsheft-kolloquium/assets/subscription.png b/21-implementierungsheft-kolloquium/assets/subscription.png new file mode 100644 index 0000000..58a84f9 Binary files /dev/null and b/21-implementierungsheft-kolloquium/assets/subscription.png differ diff --git a/21-implementierungsheft-kolloquium/logos/banner_2020_kit.jpg b/21-implementierungsheft-kolloquium/logos/banner_2020_kit.jpg new file mode 100644 index 0000000..70ae1d0 Binary files /dev/null and b/21-implementierungsheft-kolloquium/logos/banner_2020_kit.jpg differ diff --git a/21-implementierungsheft-kolloquium/notizen b/21-implementierungsheft-kolloquium/notizen new file mode 100644 index 0000000..70ac63b --- /dev/null +++ b/21-implementierungsheft-kolloquium/notizen @@ -0,0 +1,40 @@ +Titelseite: +- Begrüßung + +Einführung: +- Podcast: RSS-Feed, Episoden, Audio/Video +- Podcatcher: lokale Verwaltung von Podcasts, API Unterstützung, +Abspielen von Episoden +- Synchronisationsserver (das soll unser Produkt werden): Hörfortschritte, +Abonnements, Discovery (bei AntennaPod z.B. iTunes) + +Zielsetzung: + +Synchronisation (die Art, die wir bei uns anwenden): +- alle Aktionen werden auf den Server und infolge dessen auf alle Podcatcher +übertragen + +Features: +- Synchronisation: Abos, Hörfortschritt +- Weboberfläche: Aboliste, Zuletzt gehört +- Account-Verwaltung: Registrieren, Anmelden, Passwort ändern/zurücksetzen, +Account löschen, Daten importieren/exportieren + +UI-Journey: +- login.html: + - Sprache ändern + - OAuth (kann) + - Registrieren (muss): neues Fenster, E-Mail + 2-mal Passwort (sicher?), + vergeben? -> Fehler / Bestätigungslink per E-Mail (gültig 24h) + - Anmelden (muss): E-Mail + Passwort eingeben, Login merken, + login, Fehlermeldung oder Dashboard + - Passwort vergessen: neues Fenster, E-Mail eingeben, falls Account wird + Link versendet, 24h gültig, nach betätigen PW zweimal eingeben & bestätigen + (Anforderungen) + +- podcasts.html: Abonnements, Eisoden, Hörfortschritte + +- listening.html: Zuletzt gehörte Episoden, Hörfortschritt, Wann gehört + +- settings.html: PW ändern, Gpodder Import, pers. Daten imp./exp., + Account löschen diff --git a/21-implementierungsheft-kolloquium/presentation.tex b/21-implementierungsheft-kolloquium/presentation.tex new file mode 100644 index 0000000..88cb357 --- /dev/null +++ b/21-implementierungsheft-kolloquium/presentation.tex @@ -0,0 +1,53 @@ +%% Beispiel-Präsentation mit LaTeX Beamer im KIT-Design +%% entsprechend den Gestaltungsrichtlinien vom 1. August 2020 +%% +%% Siehe https://sdqweb.ipd.kit.edu/wiki/Dokumentvorlagen + +%% Beispiel-Präsentation +\documentclass[table]{sdqbeamer} + +\usepackage{calc} +\usepackage{multicol} +\usepackage{fontawesome5} +\usepackage{ulem} + +%% Titelbild +\titleimage{banner_2020_kit} + +%% Gruppenlogo +\grouplogo{kitlogo_de_rgb} + +%% Gruppenname und Breite (Standard: 50 mm) +\groupname{Praxis der Softwareentwicklung} +%\groupnamewidth{50mm} + +% Beginn der Präsentation + +\title[Kolloquium Implementierung]{ + PSE\textsuperscript{2} - Podcast Synchronisation made Efficient +} +\subtitle{Kolloquium Implementierung} +\author[Lukas Schmidheissler]{Lukas Schmidheissler} + +\date[28.\,02.\,2023]{28. Februar 2023} + +\begin{document} + +%Titelseite +\KITtitleframe + +%\include{slides/classdiagram} + +\include{slides/integrationstrategie} + +\include{slides/gantt} + +\include{slides/difficulties} + +\include{slides/changes} + +\include{slides/statistics} + +\include{slides/requirements} + +\end{document} diff --git a/21-implementierungsheft-kolloquium/sdqbeamer.cls b/21-implementierungsheft-kolloquium/sdqbeamer.cls new file mode 100644 index 0000000..1b9c2b8 --- /dev/null +++ b/21-implementierungsheft-kolloquium/sdqbeamer.cls @@ -0,0 +1,975 @@ +%% Vorlage für Präsentationen mit LaTeX Beamer im KIT-Design +%% entsprechend den Gestaltungsrichtlinien vom 1. August 2020 +%% +%% Siehe https://sdqweb.ipd.kit.edu/wiki/Dokumentvorlagen + + +\NeedsTeXFormat{LaTeX2e} +\ProvidesClass{sdqbeamer}[2022-05-03 v3.1.3 SDQ Beamer class] + +\RequirePackage[utf8]{inputenc} +\RequirePackage[T1]{fontenc} + +\newif\ifsectionnavigation +\newif\ifnavbarinfoot +\newif\ifnavbarinline +\newif\ifnavbarside +\newif\iffourtothree +\newif\ifsixteentonine +\newif\ifsixteentoten +\newif\ifgerman +\newif\ifsmallfooterfont + +\def\kitslogan#1{\def\@kitslogan{#1}} +\def\kitlogo#1{\def\@kitlogo{#1}} +\def\groupname#1{\def\@groupname{#1}} +\def\groupnamewidth#1{\def\@groupnamewidth{#1}} + +% siehe README.md +\DeclareOption{de}{ + \kitslogan{KIT -- Die Forschungsuniversität in der Helmholtz-Gemeinschaft} + \kitlogo{pse_logo} + \germantrue + \PassOptionsToPackage{autostyle}{csquotes} + } +\DeclareOption{en}{ + \kitslogan{KIT -- The Research University in the Helmholtz Association} + \kitlogo{kitlogo_en_rgb} + \germanfalse + } +\DeclareOption{4:3}{ + \fourtothreetrue + \sixteentoninefalse + \sixteentotenfalse +} +\DeclareOption{16:9}{ + \fourtothreefalse + \sixteentoninetrue + \sixteentotenfalse +} +\DeclareOption{16:10}{ + \fourtothreefalse + \sixteentoninefalse + \sixteentotentrue +} +\DeclareOption{navbarside}{ + \sectionnavigationtrue + \navbarsidetrue + \navbarinlinefalse + \navbarinfootfalse +} +\DeclareOption{navbarinline}{ + \sectionnavigationtrue + \navbarsidefalse + \navbarinlinetrue + \navbarinfootfalse +} +\DeclareOption{navbarinfooter}{ + \sectionnavigationtrue + \navbarsidefalse + \navbarinlinefalse + \navbarinfoottrue +} +\DeclareOption{navbaroff}{ + \sectionnavigationfalse +} +\DeclareOption{navbarkit}{ + \sectionnavigationfalse + \smallfooterfonttrue +} +\DeclareOption{smallfoot}{ + \smallfooterfonttrue +} +\DeclareOption{bigfoot}{ + \smallfooterfontfalse +} + +\ExecuteOptions{de,16:9,navbarinline,bigfoot} + +\DeclareOption*{\PassOptionsToClass{\CurrentOption}{beamer}} + +\ProcessOptions\relax + +\LoadClass[10pt,utf8]{beamer} + +% Babel-Paket wird nur bei deutscher Sprache benötigt +\ifgerman + \RequirePackage[ngerman]{babel} +\fi +\RequirePackage{csquotes} +\RequirePackage{hyperref} +\RequirePackage[absolute,overlay]{textpos} + +%% --------------- +%% | Typographie | +%% --------------- + +\RequirePackage{microtype} + +\RequirePackage[scaled=.92]{helvet} +\RequirePackage[scaled=.78]{beramono} +\RequirePackage{libertineRoman} + +\setbeamerfont{title}{series=\bfseries,size=\Large} +\setbeamerfont{frametitle}{series=\bfseries,size=\Large} +\setbeamerfont{framesubtitle}{series=\bfseries,size=\normalsize} + +%% ----------------- +%% | Folien-Layout | +%% ----------------- + +% Seitenverhältnis +% +% Die Folien sind auf die Standardhöhe in LaTeX Beamer (9,6 cm) normiert. +% Die Maße der KIT-Gestaltungsrichtlinien (Folienhöhe 14,3 cm) wurden durch +% den Quotienten 1,5 geteilt. + +\RequirePackage{geometry} +\iffourtothree + \geometry{papersize={12.8cm,9.6cm}} +\fi +\ifsixteentoten + \geometry{papersize={15.36cm,9.6cm}} +\fi +\ifsixteentonine + \geometry{papersize={17.07cm,9.6cm}} +\fi + +% Ränder laut Gestaltungsrichtlinen; 3 mm -> 2 mm, 11 mm -> 7,3 mm +\newlength{\kitoutermargin} +\setlength{\kitoutermargin}{2mm} +\newlength{\kitinnermargin} +\setlength{\kitinnermargin}{7.3mm} +\newlength{\kitbottommargin} +\setlength{\kitbottommargin}{\kitinnermargin} + +% Ränder außen +\setbeamersize{text margin left=\kitinnermargin,text margin right=\kitinnermargin} + +% keine Navigationssymbole +\setbeamertemplate{navigation symbols}{} +\setbeamercovered{invisible} +\useinnertheme{rounded} +\beamer@compresstrue % Miniframes (Navigations-Punkte) für Subsections immer in einer Zeile, ohne Umbrüche + +% Folientitel +\setbeamertemplate{frametitle}{ + \ifbeamer@plainframe\else% + % Unterkante Titeltext: 22,5 mm -> 15 mm von Seitenkopf + \begin{textblock*}{\dimexpr\paperwidth-30mm-2\kitinnermargin}[0,1](\kitinnermargin,15mm)% + \usebeamerfont{frametitle}\insertframetitle% + \ifx\insertframesubtitle\@empty\else\\[.1em]\fi + \usebeamerfont{framesubtitle}\insertframesubtitle% + \end{textblock*}% + \begin{textblock*}{20mm}[1,1](\dimexpr\paperwidth-\kitinnermargin\relax,15mm)%j + \includegraphics[width=20mm]{logos/\@kitlogo}% + \end{textblock*}% + \fi +% Rand oben (ergibt Beginn des Textes bei 34 mm -> 22,7 mm) + \vspace {18mm} +} + +%% Fußzeile +\newlength{\kitbottom} + % Navbar in Footer: Schmale Fußzeile +\ifnavbarinfoot + % Bei Navbar in Footer immer kleiner Font in der Fußzeile + \setbeamerfont{footer}{size=\fontsize{6pt}{7.2pt}\selectfont} + \setlength{\kitbottom}{4mm} +\else + % Ansonsten kleiner Text nur, wenn "smallfoot" gewählt + \ifsmallfooterfont + \setbeamerfont{footer}{size=\fontsize{6pt}{7.2pt}\selectfont} + \else + \setbeamerfont{footer}{size=\scriptsize} + \fi + \setlength{\kitbottom}{\kitbottommargin} +\fi +\setbeamerfont{page number in head/foot}{series=\bfseries} + +\newlength{\kitbottomdepth} +\newlength{\kitbottomheight} +\newlength{\kitfootergroupwidth} + +\setbeamertemplate{footline}{% +\setlength{\kitbottomdepth}{\dimexpr.5\kitbottom-.5em\relax}% +\setlength{\kitbottomheight}{\dimexpr.5\kitbottom+.5em\relax}% + %% die "%" am Ende sind nötig, damit keine Abstände eingefügt werden + % + % Falls kein Gruppenname angegeben, die ganze Breite der Fußzeile für den Titel nutzen. + \ifdefined\@groupname% + \ifx\@groupname\empty% + \setlength{\kitfootergroupwidth}{0mm}% + \else% + % Falls die Breite des Gruppenlogos definiert ist, diese nehmen, sonst 50 mm + \ifdefined\@groupnamewidth% + \setlength{\kitfootergroupwidth}{\@groupnamewidth}% + \else% + \setlength{\kitfootergroupwidth}{50mm}% + \fi% + \fi% + \else + \setlength{\kitfootergroupwidth}{0mm}% + \fi + \usebeamerfont{footer}% + \ifsectionnavigation% + % Option "navbarinline" + \ifnavbarinline% + \begin{beamercolorbox}[wd=\paperwidth, leftskip=2mm, rightskip=2mm]{} + \insertnavigation{\dimexpr\paperwidth-4mm\relax} + \vspace{1mm} + \end{beamercolorbox}% + \fi% + % Option "navbarinfooter" + \ifnavbarinfoot% + % Punkte für Subsections deaktivieren + \setbeamertemplate{mini frames}{}% + \begin{beamercolorbox}[wd=\paperwidth, leftskip=1mm, rightskip=1mm]{}% + \insertsectionnavigationhorizontal{\dimexpr\paperwidth-\kitoutermargin\relax}{}{} + \end{beamercolorbox}% + \fi% + \fi% + \leavevmode% + \begin{beamercolorbox}[wd=13mm, ht=\kitbottomheight, dp=\kitbottomdepth, leftskip=4mm]{} + \usebeamerfont{page number in head/foot}% + \strut\insertframenumber{}/\inserttotalframenumber% + \end{beamercolorbox}% + \begin{beamercolorbox}[wd=20mm, ht=\kitbottomheight, dp=\kitbottomdepth]{} + \usebeamerfont{date in head/foot}% + \strut\insertshortdate% + \end{beamercolorbox}% + % Die Boxen mit dem Titel und dem Gruppennamen sind vertikal zentriert, damit auch zweizeilige Texte schön aussehen + % Daher müssen sie um \kitbottomdepth nach unten verschoben werden + \raisebox{-\kitbottomdepth}{ + % Die Box hat daher auch Höhe \kitbottom und Tiefe 0mm + \begin{beamercolorbox}[wd=\dimexpr\paperwidth-37mm-\kitfootergroupwidth, ht=\kitbottom, dp=0mm]{}% + % Inhalt vertikal zentrieren; Anpassung um 1.5 pt, damit bei einzeiligem Inhalt genau die Baseline der Blöcke mit Seitenzahl und Datum getroffen wird + \vbox to\kitbottom{\vfill\vskip1.5pt% + \beamer@shortauthor\ifx\beamer@shortauthor\empty\else: \fi\beamer@shorttitle% + \vfill}% + \end{beamercolorbox}% + \ifdefined\@groupname% + \begin{beamercolorbox}[wd=\kitfootergroupwidth, ht=\kitbottom, dp=0mm, rightskip=\kitinnermargin]{} + \vbox to\kitbottom{\vfill\vskip1.5pt% + \raggedleft\@groupname% + \vfill}% + \end{beamercolorbox}% + \fi% + }% +} + +%% Option "navbarside" +\ifnavbarside +\useoutertheme[height=0cm,width=3.5cm,left]{sidebar} + +\setbeamerfont{title in sidebar}{family=\sffamily,series=\mdseries,size={\fontsize{10pt}{11pt}}} +\setbeamerfont{section in sidebar}{family=\sffamily,series=\mdseries,size={\fontsize{9pt}{9.9pt}}} +\setbeamerfont{subsection in sidebar}{family=\sffamily,series=\mdseries,size={\fontsize{8pt}{8.8pt}}} + +\setbeamertemplate{sidebar \beamer@sidebarside} + {\vskip1.5cm% + \hskip6.5mm% + \advance\beamer@sidebarwidth by -5mm% + \insertverticalnavigation{\beamer@sidebarwidth}% + }% +\fi + +%% Hintergrund +\usebackgroundtemplate{ + % Trennlinie nicht bei "plain"-Frames + \ifbeamer@plainframe\else\kitseparationline\fi +} + +% Trennlinie +\newcommand{\kitseparationline}{ + \begin{pgfpicture}{0mm}{0mm}{\paperwidth}{\paperheight} + \pgfsetstrokecolor{black!15} + \pgfsetlinewidth{.5pt} + \pgfpathmoveto{\pgfpoint{\kitoutermargin}{\kitinnermargin}} + \pgfpathlineto{\pgfpoint{\paperwidth-\kitoutermargin}{\kitinnermargin}} + \pgfusepath{stroke} + \end{pgfpicture}% +} + + +%% -------------- +%% | Titelseite | +%% -------------- + +\def\titleimage#1{\def\@titleimage{#1}} +\def\grouplogo#1{\def\@grouplogo{#1}} + +\newcommand{\KITtitleframe}{ + \begin{frame}[plain] + \titlepage + \end{frame} +} + +\newlength{\kittitleimageheight} +\setbeamertemplate{title page}{ + % From textpos documentation (https://ctan.org/pkg/textpos) + % + % \begin{textblock}{}[,](,) + % The coordinates and are fractions of the width and height of the text + % box, respectively, and state that the box is to be placed so that the reference point + % (,) within the box is to be placed at the point (,) on the page. + + % KIT-Logo + \begin{textblock*}{30mm}(\kitinnermargin,6.7mm) + \includegraphics[width=30mm]{logos/\@kitlogo} + \end{textblock*} + + % Gruppenlogo + \ifdefined\@grouplogo + \ifx\@grouplogo\empty \else + \begin{textblock*}{20mm}(\dimexpr\paperwidth-24mm\relax,6.7mm) + \includegraphics[width=20mm,height=20mm,keepaspectratio]{logos/\@grouplogo} + \end{textblock*} + \fi % falls \grouplogo{} aufgerufen wird, kein Gruppenlogo einbinden + \else + \begin{textblock*}{20mm}(\dimexpr\paperwidth-24mm\relax,6.7mm) + \colorbox{kit-purple100!20}{\parbox[t][12mm][c]{19mm}{\color{kit-purple100}\scriptsize\centering + \ifgerman + Bitte Logo über \texttt{\textbackslash grouplogo\{\}} festlegen. + \else + Please set a logo using \texttt{\textbackslash grouplogo\{\}}. + \fi + }} + \end{textblock*} + \fi + + % Titel + \begin{textblock*}{\dimexpr\paperwidth-8mm\relax}[0,.5](\kitinnermargin,28mm) + \usebeamerfont*{title}\inserttitle + \end{textblock*} + + % Untertitel + \begin{textblock*}{\dimexpr\paperwidth-8mm\relax}(\kitinnermargin,36mm) + \small\textbf{\insertsubtitle} + \end{textblock*} + + % Autor + \begin{textblock*}{\dimexpr\paperwidth-8mm\relax}(\kitinnermargin,41mm) + \small\insertauthor~\textbar~\insertdate + \end{textblock*} + + % Titelbild + \setlength{\kittitleimageheight}{40mm} + \begin{textblock*}{\paperwidth}(\kitoutermargin,\dimexpr\paperheight-\kitbottommargin-\kittitleimageheight) + \begin{pgfpicture}{0mm}{0mm}{\paperwidth}{\kittitleimageheight} + % Clipping-Pfad um titelbild + \pgfsetstrokecolor{black!15} + \pgfsetlinewidth{1pt} + \pgfsetcornersarced{\pgfpoint{3mm}{3mm}} + \pgfpathmoveto{\pgfpoint{\paperwidth-2\kitoutermargin}{0mm}} + \pgfpathlineto{\pgfpoint{\paperwidth-2\kitoutermargin}{\kittitleimageheight}} + \pgfsetcornersarced{\pgfpointorigin} + \pgfpathlineto{\pgfpoint{0mm}{\kittitleimageheight}} + \pgfsetcornersarced{\pgfpoint{3mm}{3mm}} + \pgfpathlineto{\pgfpointorigin} + \pgfsetcornersarced{\pgfpointorigin} + \pgfpathclose + \pgfusepath{stroke,clip} + \pgfsetstrokecolor{black} + + % Titelbild + \ifdefined\@titleimage + \ifx\@titleimage\empty \else% + \pgftext[at=\pgfpoint{.5\paperwidth}{0mm},center,bottom]{% + \includegraphics[height=40mm]{logos/\@titleimage} + } + \fi % Bei Aufruf von \titleimage{} leeren Rahmen anzeigen. + \else + \pgftext[at=\pgfpoint{.5\paperwidth}{.5\kittitleimageheight},center,base]{% + \colorbox{kit-purple100!20}{\parbox[c][\kittitleimageheight][c]{\paperwidth}{\color{kit-purple100}\centering Bitte Titelbild über \texttt{\textbackslash titleimage\{\}} festlegen. + }}% + } + \fi + + + \end{pgfpicture}% + \end{textblock*} + + + % KIT slogan + \begin{textblock*}{80mm}[0,.5](\kitoutermargin,\dimexpr\paperheight-.5\kitbottommargin) + \fontsize{5.5pt}{5.5pt}\selectfont\@kitslogan + \end{textblock*} + + \begin{textblock*}{30mm}[1,.5](\dimexpr\paperwidth-\kitoutermargin\relax,\dimexpr\paperheight-.5\kitbottommargin) + \fontsize{11pt}{11pt}\selectfont\bfseries\raggedleft% + {\href{https://www.kit.edu}{www.kit.edu}} + \end{textblock*} + +} +%% --------------- +%% | /Titelseite | +%% --------------- + +%% ---------- +%% | Farben | +%% ---------- +%% KIT-Farbschema + +% KIT color green : +\definecolor{kit-green}{RGB}{0, 150, 130} +\definecolor{kit-green100}{RGB}{0, 150, 130} +\definecolor{kit-green90}{rgb}{0.1, 0.6294, 0.5588} +\definecolor{kit-green80}{rgb}{0.2, 0.6706, 0.6078} +\definecolor{kit-green75}{rgb}{0.25, 0.6912, 0.6324} +\definecolor{kit-green70}{rgb}{0.3, 0.7118, 0.6569} +\definecolor{kit-green60}{rgb}{0.4, 0.7529, 0.7059} +\definecolor{kit-green50}{rgb}{0.5, 0.7941, 0.7549} +\definecolor{kit-green40}{rgb}{0.6, 0.8353, 0.8039} +\definecolor{kit-green30}{rgb}{0.7, 0.8765, 0.8529} +\definecolor{kit-green25}{rgb}{0.75, 0.8971, 0.8775} +\definecolor{kit-green20}{rgb}{0.8, 0.9176, 0.902} +\definecolor{kit-green15}{rgb}{0.85, 0.9382, 0.9265} +\definecolor{kit-green10}{rgb}{0.9, 0.9588, 0.951} +\definecolor{kit-green5}{rgb}{0.95, 0.9794, 0.9755} + +% KIT color blue: +\definecolor{kit-blue}{RGB}{70, 100, 170} +\definecolor{kit-blue100}{RGB}{70, 100, 170} +\definecolor{kit-blue90}{rgb}{0.3471, 0.4529, 0.7} +\definecolor{kit-blue80}{rgb}{0.4196, 0.5137, 0.7333} +\definecolor{kit-blue75}{rgb}{0.4559, 0.5441, 0.75} +\definecolor{kit-blue70}{rgb}{0.4922, 0.5745, 0.7667} +\definecolor{kit-blue60}{rgb}{0.5647, 0.6353, 0.8} +\definecolor{kit-blue50}{rgb}{0.6373, 0.6961, 0.8333} +\definecolor{kit-blue40}{rgb}{0.7098, 0.7569, 0.8667} +\definecolor{kit-blue30}{rgb}{0.7824, 0.8176, 0.9} +\definecolor{kit-blue25}{rgb}{0.8186, 0.848, 0.9167} +\definecolor{kit-blue20}{rgb}{0.8549, 0.8784, 0.9333} +\definecolor{kit-blue15}{rgb}{0.8912, 0.9088, 0.95} +\definecolor{kit-blue10}{rgb}{0.9275, 0.9392, 0.9667} +\definecolor{kit-blue5}{rgb}{0.9637, 0.9696, 0.9833} + +% KIT color red : +\definecolor{kit-red}{RGB}{162, 34, 35} +\definecolor{kit-red100}{RGB}{162, 34, 35} +\definecolor{kit-red90}{rgb}{0.6718, 0.22, 0.2235} +\definecolor{kit-red80}{rgb}{0.7082, 0.3067, 0.3098} +\definecolor{kit-red75}{rgb}{0.7265, 0.35, 0.3529} +\definecolor{kit-red70}{rgb}{0.7447, 0.3933, 0.3961} +\definecolor{kit-red60}{rgb}{0.7812, 0.48, 0.4824} +\definecolor{kit-red50}{rgb}{0.8176, 0.5667, 0.5686} +\definecolor{kit-red40}{rgb}{0.8541, 0.6533, 0.6549} +\definecolor{kit-red30}{rgb}{0.8906, 0.74, 0.7412} +\definecolor{kit-red25}{rgb}{0.9088, 0.7833, 0.7843} +\definecolor{kit-red20}{rgb}{0.9271, 0.8267, 0.8275} +\definecolor{kit-red15}{rgb}{0.9453, 0.87, 0.8706} +\definecolor{kit-red10}{rgb}{0.9635, 0.9133, 0.9137} +\definecolor{kit-red5}{rgb}{0.9818, 0.9567, 0.9569} + +% KIT color yellow : +\definecolor{kit-yellow}{RGB}{252, 229, 0} +\definecolor{kit-yellow100}{RGB}{252, 229, 0} +\definecolor{kit-yellow90}{rgb}{0.9894, 0.9082, 0.1} +\definecolor{kit-yellow80}{rgb}{0.9906, 0.9184, 0.2} +\definecolor{kit-yellow75}{rgb}{0.9912, 0.9235, 0.25} +\definecolor{kit-yellow70}{rgb}{0.9918, 0.9286, 0.3} +\definecolor{kit-yellow60}{rgb}{0.9929, 0.9388, 0.4} +\definecolor{kit-yellow50}{rgb}{0.9941, 0.949, 0.5} +\definecolor{kit-yellow40}{rgb}{0.9953, 0.9592, 0.6} +\definecolor{kit-yellow30}{rgb}{0.9965, 0.9694, 0.7} +\definecolor{kit-yellow25}{rgb}{0.9971, 0.9745, 0.75} +\definecolor{kit-yellow20}{rgb}{0.9976, 0.9796, 0.8} +\definecolor{kit-yellow15}{rgb}{0.9982, 0.9847, 0.85} +\definecolor{kit-yellow10}{rgb}{0.9988, 0.9898, 0.9} +\definecolor{kit-yellow5}{rgb}{0.9994, 0.9949, 0.95} + +% KIT color orange : +\definecolor{kit-orange}{RGB}{223, 155, 27} +\definecolor{kit-orange100}{RGB}{223, 155, 27} +\definecolor{kit-orange90}{rgb}{0.8871, 0.6471, 0.1953} +\definecolor{kit-orange80}{rgb}{0.8996, 0.6863, 0.2847} +\definecolor{kit-orange75}{rgb}{0.9059, 0.7059, 0.3294} +\definecolor{kit-orange70}{rgb}{0.9122, 0.7255, 0.3741} +\definecolor{kit-orange60}{rgb}{0.9247, 0.7647, 0.4635} +\definecolor{kit-orange50}{rgb}{0.9373, 0.8039, 0.5529} +\definecolor{kit-orange40}{rgb}{0.9498, 0.8431, 0.6424} +\definecolor{kit-orange30}{rgb}{0.9624, 0.8824, 0.7318} +\definecolor{kit-orange25}{rgb}{0.9686, 0.902, 0.7765} +\definecolor{kit-orange20}{rgb}{0.9749, 0.9216, 0.8212} +\definecolor{kit-orange15}{rgb}{0.9812, 0.9412, 0.8659} +\definecolor{kit-orange10}{rgb}{0.9875, 0.9608, 0.9106} +\definecolor{kit-orange5}{rgb}{0.9937, 0.9804, 0.9553} + +% KIT color lightgreen : +\definecolor{kit-lightgreen}{RGB}{140, 182, 60} +\definecolor{kit-lightgreen100}{RGB}{140, 182, 60} +\definecolor{kit-lightgreen90}{rgb}{0.5941, 0.7424, 0.3118} +\definecolor{kit-lightgreen80}{rgb}{0.6392, 0.771, 0.3882} +\definecolor{kit-lightgreen75}{rgb}{0.6618, 0.7853, 0.4265} +\definecolor{kit-lightgreen70}{rgb}{0.6843, 0.7996, 0.4647} +\definecolor{kit-lightgreen60}{rgb}{0.7294, 0.8282, 0.5412} +\definecolor{kit-lightgreen50}{rgb}{0.7745, 0.8569, 0.6176} +\definecolor{kit-lightgreen40}{rgb}{0.8196, 0.8855, 0.6941} +\definecolor{kit-lightgreen30}{rgb}{0.8647, 0.9141, 0.7706} +\definecolor{kit-lightgreen25}{rgb}{0.8873, 0.9284, 0.8088} +\definecolor{kit-lightgreen20}{rgb}{0.9098, 0.9427, 0.8471} +\definecolor{kit-lightgreen15}{rgb}{0.9324, 0.9571, 0.8853} +\definecolor{kit-lightgreen10}{rgb}{0.9549, 0.9714, 0.9235} +\definecolor{kit-lightgreen5}{rgb}{0.9775, 0.9857, 0.9618} + + +% KIT color purple : +\definecolor{kit-purple}{RGB}{163, 16, 124} +\definecolor{kit-purple100}{RGB}{163, 16, 124} +\definecolor{kit-purple90}{rgb}{0.6753, 0.1565, 0.5376} +\definecolor{kit-purple80}{rgb}{0.7114, 0.2502, 0.589} +\definecolor{kit-purple75}{rgb}{0.7294, 0.2971, 0.6147} +\definecolor{kit-purple70}{rgb}{0.7475, 0.3439, 0.6404} +\definecolor{kit-purple60}{rgb}{0.7835, 0.4376, 0.6918} +\definecolor{kit-purple50}{rgb}{0.8196, 0.5314, 0.7431} +\definecolor{kit-purple40}{rgb}{0.8557, 0.6251, 0.7945} +\definecolor{kit-purple30}{rgb}{0.8918, 0.7188, 0.8459} +\definecolor{kit-purple25}{rgb}{0.9098, 0.7657, 0.8716} +\definecolor{kit-purple20}{rgb}{0.9278, 0.8125, 0.8973} +\definecolor{kit-purple15}{rgb}{0.9459, 0.8594, 0.9229} +\definecolor{kit-purple10}{rgb}{0.9639, 0.9063, 0.9486} +\definecolor{kit-purple5}{rgb}{0.982, 0.9531, 0.9743} + +% KIT color brown : +\definecolor{kit-brown}{RGB}{167, 130, 46} +\definecolor{kit-brown100}{RGB}{167, 130, 46} +\definecolor{kit-brown90}{rgb}{0.6894, 0.5588, 0.2624} +\definecolor{kit-brown80}{rgb}{0.7239, 0.6078, 0.3443} +\definecolor{kit-brown75}{rgb}{0.7412, 0.6324, 0.3853} +\definecolor{kit-brown70}{rgb}{0.7584, 0.6569, 0.4263} +\definecolor{kit-brown60}{rgb}{0.7929, 0.7059, 0.5082} +\definecolor{kit-brown50}{rgb}{0.8275, 0.7549, 0.5902} +\definecolor{kit-brown40}{rgb}{0.862, 0.8039, 0.6722} +\definecolor{kit-brown30}{rgb}{0.8965, 0.8529, 0.7541} +\definecolor{kit-brown25}{rgb}{0.9137, 0.8775, 0.7951} +\definecolor{kit-brown20}{rgb}{0.931, 0.902, 0.8361} +\definecolor{kit-brown15}{rgb}{0.9482, 0.9265, 0.8771} +\definecolor{kit-brown10}{rgb}{0.9655, 0.951, 0.918} +\definecolor{kit-brown5}{rgb}{0.9827, 0.9755, 0.959} + +% KIT color cyan : +\definecolor{kit-cyan}{RGB}{35, 161, 224} +\definecolor{kit-cyan100}{RGB}{35, 161, 224} +\definecolor{kit-cyan90}{rgb}{0.2235, 0.6682, 0.8906} +\definecolor{kit-cyan80}{rgb}{0.3098, 0.7051, 0.9027} +\definecolor{kit-cyan75}{rgb}{0.3529, 0.7235, 0.9088} +\definecolor{kit-cyan70}{rgb}{0.3961, 0.742, 0.9149} +\definecolor{kit-cyan60}{rgb}{0.4824, 0.7788, 0.9271} +\definecolor{kit-cyan50}{rgb}{0.5686, 0.8157, 0.9392} +\definecolor{kit-cyan40}{rgb}{0.6549, 0.8525, 0.9514} +\definecolor{kit-cyan30}{rgb}{0.7412, 0.8894, 0.9635} +\definecolor{kit-cyan25}{rgb}{0.7843, 0.9078, 0.9696} +\definecolor{kit-cyan20}{rgb}{0.8275, 0.9263, 0.9757} +\definecolor{kit-cyan15}{rgb}{0.8706, 0.9447, 0.9818} +\definecolor{kit-cyan10}{rgb}{0.9137, 0.9631, 0.9878} +\definecolor{kit-cyan5}{rgb}{0.9569, 0.9816, 0.9939} + +% KIT color gray : +\definecolor{kit-gray}{RGB}{0, 0, 0} +\definecolor{kit-gray100}{RGB}{0, 0, 0} +\definecolor{kit-gray90}{rgb}{0.1, 0.1, 0.1} +\definecolor{kit-gray80}{rgb}{0.2, 0.2, 0.2} +\definecolor{kit-gray75}{rgb}{0.25, 0.25, 0.25} +\definecolor{kit-gray70}{rgb}{0.3, 0.3, 0.3} +\definecolor{kit-gray60}{rgb}{0.4, 0.4, 0.4} +\definecolor{kit-gray50}{rgb}{0.5, 0.5, 0.5} +\definecolor{kit-gray40}{rgb}{0.6, 0.6, 0.6} +\definecolor{kit-gray30}{rgb}{0.7, 0.7, 0.7} +\definecolor{kit-gray25}{rgb}{0.75, 0.75, 0.75} +\definecolor{kit-gray20}{rgb}{0.8, 0.8, 0.8} +\definecolor{kit-gray15}{rgb}{0.85, 0.85, 0.85} +\definecolor{kit-gray10}{rgb}{0.9, 0.9, 0.9} +\definecolor{kit-gray5}{rgb}{0.95, 0.95, 0.95} + + + +\setbeamercolor*{normal text}{fg=black} +\setbeamercolor*{alerted text}{fg=kit-red100} +\setbeamercolor*{example text}{fg=black} +\setbeamercolor*{structure}{fg=black} + +\setbeamercolor*{palette primary}{fg=black,bg=black!15} +\setbeamercolor*{palette secondary}{fg=black,bg=black!15} +\setbeamercolor*{palette tertiary}{fg=black,bg=black!15} +\setbeamercolor*{palette quaternary}{fg=black,bg=black!15} + +\setbeamercolor*{palette sidebar primary}{fg=black!75} +\setbeamercolor*{palette sidebar secondary}{fg=black!75} +\setbeamercolor*{palette sidebar tertiary}{fg=black!75} +\setbeamercolor*{palette sidebar quaternary}{fg=black!75} + +\setbeamercolor*{item projected}{fg=white,bg=kit-green100} + +\setbeamercolor*{block title}{fg=white,bg=kit-green100} +\setbeamercolor*{block title alerted}{use=alerted text,fg=white,bg=alerted text.fg!75!black} +\setbeamercolor*{block title example}{fg=white,bg=kit-blue100} + +\setbeamercolor*{block body}{fg=black,bg=kit-green15} +\setbeamercolor*{block body alerted}{parent=normal text,use=block title alerted,bg=block title alerted.bg!10!bg} +\setbeamercolor*{block body example}{fg=black,bg=kit-blue15} + +\setbeamercolor*{separation line}{} +\setbeamercolor*{fine separation line}{} + +\setbeamercolor*{background canvas}{bg=white} + +%% ----------- +%% | /Farben | +%% ----------- + +%% ----------------------------------- +%% | halbgerundete Aufzählungspunkte | +%% ----------------------------------- + +\newcommand{\itemizeColor}{kit-green100} + +% KIT-Aufzählungszeichen +\newcommand{\KITmark}{% + \begin{pgfpicture}{0mm}{0mm}{1ex}{1ex} + {\pgfsetcornersarced{\pgfpoint{.3ex}{.3ex}} + \pgfpathmoveto{\pgfpoint{0cm}{1ex}} + \pgfpathlineto{\pgfpoint{1ex}{1ex}} + \pgfpathlineto{\pgfpoint{1ex}{0cm}}} + {\pgfsetcornersarced{\pgfpoint{.3ex}{.3ex}} + \pgfpathmoveto{\pgfpoint{1ex}{0cm}} + \pgfpathlineto{\pgfpointorigin} + \pgfpathlineto{\pgfpoint{0cm}{1ex}}} + \color{\itemizeColor} + \pgfusepath{fill} + \end{pgfpicture}% +} + +% \setbeamertemplate{itemize items}{\raisebox{.2ex}{\KITmark}} +\setbeamertemplate{itemize items}[square] +\setbeamercolor{itemize item}{fg=kit-green100} + +%% ---------------------- +%% | Inhaltsverzeichnis | +%% ---------------------- + +\setbeamertemplate{section in toc}{\normalsize\textbf{\textcolor{kit-blue}{\inserttocsectionnumber.~\inserttocsection}}\par} +\setbeamertemplate{subsection in toc}{\small\hspace{1.2em}\raisebox{.2ex}{\KITmark}\hspace{\labelsep}\inserttocsubsection\par} + +%% ------------------------------ +%% | halbgerundete Beamer-Boxen | +%% ------------------------------ + +\renewcommand\beamerboxesrounded[2][]{% + \global\let\beamer@firstlineitemizeunskip=\relax% + \vbox\bgroup% + \setkeys{beamerboxes}{upper=block title,lower=block body,width=\textwidth,shadow=false}% + \setkeys{beamerboxes}{#1}% + {% + \usebeamercolor{\bmb@lower}% + \globalcolorstrue% + \colorlet{lower.bg}{bg}% + }% + {% + \usebeamercolor{\bmb@upper}% + \globalcolorstrue% + \colorlet{upper.bg}{bg}% + }% + % + % Typeset head + % + \vskip4bp + \setbox\bmb@box=\hbox{% + \begin{minipage}[b]{\bmb@width}% + \usebeamercolor[fg]{\bmb@upper}% + #2% + \end{minipage}}% + \ifdim\wd\bmb@box=0pt% + \setbox\bmb@box=\hbox{}% + \ht\bmb@box=1.5pt% + \bmb@prevheight=-4.5pt% + \else% + \wd\bmb@box=\bmb@width% + \bmb@temp=\dp\bmb@box% + \ifdim\bmb@temp<1.5pt% + \bmb@temp=1.5pt% + \fi% + \setbox\bmb@box=\hbox{\raise\bmb@temp\hbox{\box\bmb@box}}% + \dp\bmb@box=0pt% + \bmb@prevheight=\ht\bmb@box% + \fi% + \bmb@temp=\bmb@width% + \bmb@dima=\bmb@temp\advance\bmb@dima by2.2bp% + \bmb@dimb=\bmb@temp\advance\bmb@dimb by4bp% + \hbox{% + \begin{pgfpicture}{0bp}{+-\ht\bmb@box}{0bp}{+-\ht\bmb@box} + \ifdim\wd\bmb@box=0pt% + \color{lower.bg}% + \else% + \color{upper.bg}% + \fi% + \pgfpathqmoveto{-4bp}{-1bp} + % Adaption for "KIT-Design" + \pgfpathlineto{\pgfpoint{-4bp}{3bp}} + %\pgfpathqcurveto{-4bp}{1.2bp}{-2.2bp}{3bp}{0bp}{3bp} + \pgfpathlineto{\pgfpoint{\bmb@temp}{3bp}} + \pgfpathcurveto% + {\pgfpoint{\bmb@dima}{3bp}}% + {\pgfpoint{\bmb@dimb}{1.2bp}}% + {\pgfpoint{\bmb@dimb}{-1bp}}% + \bmb@dima=-\ht\bmb@box% + \advance\bmb@dima by-2pt% + \pgfpathlineto{\pgfpoint{\bmb@dimb}{\bmb@dima}} + \pgfpathlineto{\pgfpoint{-4bp}{\bmb@dima}} + \pgfusepath{fill} + \end{pgfpicture}% + \copy\bmb@box% + }% + \nointerlineskip% + \vskip-1pt% + \ifdim\wd\bmb@box=0pt% + \else% + \hbox{% + \begin{pgfpicture}{0pt}{0pt}{\bmb@width}{6pt} + \bmb@dima=\bmb@width% + \advance\bmb@dima by8bp% + \pgfpathrectangle{\pgfpoint{-4bp}{-1bp}}{\pgfpoint{\bmb@dima}{8bp}} + \pgfusepath{clip} + {\pgftransformshift{\pgfpoint{-4bp}{0bp}}\pgftext[left,base]{\pgfuseshading{bmb@transition}}}% + \end{pgfpicture}% + }% + \nointerlineskip% + \vskip-0.5pt% + \fi% + \ifbmb@shadow% + \setbox\bmb@boxshadow=\hbox{\pgfuseshading{bmb@shadow}}% + \setbox\bmb@boxshadowball=\hbox{\pgfuseshading{bmb@shadowball}}% + \setbox\bmb@boxshadowballlarge=\hbox{\pgfuseshading{bmb@shadowballlarge}}% + \fi% + \setbox\bmb@colorbox=\hbox{{\pgfpicturetrue\pgfsetcolor{lower.bg}}}% + \setbox\bmb@box=\hbox\bgroup\begin{minipage}[b]{\bmb@width}% + \vskip2pt% + \usebeamercolor[fg]{\bmb@lower}% + \colorlet{beamerstructure}{upper.bg}% + \colorlet{structure}{upper.bg}% + %\color{.}% + } + +\def\endbeamerboxesrounded{% + \end{minipage}\egroup% + \wd\bmb@box=\bmb@width% + \bmb@temp=\dp\bmb@box% + \advance\bmb@temp by.5pt% + \setbox\bmb@box=\hbox{\raise\bmb@temp\hbox{\box\bmb@box}}% + \dp\bmb@box=0pt% + \bmb@temp=\wd\bmb@box% + \bmb@dima=\bmb@temp\advance\bmb@dima by2.2bp% + \bmb@dimb=\bmb@temp\advance\bmb@dimb by4bp% + \hbox{% + \begin{pgfpicture}{0bp}{0bp}{0bp}{0bp} + \ifbmb@shadow% + {\pgftransformshift{\pgfpoint{4bp}{-3bp}}\pgftext{\copy\bmb@boxshadowball}} + \begin{pgfscope} + {% + \advance\bmb@temp by-1bp% + \pgfpathrectangle{\pgfpoint{\bmb@temp}{-7bp}}{\pgfpoint{9bp}{9bp}}% + \pgfusepath{clip} + }% + {\pgftransformshift{\pgfpoint{\bmb@temp}{1bp}}\pgftext{\box\bmb@boxshadowballlarge}} + \end{pgfscope} + \begin{pgfscope} + \advance\bmb@temp by-4bp% + \pgfpathrectangle{\pgfpoint{4bp}{-7bp}}{\pgfpoint{\bmb@temp}{5bp}} + \pgfusepath{clip} + {\pgftransformshift{\pgfpoint{4bp}{-7bp}}\pgftext[left,base]{\copy\bmb@boxshadow}}% + \end{pgfscope} + \begin{pgfscope} + \advance\bmb@temp by 4bp% + \bmb@dima=\ht\bmb@box% + \advance\bmb@dima by\bmb@prevheight% + \advance\bmb@dima by 4bp% + \pgfpathrectangle{\pgfpoint{\bmb@temp}{1bp}}{\pgfpoint{4bp}{\bmb@dima}} + \pgfusepath{clip} + \advance\bmb@dima by-4bp% + {\pgftransformshift{\pgfpoint{\bmb@temp}{\bmb@dima}}\pgftext{\box\bmb@boxshadowball}} + \advance\bmb@dima by-1bp% + \pgfpathrectangle{\pgfpoint{\bmb@temp}{1bp}}{\pgfpoint{4bp}{\bmb@dima}} + \pgfusepath{clip} + \advance\bmb@temp by4bp% + {\pgftransformshift{\pgfpoint{\bmb@temp}{-3bp}}% + \pgftransformrotate{90}% + \pgftext[left,base]{\box\bmb@boxshadow}}% + \end{pgfscope} + \fi% + \unhbox\bmb@colorbox% + \pgfpathqmoveto{-4bp}{1bp} + \pgfpathqcurveto{-4bp}{-1.2bp}{-2.2bp}{-3bp}{0bp}{-3bp} + \pgfpathlineto{\pgfpoint{\the\bmb@dimb}{-3bp}} + { + \bmb@dima=\ht\bmb@box% + \pgfpathlineto{\pgfpoint{\bmb@dimb}{\bmb@dima}} + \pgfpathlineto{\pgfpoint{-4bp}{\bmb@dima}} + \pgfusepath{fill} + } + \ifbmb@shadow% + { + \color{black!50!bg} + \pgfsetlinewidth{0pt} + \pgfpathmoveto{\pgfpoint{\bmb@dimb}{-.5bp}} + \bmb@dima=\ht\bmb@box% + \advance\bmb@dima by\bmb@prevheight% + \advance\bmb@dima by 1bp% + \pgfpathlineto{\pgfpoint{\bmb@dimb}{\bmb@dima}} + \pgfusepath{stroke} + \bmb@temp=\bmb@dima + \advance\bmb@dima by 1bp% + \color{black!31!bg} + \pgfpathmoveto{\pgfpoint{\bmb@dimb}{\bmb@temp}} + \pgfpathlineto{\pgfpoint{\bmb@dimb}{\bmb@dima}} + \pgfusepath{stroke} + \advance\bmb@dima by 1bp% + \advance\bmb@temp by 1bp% + \color{black!19!bg} + \pgfpathmoveto{\pgfpoint{\bmb@dimb}{\bmb@temp}} + \pgfpathlineto{\pgfpoint{\bmb@dimb}{\bmb@dima}} + \pgfusepath{stroke} + \advance\bmb@dima by 1bp% + \advance\bmb@temp by 1bp% + \color{black!6!bg} + \pgfpathmoveto{\pgfpoint{\bmb@dimb}{\bmb@temp}} + \pgfpathlineto{\pgfpoint{\bmb@dimb}{\bmb@dima}} + \pgfusepath{stroke} + \advance\bmb@dima by 1.5bp% + \advance\bmb@temp by 1bp% + \color{bg} + \pgfpathmoveto{\pgfpoint{\bmb@dimb}{\bmb@temp}} + \pgfpathlineto{\pgfpoint{\bmb@dimb}{\bmb@dima}} + \pgfusepath{stroke} + } + \fi + \end{pgfpicture}% + \box\bmb@box% + }% + \ifbmb@shadow% + \vskip4bp minus 2bp% + \else% + \vskip2bp% + \fi% + \egroup% of \vbox\bgroup +} + + +%% ------------------------------- +%% | /halbgerundete Beamer-Boxen | +%% ------------------------------- + + +%% ---------------------- +%% | Block-Definitionen | +%% ---------------------- + +% Content environment for structuring. Basically a headline followed by text +\newenvironment<>{contentblock}[1]{\begingroup% + \setbeamertemplate{blocks}[default] + \setbeamercolor{block body}{fg=black,bg=}% + \setbeamercolor{block title}{fg=black,bg=}% + \setbeamerfont*{block title}{family=\sffamily,series=\bfseries,size=\large} + \begin{block}#2{#1}% + }{\end{block}% +\endgroup} + + +\newenvironment<>{greenblock}[1]{\begingroup% + \setbeamercolor{block body}{fg=black,bg=kit-green15}% + \setbeamercolor{block title}{fg=white,bg=kit-green100}% + \begin{block}#2{#1}% + }{\end{block} +\endgroup} + +\newenvironment<>{blueblock}[1]{\begingroup% + \setbeamercolor{block body}{fg=black,bg=kit-blue15}% + \setbeamercolor{block title}{fg=white,bg=kit-blue100}% + \setbeamercolor{itemize item}{fg=kit-blue100} + \begin{block}#2{#1}% + }{\end{block} +\endgroup} + +\newenvironment<>{redblock}[1]{\begingroup% + \setbeamercolor{block body}{fg=black,bg=kit-red15}% + \setbeamercolor{block title}{fg=white,bg=kit-red100}% + \setbeamercolor{itemize item}{fg=kit-red100}% + \begin{block}#2{#1}% + }{\end{block}% +\endgroup} + +\newenvironment<>{brownblock}[1]{\begingroup% + \setbeamercolor{block body}{fg=black,bg=kit-brown15}% + \setbeamercolor{block title}{fg=white,bg=kit-brown100}% + \setbeamercolor{itemize item}{fg=kit-brown100}% + \begin{block}#2{#1}% + }{\end{block}% +\endgroup} + +\newenvironment<>{purpleblock}[1]{\begingroup% + \setbeamercolor{block body}{fg=black,bg=kit-purple15}% + \setbeamercolor{block title}{fg=white,bg=kit-purple100}% + \setbeamercolor{itemize item}{fg=kit-purple100}% + \begin{block}#2{#1}% + }{\end{block}% +\endgroup} + +\newenvironment<>{grayblock}[1]{\begingroup% + \setbeamercolor{block body}{fg=black,bg=kit-gray15}% + \setbeamercolor{block title}{fg=white,bg=kit-gray70}% + \setbeamercolor{itemize item}{fg=kit-gray70}% + \begin{block}#2{#1}% + }{\end{block}% +\endgroup} + +\newenvironment<>{yellowblock}[1]{\begingroup% + \setbeamercolor{block body}{fg=black,bg=kit-yellow30}% + \setbeamercolor{block title}{fg=black,bg=kit-yellow100}% + \setbeamercolor{itemize item}{fg=kit-yellow100}% + \begin{block}#2{#1}% + }{\end{block}% +\endgroup} + +\newenvironment<>{lightgreenblock}[1]{\begingroup% + \setbeamercolor{block body}{fg=black,bg=kit-lightgreen15}% + \setbeamercolor{block title}{fg=white,bg=kit-lightgreen100}% + \setbeamercolor{itemize item}{fg=kit-lightgreen100}% + \begin{block}#2{#1}% + }{\end{block}% +\endgroup} + +\newenvironment<>{orangeblock}[1]{\begingroup% + \setbeamercolor{block body}{fg=black,bg=kit-orange15}% + \setbeamercolor{block title}{fg=white,bg=kit-orange100}% + \setbeamercolor{itemize item}{fg=kit-orange100}% + \begin{block}#2{#1}% + }{\end{block}% +\endgroup} + +\newenvironment<>{cyanblock}[1]{\begingroup% + \setbeamercolor{block body}{fg=black,bg=kit-cyan15}% + \setbeamercolor{block title}{fg=white,bg=kit-cyan100}% + \setbeamercolor{itemize item}{fg=kit-cyan100}% + \begin{block}#2{#1}% + }{\end{block}% +\endgroup} + +\newenvironment<>{maroonblock}[1]{\begingroup% +\setbeamercolor{block body}{fg=black,bg=Maroon!15}% +\setbeamercolor{block title}{fg=white,bg=Maroon}% + \setbeamercolor{itemize item}{fg=Maroon}% +\begin{block}#2{#1}% +}{\end{block}% +\endgroup} +%% ------------------------------ +%% | /Block-Definitionen | +%% ------------------------------ + +%% use this for setting the total page number +\newcommand{\beginbackup}{ + \newcounter{framenumbervorappendix} + \setcounter{framenumbervorappendix}{\value{framenumber}} +} +\newcommand{\backupend}{ + \addtocounter{framenumbervorappendix}{-\value{framenumber}} + \addtocounter{framenumber}{\value{framenumbervorappendix}} +} diff --git a/21-implementierungsheft-kolloquium/slides/changes.tex b/21-implementierungsheft-kolloquium/slides/changes.tex new file mode 100644 index 0000000..692c6ce --- /dev/null +++ b/21-implementierungsheft-kolloquium/slides/changes.tex @@ -0,0 +1,84 @@ +\begin{frame}{Änderungen zum Entwurf} + +\vspace{-.4cm} +\begin{orangeblock}{Kompatibilität} + \begin{minipage}{1.7cm} + \centering + \fontsize{23pt}{0pt} + \selectfont + \textcolor{orange} + \faPlug + \end{minipage} + \hspace{-.5cm} + \begin{minipage}{.7\textwidth} + \begin{itemize} + \item Benutzernamen und E-Mail-Adresse speichern + \item Device API + \end{itemize} + \end{minipage} +\end{orangeblock} + +\begin{greenblock}{E-Mail-Verifizierung} + \begin{minipage}{1.7cm} + \centering + \fontsize{23pt}{0pt} + \selectfont + \textcolor{kit-green} + \faUserCheck + \end{minipage} + \hspace{-.5cm} + \begin{minipage}{.7\textwidth} + \begin{itemize} + \item Neuer Endpunkt zur Verifizierung der E-Mail-Adresse + \item Anmelden nur mit bestätigter E-Mail-Adresse + \end{itemize} + \end{minipage} +\end{greenblock} + +\begin{blueblock}{RSS Parser} + \begin{minipage}{1.7cm} + \centering + \fontsize{23pt}{0pt} + \selectfont + \textcolor{kit-blue} + \faRssSquare + \end{minipage} + \hspace{-.5cm} + \begin{minipage}{.7\textwidth} + \begin{itemize} + \item Asynchroner RSS Parser nach \enquote{Fire and Forget} Prinzip + \item Speichern/Löschen über Referenzen in DAO-Schicht + \end{itemize} + \end{minipage} +\end{blueblock} + +\begin{redblock}{Datenzugriffsschicht} + \begin{minipage}{1.7cm} + \centering + \fontsize{23pt}{0pt} + \selectfont + \textcolor{kit-red} + \faDatabase + \end{minipage} + \hspace{-.5cm} + \begin{minipage}{.7\textwidth} + \begin{itemize} + \item \sout{DAO-Implementierungen} (JPA-Repository) + \end{itemize} + \end{minipage} +\end{redblock} + +% +% Original +% +%\begin{itemize} +% \item Kompatibilität mit Podcatchern \begin{itemize} +% \item Speicherung von Benutzernamen und E-Mail-Adressen +% \item Device API +% \end{itemize} +% \item Verifizierung der E-Mail-Adresse +% \item RSSParser +% \item Reduzierte Datenzugriffsschicht +%\end{itemize} + +\end{frame} diff --git a/21-implementierungsheft-kolloquium/slides/classdiagram.tex b/21-implementierungsheft-kolloquium/slides/classdiagram.tex new file mode 100644 index 0000000..3186a53 --- /dev/null +++ b/21-implementierungsheft-kolloquium/slides/classdiagram.tex @@ -0,0 +1,36 @@ +\begin{frame}[t]{Einführung} + \centering + \includegraphics[width=\textwidth]{assets/diagrams/classdiagram} +\end{frame} + +% sub +% episode +% auth +% model +% util + +\begin{frame}[t]{Subscriptions-API} + \centering + \includegraphics[width=\textwidth]{assets/diagrams/classdiagram-subscriptions} +\end{frame} + +\begin{frame}[t]{EpisodeActions-API} + \centering + \includegraphics[width=\textwidth]{assets/diagrams/classdiagram-episode-actions} +\end{frame} + +\begin{frame}[t]{Authentication-API} + \centering + \includegraphics[width=.75\textwidth]{assets/diagrams/classdiagram-authentication} +\end{frame} + +\begin{frame}[t]{Model-Paket} + \centering + \includegraphics[width=\textwidth]{assets/diagrams/classdiagram-model} +\end{frame} + +\begin{frame}[t]{Util-Paket} + \centering + \includegraphics[width=\textwidth]{assets/diagrams/classdiagram-util} +\end{frame} + diff --git a/21-implementierungsheft-kolloquium/slides/components.tex b/21-implementierungsheft-kolloquium/slides/components.tex new file mode 100644 index 0000000..5a6afe6 --- /dev/null +++ b/21-implementierungsheft-kolloquium/slides/components.tex @@ -0,0 +1,7 @@ +\begin{frame}[t]{Komponenten Backend} + + \begin{figure}[h] + \includegraphics[width=0.7\textwidth]{assets/diagrams/backendComponentDiagram} + \end{figure} + +\end{frame} \ No newline at end of file diff --git a/21-implementierungsheft-kolloquium/slides/difficulties.tex b/21-implementierungsheft-kolloquium/slides/difficulties.tex new file mode 100644 index 0000000..d3e64dd --- /dev/null +++ b/21-implementierungsheft-kolloquium/slides/difficulties.tex @@ -0,0 +1,34 @@ +\begin{frame}{Schwierigkeiten} + + \begin{columns}[t] + + \begin{column}{.5\textwidth} + \centering{\fontsize{30pt}{36pt}\selectfont\faDesktop} + \vspace{.2cm} + \begin{blueblock}{Dashboard} + \begin{itemize} + \item CORS Einschränkung + \item Error-Handling + \item Kommunikation zwischen Komponenten + \end{itemize} + \end{blueblock} + \end{column} + + \begin{column}{.5\textwidth} + \centering{\fontsize{30pt}{36pt}\selectfont\faServer} + \vspace{.2cm} + \begin{orangeblock}{Server} + \begin{itemize} + \item Authentifizierung + \item API + \item EpisodeActionService (AntennaPod) + \end{itemize} + \end{orangeblock} + \end{column} + + + \end{columns} + + +\end{frame} + diff --git "a/21-implementierungsheft-kolloquium/slides/einf\303\274hrung.tex" "b/21-implementierungsheft-kolloquium/slides/einf\303\274hrung.tex" new file mode 100644 index 0000000..d818cd3 --- /dev/null +++ "b/21-implementierungsheft-kolloquium/slides/einf\303\274hrung.tex" @@ -0,0 +1,40 @@ +\begin{frame}[t]{Einführung} + + + \begin{columns}[t] + \begin{column}{.3\textwidth} + \centering{\fontsize{30pt}{36pt}\selectfont\faPodcast} + \vspace{.2cm} + \begin{block}{Podcast} + \begin{itemize} + \item RSS-Feed + \item Episoden + \item Audio/Video + \end{itemize} + \end{block} + \end{column} + \begin{column}{.3\textwidth} + \centering{\fontsize{30pt}{36pt}\selectfont\faArrowCircleDown} + \vspace{.2cm} + \begin{block}{Podcatcher} + \begin{itemize} + \item lokale Verwaltung von Podcasts + \item API Unterstützung + \item Abspielen von Episoden + \end{itemize} + \end{block} + \end{column} + \begin{column}{.3\textwidth} + \centering{\fontsize{30pt}{36pt}\selectfont\faSync} + \vspace{.2cm} + \begin{block}{Synchronisationsserver} + \begin{itemize} + \item Hörfortschritte + \item Abonnements + \item Discovery + \end{itemize} + \end{block} + \end{column} + \end{columns} + +\end{frame} \ No newline at end of file diff --git a/21-implementierungsheft-kolloquium/slides/features.tex b/21-implementierungsheft-kolloquium/slides/features.tex new file mode 100644 index 0000000..6ec435b --- /dev/null +++ b/21-implementierungsheft-kolloquium/slides/features.tex @@ -0,0 +1,37 @@ +\begin{frame}{Features} +\begin{columns}[t] +\begin{column}{.3\textwidth} +\centering{\fontsize{30pt}{36pt}\selectfont\faSync} +\vspace{.2cm} +\begin{block}{Synchronisation} +\begin{itemize} + \item Abos + \item Hörfortschritt +\end{itemize} +\end{block} +\end{column} +\begin{column}{.3\textwidth} +\centering{\fontsize{30pt}{36pt}\selectfont\faGlobe} +\vspace{.2cm} +\begin{block}{Weboberfläche} +\begin{itemize} + \item Aboliste + \item Zuletzt gehört +\end{itemize} +\end{block} +\end{column} +\begin{column}{.3\textwidth} +\centering{\fontsize{30pt}{36pt}\selectfont\faUser} +\vspace{.2cm} +\begin{block}{Account-Verwaltung} +\begin{itemize} + \item Registrieren und Anmelden + \item Passwort ändern und zurücksetzen + \item Account löschen + \item Daten importieren/exportieren +\end{itemize} +\end{block} +\end{column} +\end{columns} +\end{frame} + diff --git a/21-implementierungsheft-kolloquium/slides/gantt.tex b/21-implementierungsheft-kolloquium/slides/gantt.tex new file mode 100644 index 0000000..31ed9b9 --- /dev/null +++ b/21-implementierungsheft-kolloquium/slides/gantt.tex @@ -0,0 +1,11 @@ +\begin{frame}{Geplanter Zeitplan} + +\includegraphics[width=\textwidth]{assets/gantt-plan.eps} + +\end{frame} +\begin{frame}{Tatsächlicher Zeitplan} + +\includegraphics[width=\textwidth]{assets/gantt-reality.eps} + +\end{frame} + diff --git a/21-implementierungsheft-kolloquium/slides/integrationstrategie.tex b/21-implementierungsheft-kolloquium/slides/integrationstrategie.tex new file mode 100644 index 0000000..0564b5f --- /dev/null +++ b/21-implementierungsheft-kolloquium/slides/integrationstrategie.tex @@ -0,0 +1,82 @@ +\begin{frame}{Integrationsstrategie} + +\vspace{-.5cm} +\begin{minipage}[t]{.47\textwidth} + \begin{orangeblock}{Top-Down} + \begin{minipage}{1.6cm} + \centering + \vspace{.15cm} + \fontsize{30pt}{36pt} + \selectfont + \faSortAmountDown + \vspace{.2cm} + \end{minipage} + \hspace{-.5cm} + \begin{minipage}{\textwidth - 1.4cm} + \begin{itemize} + \item Schnelle anschauliche Ergebnisse + \item Integration von Dashboard aus zum Server + \item spätes Zusammenspiel mit Backend + \item Aufwendige/Schwere Tests + \end{itemize} + \end{minipage} + \end{orangeblock} +\end{minipage} +\hfill +\begin{minipage}[t]{.47\textwidth} + \begin{greenblock}{Bottom-Up} + \begin{minipage}{1.6cm} + \centering + \vspace{.2cm} + \fontsize{30pt}{36pt} + \selectfont + {\faSortAmountUp*} + \vspace{.25cm} + \end{minipage} + \hspace{-.5cm} + \begin{minipage}{\textwidth - 1.4cm} + \begin{itemize} + \item Integration von Datenbank aus zum Dashboard + \item viele einfache Tests sind erforderlich + \item spätes Zusammenspiel mit Frontend + \end{itemize} + \end{minipage} + \end{greenblock} +\end{minipage} +\vspace{.3cm} + +\begin{blueblock}{Outside-In} + \begin{minipage}{2cm} + \centering + \vspace{.2cm} + \fontsize{30pt}{36pt} + \selectfont + \faCompressArrows* + \vspace{.3cm} + \end{minipage} + \hspace{-.5cm} + \begin{minipage}{\textwidth - 2cm} + \begin{itemize} + \item Vereinigt Vorteile von Bottom-Up und Top-Down + \item Implementierung von Dashboard und Server gleichzeitig + \item Schnelle Tests + anschauliche Ergebnisse + \item Gut Parallelisierbar auf ganze Team + \end{itemize} + \end{minipage} +\end{blueblock} + +% \begin{greenblock}{Outside-In Prinzip} +% \begin{itemize} +% \item Eine Kombination aus dem Bottom-Up und Top-Down Prinzip +% \item Beginnt gleichzeitig auf höchster und niedrigster Logischer Ebene +% \item Schrittweise Integration aus beiden Richtungen +% \begin{itemize} +% \item Model und Datenbank +% \item Controller und Frontend +% \end{itemize} +% \item Frühzeitiges testen der Endpunkte und Unit Tests +% \item Gut Parallelisierbar auf ganze Team +% \end{itemize} +% \end{greenblock} + +\end{frame} diff --git a/21-implementierungsheft-kolloquium/slides/pattern.tex b/21-implementierungsheft-kolloquium/slides/pattern.tex new file mode 100644 index 0000000..141954b --- /dev/null +++ b/21-implementierungsheft-kolloquium/slides/pattern.tex @@ -0,0 +1,12 @@ +\begin{frame}{Entwurfsmuster} + \begin{columns} + \column{.5\textwidth} + \begin{greenblock}{Dependency Injection} + Standard (\texttt{block}) + \end{greenblock} + \column{.5\textwidth} + \begin{blueblock}{Data Access Object (DAO)} + = \texttt{exampleblock} + \end{blueblock} + \end{columns} +\end{frame} \ No newline at end of file diff --git a/21-implementierungsheft-kolloquium/slides/requirements.tex b/21-implementierungsheft-kolloquium/slides/requirements.tex new file mode 100644 index 0000000..e660bb9 --- /dev/null +++ b/21-implementierungsheft-kolloquium/slides/requirements.tex @@ -0,0 +1,58 @@ +\begin{frame}{Muss-Kriterien} +\rowcolors{1}{kit-orange60}{kit-orange30} +\begin{tabular}{ >{\bfseries}l l l} + \rowcolor{kit-orange} + Bezeichner & Zusammenfassung & Implementiert\\\hline + \textlangle RM1 \textrangle & Registrierung, Bestätigung und Anmeldung eines Accounts über das Frontend & Ja\\ + \textlangle RM2 \textrangle & Speichern von Abonnements und Episoden auf dem Server & Ja\\ + \textlangle RM3 \textrangle & Synchronisierung zwischen Podcatchern & Ja\\ + \textlangle RM4 \textrangle & Eine Weboberfläche & Ja\\ + \textlangle RM5 \textrangle & Möglichkeit das eigene Passwort zu ändern / zurückzusetzen & Ja\\ + \textlangle RM6 \textrangle & Abonnementliste in der Weboberfläche & Ja\\ + \textlangle RM7 \textrangle & 50 Anfragen / Sekunde bearbeiten & Zu testen\\ + \textlangle RM8 \textrangle & Primäre Auslegung des Webfrontends für Desktop-Nutzer & Ja\\ + \textlangle RM9 \textrangle & Unterstützung der gpodder.net RESTful-API & Ja\\ + \textlangle RM10\textrangle & Das Nutzen einer Datenbank zur Speicherung von Daten & Ja\\ + \textlangle RM11\textrangle & Erweiterte API für Kommunikation zwischen Front- und Backend & Ja +\end{tabular} +\end{frame} + +\begin{frame}{Soll-Kriterien} +\rowcolors{1}{kit-green60}{kit-green30} +\begin{tabular}{ >{\bfseries}l l l} + \rowcolor{kit-green} + Bezeichner & Zusammenfassung & Implementiert\\\hline + \textlangle RS1 \textrangle & Eine Anleitung (Platzhalter) & Ja\\ + \textlangle RS2 \textrangle & Die Möglichkeit einen Account zu löschen & Ja\\ + \textlangle RS3 \textrangle & Spring für das Backend und MariaDB als Datenbank & Ja\\ + \textlangle RS4 \textrangle & Vue.js und Bootstrap für das Frontend & Ja\\ + \textlangle RS5 \textrangle & npm und vite für das Frontend & Ja\\ + \textlangle RS6 \textrangle & Eine Single-Page-Application als Frontend mit dynamischer Aktualisierung & Ja\\ + \textlangle RS7 \textrangle & Ein RSS-Parser um Daten aus einem RSS-Feed zu lesen & Ja\\ + \textlangle RS8 \textrangle & Mindestanforderungen an ein Passwort & Ja\\ + \textlangle RS9 \textrangle & Salting und Hashing für Verschlüsselung der Personenbezogenen Daten & Ja\\ + \textlangle RS10\textrangle & Nutzer bleibt über JWT angemeldet und authentifiziert & Ja +\end{tabular} +\end{frame} + +\begin{frame}{Kann-Kriterien} + +\rowcolors{1}{kit-blue60}{kit-blue30} +\begin{tabular}{ >{\bfseries}l l l } + \rowcolor{kit-blue} + Bezeichner & Zusammenfassung & Implementiert\\\hline + \textlangle RC1 \textrangle & Abonnierten Podcast in Weboberfläche über Link teilen und hinzufügen & Ja\\ + \textlangle RC2 \textrangle & Abonnements in Weboberfläche deabonnieren & Nein\\ + \textlangle RC3 \textrangle & Importieren und Exportieren aller benutzerbezogenen Daten & Ja\\ + \textlangle RC4 \textrangle & Umsiedeln von anderen Gpodder-Plattformen & Ja\\ + \textlangle RC5 \textrangle & Kompatible Weboberfläche für beliebige gpodder.net APIs & Entfernt\\ + \textlangle RC6 \textrangle & Responsive designte Weboberfläche & Ja\\ + \textlangle RC7 \textrangle & Administratorkonten mit privilegierten Funktionen & Nein\\ + \textlangle RC8 \textrangle & OAuth 2.0 im Webfrontend & Nein\\ + \textlangle RC9 \textrangle & Bei Ausfall des Backends im Frontend angemeldet bleiben & Ja\\ + \textlangle RC10\textrangle & Mehrsprachige Weboberfläche & Ja\\ + \textlangle RC11\textrangle & Erfüllung der DSGVO & Teils\\ + \textlangle RC12\textrangle & Docker für einfaches Deployment und Sicherheit & Ja +\end{tabular} +\end{frame} + diff --git a/21-implementierungsheft-kolloquium/slides/statistics.tex b/21-implementierungsheft-kolloquium/slides/statistics.tex new file mode 100644 index 0000000..ed9b143 --- /dev/null +++ b/21-implementierungsheft-kolloquium/slides/statistics.tex @@ -0,0 +1,85 @@ +\begin{frame}{Statistiken} + +\begin{columns}[t] + \begin{column}{.4\textwidth} + \begin{orangeblock}{Dashboard} + \centering + \begin{minipage}{.2\textwidth} + \centering + \vspace{.15cm} + \fontsize{30pt}{36pt} + \selectfont + \faDesktop + \vspace{.2cm} + \end{minipage} + \begin{tabular}{l l} + \textbf{SLOC} & 2020 Zeilen \\ + \textbf{Commits} & 191 \\ + \textbf{Dateien} & 38 + \end{tabular} + \end{orangeblock} + \end{column} + + \begin{column}{.4\textwidth} + \begin{greenblock}{Server} + \centering + \begin{minipage}{.2\textwidth} + \centering + \vspace{.2cm} + \fontsize{30pt}{36pt} + \selectfont + \faServer + \vspace{.25cm} + \end{minipage} + \begin{tabular}{l l} + \textbf{SLOC} & 3812 Zeilen \\ + \textbf{Commits} & 305 \\ + \textbf{Dateien} & 60 + \end{tabular} + \end{greenblock} + \end{column} +\end{columns} +\vspace{.3cm} + +\centering +\begin{minipage}{.9\textwidth} + \begin{blueblock}{Insgesamt} + \centering + \begin{minipage}{.5\textwidth} + \centering + \begin{minipage}{.2\textwidth} + \centering + \vspace{.2cm} + \fontsize{30pt}{36pt} + \selectfont + \faChartLine + \vspace{.3cm} + \end{minipage} + \begin{tabular}{l l} + \textbf{SLOC} & 5832 Zeilen \\ + \textbf{Commits} & 486 \\ + \textbf{Dateien} & 98 + \end{tabular} + + \end{minipage} + \end{blueblock} + \small SLOC und Anzahl der Dateien würden basierend der Inhalte des + Quellordners \texttt{src/} berechnet und beinhalten keine automatisch + generierten Dateien. +\end{minipage} +\end{frame} + +\begin{frame}{Zeitlicher Verlauf der Commits} + \textbf{Dashboard} + \includegraphics[width=\textwidth]{assets/commits-dashboard.png} + \textbf{Server} + \includegraphics[width=\textwidth]{assets/commits-server.png} +\end{frame} + +\begin{frame}{Commit-Verteilung Server} + \includegraphics[width=\textwidth]{assets/contributors-server.png} +\end{frame} + +\begin{frame}{Commit-Verteilung Dashboard} + \includegraphics[width=\textwidth]{assets/contributors-dashboard.png} +\end{frame} diff --git a/21-implementierungsheft-kolloquium/slides/synchronisation.tex b/21-implementierungsheft-kolloquium/slides/synchronisation.tex new file mode 100644 index 0000000..0849279 --- /dev/null +++ b/21-implementierungsheft-kolloquium/slides/synchronisation.tex @@ -0,0 +1,36 @@ +\begin{frame}{Synchronisation} + + \tikzstyle{line} = [draw, -latex'] + + \begin{figure}[H] + + \begin{tikzpicture} + + \tikzset{focus/.style={rectangle, minimum width = 1cm, minimum height = 0.5cm, rounded corners, draw}}; + \tikzset{hyperfocus/.style={rectangle, minimum width = 1cm, minimum height = 0.5cm, draw}}; + + \node[hyperfocus, text width = 2.6cm, fill = green!25](s){Synchronisations-\\\quad \quad Server}; + \node[focus, left = 3cm of s, text width = 3.1cm, orange](p1){\quad \underline{p1:Podcatcher} + \begin{itemize} + \vspace{0.2cm} + \item Abonnements + \item Hörfortschritt + \vspace{0.1cm} + \end{itemize}}; + \node[focus, above = 1.8cm of s, orange](p2){p2}; + \node[focus, right = 2.5cm of s, orange](p3){p3}; + \node[below = 1.7cm of s](p4){\textbf{...}}; + \umlactor[left = 2.7cm of p2, blue!60]{Benutzer}; + + \draw[] (p1) -- (s); + \draw[] (p2) -- (s); + \draw[] (s) -- (p3); + \draw[] (s) -- (p4); + \path [line, thick, blue!60] (Benutzer) -- node [text width=2.5cm, midway, above=0.1cm, align=center] {Podcast abonnieren} (p2); + \path [line, thick, blue!60] (Benutzer) -| node [text width=2.5cm, midway, above=0.1cm, align=center] {Episode anhören} (p1); + + \end{tikzpicture} + + \end{figure} + +\end{frame} \ No newline at end of file diff --git a/21-implementierungsheft-kolloquium/slides/zielsetzung.tex b/21-implementierungsheft-kolloquium/slides/zielsetzung.tex new file mode 100644 index 0000000..84e1064 --- /dev/null +++ b/21-implementierungsheft-kolloquium/slides/zielsetzung.tex @@ -0,0 +1,39 @@ +\begin{frame}{Zielsetzung} + +\begin{figure}[H] + +\raggedright + +\begin{tikzpicture} + +\tikzset{focus/.style={rectangle, minimum width=1cm, minimum height=0.5cm, rounded corners=7pt, draw}}; + +\tikzset{hyperfocus/.style={rectangle, minimum width=1cm, minimum height=0.5cm, draw}}; + +\node[hyperfocus] (main) {gPodder}; +\node[focus, right = 2cm of main] (sync) {Synchronisation}; +\node[above = of sync] (share) {Inhalt teilen}; +\node[above = 0.5cm of share] (discover) {Podcasts entdecken}; +\node[text width = 3cm, below = of sync] (create) {Listen erstellen und teilen}; +\node[text width = 3cm, below = 0.5cm of create] (popular) {Publisher:\\ Was ist beliebt?}; + +\node[hyperfocus, right = 3cm of sync, text width = 4cm] (PSE) {\underline{PSE\textsuperscript{2}}\begin{itemize} + \item Schlankes Design + \item Effizient + \item Intuitiv +\end{itemize} +}; + +\draw[] (main) -- (sync); +\draw[] (main) -- (share); +\draw[] (main) |- (discover); +\draw[] (main) -- (create); +\draw[] (main) |- (popular); +\draw[stealth-, thick] (sync.east) -- ($(PSE.north west) + (0, -0.3)$); + + +\end{tikzpicture} + +\end{figure} + +\end{frame} \ No newline at end of file diff --git a/21-implementierungsheft-kolloquium/tikz-uml.sty b/21-implementierungsheft-kolloquium/tikz-uml.sty new file mode 100644 index 0000000..c6e8e0d --- /dev/null +++ b/21-implementierungsheft-kolloquium/tikz-uml.sty @@ -0,0 +1,5377 @@ +%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% Start of tikz-uml.sty +% +% Some macros for UML Diagrams. +% Home page of project : +% Author: Nicolas Kielbasiewicz +% Style from : +% Fixed by Nicolas Kielbasiewicz (nicolas.kielbasiewicz@ensta-paristech.fr) in march 2016 to compile with pgf 3.00 +%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +\NeedsTeXFormat{LaTeX2e}[1995/12/01]% +\ProvidesPackage{tikz-uml}[2011/01/26]% +% +\RequirePackage{etoolbox}% +\RequirePackage{ifthen}% +\RequirePackage{tikz}% +\RequirePackage{xstring}% +\RequirePackage{calc}% +\RequirePackage{pgfopts}% +\usetikzlibrary{backgrounds,arrows,shapes,fit,shadows,decorations.markings}% +% +\def\tikzumlPackageLayersNum{3}% +\pgfkeys{/tikzuml/options/.cd, packageLayers/.initial=3}% +\pgfkeys{/tikzuml/options/.cd, packageLayers/.store in=\tikzumlPackageLayersNum}% +\def\tikzumlStateLayersNum{3}% +\pgfkeys{/tikzuml/options/.cd, stateLayers/.initial=3}% +\pgfkeys{/tikzuml/options/.cd, stateLayers/.store in=\tikzumlStateLayersNum}% +\def\tikzumlFragmentLayersNum{3}% +\pgfkeys{/tikzuml/options/.cd, fragmentLayers/.initial=3}% +\pgfkeys{/tikzuml/options/.cd, fragmentLayers/.store in=\tikzumlFragmentLayersNum}% +\def\tikzumlComponentLayersNum{3}% +\pgfkeys{/tikzuml/options/.cd, componentLayers/.initial=3}% +\pgfkeys{/tikzuml/options/.cd, componentLayers/.store in=\tikzumlComponentLayersNum}% +% +\ProcessPgfOptions{/tikzuml/options}% +% +\def\pgfsetlayersArg{background}% +\pgfdeclarelayer{background}% +\newcounter{tikzumlPackageLayers}% +\loop \pgfdeclarelayer{package\thetikzumlPackageLayers}% + \xdef\pgfsetlayersArg{\pgfsetlayersArg,package\thetikzumlPackageLayers}% + \ifnum\tikzumlPackageLayersNum>\thetikzumlPackageLayers% + \stepcounter{tikzumlPackageLayers}% +\repeat% +% +\newcounter{tikzumlFragmentLayers}% +\loop \pgfdeclarelayer{fragment\thetikzumlFragmentLayers}% + \xdef\pgfsetlayersArg{\pgfsetlayersArg,fragment\thetikzumlFragmentLayers}% + \ifnum\tikzumlFragmentLayersNum>\thetikzumlFragmentLayers% + \stepcounter{tikzumlFragmentLayers}% +\repeat% +% +\newcounter{tikzumlStateLayers}% +\loop \pgfdeclarelayer{state\thetikzumlStateLayers}% + \xdef\pgfsetlayersArg{\pgfsetlayersArg,state\thetikzumlStateLayers}% + \ifnum\tikzumlStateLayersNum>\thetikzumlStateLayers% + \stepcounter{tikzumlStateLayers}% +\repeat% +% +\newcounter{tikzumlComponentLayers}% +\loop \pgfdeclarelayer{component\thetikzumlComponentLayers}% + \xdef\pgfsetlayersArg{\pgfsetlayersArg,component\thetikzumlComponentLayers}% + \ifnum\tikzumlComponentLayersNum>\thetikzumlComponentLayers% + \stepcounter{tikzumlComponentLayers}% +\repeat% +% +\pgfdeclarelayer{lifelines}% +\pgfdeclarelayer{activity}% +\pgfdeclarelayer{connections}% +\xdef\pgfsetlayersArg{\pgfsetlayersArg,lifelines,activity,connections,main}% +\pgfsetlayers{\pgfsetlayersArg}% +% +\pgfkeys{/tikzuml/.cd, text/.initial=black, draw/.initial=black, font/.initial=\small,% + x/.initial=0, y/.initial=0,% + package type/.initial=tikzumlEmpty, fill package/.initial=blue!20,% + class width/.initial=10ex, simple interface width/.initial=4ex, class type/.initial=class, fill class/.initial=yellow!20, fill template/.initial=yellow!2,% + narynode width/.initial=6ex,% + relation geometry/.initial=--, relation angle1/.initial=-30, relation angle2/.initial=30, relation loopsize/.initial=3em, relation weight/.initial=0.5, relation pos1/.initial=0.2, relation pos2/.initial=0.8, relation pos stereo/.initial=0.5,% + note width/.initial=3cm, fill note/.initial=green!20,% + fill system/.initial=white,% + fill usecase/.initial=blue!20,% + actor below/.initial=0.5cm,% + state join width/.initial=3ex,% + state decision width/.initial=3ex,% + state initial width/.initial=5ex,% + state final width/.initial=5.5ex,% + state enter width/.initial=5ex,% + state exit width/.initial=5ex,% + state end width/.initial=5ex,% + state history width/.initial=5ex,% + state deep history width/.initial=5ex,% + state width/.initial=8ex, fill state/.initial=yellow!20,% + object stereo/.initial=object, fill object/.initial=yellow!20,% + call dt/.initial=tikzumlEmpty, call padding/.initial=2, call type/.initial=synchron, fill call/.initial=white,% + fragment type/.initial=opt, fragment inner xsep/.initial=1, fragment inner ysep/.initial=1, fill fragment/.initial= none,% + create call dt/.initial=4,% + component width/.initial=8ex, fill component/.initial= yellow!20,% + required interface distance/.initial=2.5cm, required interface width/.initial=1em, required interface padding/.initial=1cm,% + provided interface distance/.initial=3cm, provided interface width/.initial=1em, provided interface padding/.initial=1cm,% + port width/.initial=1ex, fill port/.initial= yellow!20,% + fill assembly connector/.initial= white,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \errmessage{TIKZUML ERROR : in tikzuml global, invalid option \keyname}% + }% +}% +% +\pgfkeys{/tikzuml/.cd, text/.get=\tikzumlDefaultTextColor, draw/.get=\tikzumlDefaultDrawColor, font/.get=\tikzumlDefaultFont,% + x/.get=\tikzumlDefaultX, y/.get=\tikzumlDefaultY,% + package type/.get=\tikzumlPackageDefaultType, fill package/.get=\tikzumlPackageDefaultFillColor,% + class width/.get=\tikzumlClassDefaultWidth, simple interface width/.get=\tikzumlSimpleInterfaceDefaultWidth, class type/.get=\tikzumlClassDefaultType, fill class/.get=\tikzumlClassDefaultFillColor, fill template/.get=\tikzumlClassTemplateFillColorDefaultFillColor,% + narynode width/.get=\tikzumlNaryNodeDefaultWidth,% + relation geometry/.get=\tikzumlRelationDefaultGeometry, relation angle1/.get=\tikzumlRelationDefaultAngleO, relation angle2/.get=\tikzumlRelationDefaultAngleT, relation loopsize/.get=\tikzumlRelationDefaultLoopSize, relation weight/.get=\tikzumlRelationDefaultWeight, relation pos1/.get=\tikzumlRelationDefaultPosO, relation pos2/.get=\tikzumlRelationDefaultPosT, relation pos stereo/.get=\tikzumlRelationDefaultPosStereo,% + note width/.get=\tikzumlNoteDefaultWidth, fill note/.get=\tikzumlNoteDefaultFillColor,% + fill system/.get=\tikzumlSystemDefaultFillColor,% + fill usecase/.get=\tikzumlUseCaseDefaultFillColor,% + actor below/.get=\tikzumlActorDefaultBelow,% + state join width/.get=\tikzumlStateJoinDefaultWidth,% + state decision width/.get=\tikzumlStateDecisionDefaultWidth,% + state initial width/.get=\tikzumlStateInitialDefaultWidth,% + state final width/.get=\tikzumlStateFinalDefaultWidth,% + state enter width/.get=\tikzumlStateEnterDefaultWidth,% + state exit width/.get=\tikzumlStateExitDefaultWidth,% + state end width/.get=\tikzumlStateEndDefaultWidth,% + state history width/.get=\tikzumlStateHistoryDefaultWidth,% + state deep history width/.get=\tikzumlStateDeepHistoryDefaultWidth,% + state width/.get=\tikzumlStateDefaultWidth, fill state/.get=\tikzumlStateDefaultFillColor,% + object stereo/.get=\tikzumlObjectDefaultStereo, fill object/.get=\tikzumlObjectDefaultFillColor,% + call dt/.get=\tikzumlCallDefaultDT, call padding/.get=\tikzumlCallDefaultPadding, call type/.get=\tikzumlCallDefaultType, fill call/.get=\tikzumlCallDefaultFillColor,% + fragment type/.get=\tikzumlFragmentDefaultType, fragment inner xsep/.get=\tikzumlFragmentDefaultXSep, fragment inner ysep/.get=\tikzumlFragmentDefaultYSep, fill fragment/.get=\tikzumlFragmentDefaultFillColor,% + create call dt/.get=\tikzumlCreateCallDefaultDT,% + component width/.get=\tikzumlComponentDefaultWidth, fill component/.get=\tikzumlComponentDefaultFillColor,% + required interface distance/.get=\tikzumlRequiredInterfaceDefaultDistance, required interface width/.get=\tikzumlRequiredInterfaceDefaultWidth, required interface padding/.get=\tikzumlRequiredInterfaceDefaultPadding,% + provided interface distance/.get=\tikzumlProvidedInterfaceDefaultDistance, provided interface width/.get=\tikzumlProvidedInterfaceDefaultWidth, provided interface padding/.get=\tikzumlProvidedInterfaceDefaultPadding,% + port width/.get=\tikzumlPortDefaultWidth, fill port/.get=\tikzumlPortDefaultFillColor,% + fill assembly connector/.get=\tikzumlAssemblyConnectorDefaultFillColor% +}% +% +% utility : change default colors +\newcommand{\tikzumlset}[1]{% + \pgfkeys{/tikzuml/.cd,#1}% + \pgfkeys{/tikzuml/.cd, text/.get=\tikzumlDefaultTextColor, draw/.get=\tikzumlDefaultDrawColor, font/.get=\tikzumlDefaultFont,% + x/.get=\tikzumlDefaultX, y/.get=\tikzumlDefaultY,% + package type/.get=\tikzumlPackageDefaultType, fill package/.get=\tikzumlPackageDefaultFillColor,% + class width/.get=\tikzumlClassDefaultWidth, simple interface width/.get=\tikzumlSimpleInterfaceDefaultWidth, class type/.get=\tikzumlClassDefaultType, fill class/.get=\tikzumlClassDefaultFillColor, fill template/.get=\tikzumlClassTemplateFillColorDefaultFillColor,% + narynode width/.get=\tikzumlNaryNodeWidth,% + relation geometry/.get=\tikzumlRelationDefaultGeometry, relation angle1/.get=\tikzumlRelationDefaultAngleO, relation angle2/.get=\tikzumlRelationDefaultAngleT, relation loopsize/.get=\tikzumlRelationDefaultLoopSize, relation weight/.get=\tikzumlRelationDefaultWeight, relation pos1/.get=\tikzumlRelationDefaultPosO, relation pos2/.get=\tikzumlRelationDefaultPosT, relation pos stereo/.get=\tikzumlRelationDefaultPosStereo,% + note width/.get=\tikzumlNoteDefaultWidth, fill note/.get=\tikzumlNoteDefaultFillColor,% + fill system/.get=\tikzumlSystemDefaultFillColor,% + fill usecase/.get=\tikzumlUseCaseDefaultFillColor,% + actor below/.get=\tikzumlActorDefaultBelow,% + state join width/.get=\tikzumlStateJoinDefaultWidth,% + state decision width/.get=\tikzumlStateDecisionDefaultWidth,% + state initial width/.get=\tikzumlStateInitialDefaultWidth,% + state final width/.get=\tikzumlStateFinalDefaultWidth,% + state enter width/.get=\tikzumlStateEnterDefaultWidth,% + state exit width/.get=\tikzumlStateExitDefaultWidth,% + state end width/.get=\tikzumlStateEndDefaultWidth,% + state history width/.get=\tikzumlStateHistoryDefaultWidth,% + state deep history width/.get=\tikzumlStateDeepHistoryDefaultWidth,% + state width/.get=\tikzumlStateDefaultWidth, fill state/.get=\tikzumlStateDefaultFillColor,% + object stereo/.get=\tikzumlObjectDefaultStereo, fill object/.get=\tikzumlObjectDefaultFillColor,% + call dt/.get=\tikzumlCallDefaultDT, call padding/.get=\tikzumlCallDefaultPadding, call type/.get=\tikzumlCallDefaultType, fill call/.get=\tikzumlCallDefaultFillColor,% + fragment type/.get=\tikzumlFragmentDefaultType, fragment inner xsep/.get=\tikzumlFragmentDefaultXSep, fragment inner ysep/.get=\tikzumlFragmentDefaultYSep, fill fragment/.get=\tikzumlFragmentDefaultFillColor,% + create call dt/.get=\tikzumlCreateCallDT,% + component width/.get=\tikzumlComponentDefaultWidth, fill component/.get=\tikzumlComponentDefaultFillColor,% + required interface distance/.get=\tikzumlRequiredInterfaceDefaultDistance, required interface width/.get=\tikzumlRequiredInterfaceDefaultWidth, required interface padding/.get=\tikzumlRequiredInterfaceDefaultPadding,% + provided interface distance/.get=\tikzumlProvidedInterfaceDefaultDistance, provided interface width/.get=\tikzumlProvidedInterfaceDefaultWidth, provided interface padding/.get=\tikzumlProvidedInterfaceDefaultPadding,% + port width/.get=\tikzumlPortDefaultWidth, fill port/.get=\tikzumlPortDefaultFillColor,% + fill assembly connector/.get=\tikzumlAssemblyConnectorDefaultFillColor% + }% +}% +% +% define a point +% arg : node/coordinates of the point +\newcommand{\umlpoint}[1]{% + \begin{pgfonlayer}{connections}% + \node[tikzuml control nodes style] at (#1) {};% + \end{pgfonlayer}% +}% +% +\newcommand{\tikzumlskipescape}[3][_]{% +\begingroup% + \def\_{#1}\edef\x{\endgroup% + \def\noexpand\csname #3\endcsname{#2}}\x% +}% +% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% class diagrams % +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +\pgfkeys{/tikzuml/relation/.cd, attr1/.style args={#1|#2}{arg1=#1, mult1=#2},% + attr2/.style args={#1|#2}{arg2=#1, mult2=#2},% + attr/.style args={#1|#2}{arg=#1, mult=#2},% + recursive/.style args={#1|#2|#3}{angle1=#1, angle2=#2, loopsize=#3},% + anchors/.style args={#1 and #2}{anchor1=#1, anchor2=#2},% + recursive direction/.style args={#1 to #2}{recursive direction start=#1, recursive direction end=#2}% +}% +% +\pgfkeys{/tikzuml/note/.cd, anchors/.style args={#1 and #2}{anchor1=#1, anchor2=#2}}% +% +\tikzstyle{tikzuml simpleclass style}=[rectangle, minimum height=2em, node distance=2em]% +\tikzstyle{tikzuml simpleinterface style}=[circle, minimum height=1em, node distance=1em]% +\tikzstyle{tikzuml class style}=[rectangle split, rectangle split parts=3, rectangle split part align={center, left, left}, minimum height=2em, node distance=2em]% +\tikzstyle{tikzuml narynode style}=[diamond]% +\tikzstyle{tikzuml template style}=[dashed, inner ysep=0.5em, inner xsep=1ex]% +\tikzstyle{tikzuml control nodes style}=[fill=black, inner sep=1.5pt, circle]% +% +\tikzstyle{tikzuml association style}=[color=\tikzumlDefaultDrawColor, -]% +\tikzstyle{tikzuml bidirectional association style}=[color=\tikzumlDefaultDrawColor, angle45-angle45]% +\tikzstyle{tikzuml unidirectional association style}=[color=\tikzumlDefaultDrawColor, -angle 45]% +\tikzstyle{tikzuml aggregation style}=[color=\tikzumlDefaultDrawColor, open diamond-]% +\tikzstyle{tikzuml unidirectional aggregation style}=[color=\tikzumlDefaultDrawColor, open diamond-angle 45]% +\tikzstyle{tikzuml composition style}=[color=\tikzumlDefaultDrawColor, diamond-]% +\tikzstyle{tikzuml unidirectional composition style}=[color=\tikzumlDefaultDrawColor, diamond-angle 45]% +\tikzstyle{tikzuml nesting style}=[color=\tikzumlDefaultDrawColor]% +\tikzstyle{tikzuml dependency style}=[color=\tikzumlDefaultDrawColor, -angle 45, dashed]% +\tikzstyle{tikzuml import style}=[color=\tikzumlDefaultDrawColor, -angle 45, dashed]% +\tikzstyle{tikzuml inherit style}=[color=\tikzumlDefaultDrawColor, -open triangle 45]% +\tikzstyle{tikzuml implements style}=[color=\tikzumlDefaultDrawColor, -open triangle 45, dashed]% +% +\pgfkeys{/tikzuml/assemblyconnectorrelation/.cd, anchors/.style args={#1 and #2}{anchor1=#1, anchor2=#2}}% +% +\newcounter{tikzumlPackageClassNum}% +\newcounter{tikzumlPackageSubPackageNum}% +\newcounter{tikzumlRelationNum}% +\setcounter{tikzumlRelationNum}{1}% +\newcounter{tikzumlNoteNum}% +\setcounter{tikzumlNoteNum}{1}% +% +\newcounter{pos}% +\newcounter{posT}% +\newcounter{posStereo}% +% +\newcounter{tikzumlPackageLevel}% +\setcounter{tikzumlPackageLevel}{0}% +% +\newif\iftikzumlpackageSimpleStyle% +\newif\iftikzumlclassSimpleStyle% +\newif\iftikzumlclassCircleShape% +\newif\iftikzumlpackageWithoutCoords% +\newif\iftikzumlclassWithoutCoords% +\newif\iftikzumlassocclassWithoutCoords% +\newif\iftikzumlnoteWithoutCoords% +% +% define a uml package +% arg : package name +% optional : x, y: coordinates of the package +% type: stereotype of the package +% name: name of the package node +% draw, fill, text: colors +% style: to manage every default TikZ option +% no coords: to tell that the package position is defined relatively +% to another node (automatically used with TikZ options above, below, left, right, below left, ...) +\newenvironment{umlpackage}[2][]{% + \pgfkeys{/tikzuml/package/.cd, x/.initial=\tikzumlDefaultX, y/.initial=\tikzumlDefaultX, style/.style={},% + name/.initial=tikzumlEmpty, type/.initial=\tikzumlPackageDefaultType, draw/.initial=\tikzumlDefaultDrawColor,% + fill/.initial=\tikzumlPackageDefaultFillColor, text/.initial=\tikzumlDefaultTextColor,% + no coords/.is if=tikzumlpackageWithoutCoords,% + no coords=false,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{above}\OR% + \equal{\keyname}{left}\OR% + \equal{\keyname}{below}\OR% + \equal{\keyname}{right}\OR% + \equal{\keyname}{above left}\OR% + \equal{\keyname}{above right}\OR% + \equal{\keyname}{below left}\OR% + \equal{\keyname}{below right}}{% + \IfSubStr{\keyvalue}{ of }{% + \pgfkeys{/tikzuml/package/.cd, no coords}% + }{}% + }{}% + \ifx\keyvalue\pgfkeysnovalue% + \pgfkeys{/tikzuml/package/.cd, style/.append style/.expand once={\keyname}}% + \else% + \pgfkeys{/tikzuml/package/.cd, style/.append style/.expand twice={\expandafter\keyname\expandafter=\keyvalue}}% + \fi% + %\errmessage{TIKZUML ERROR : in umlpackage, invalid option \keyname}% + }% + }% + \pgfkeys{/tikzuml/package/.cd, #1}% + \pgfkeys{/tikzuml/package/.cd, x/.get=\tikzumlPackageXShift, y/.get=\tikzumlPackageYShift, name/.get=\tikzumlPackageName, type/.get=\tikzumlPackageTypeTmp,% + draw/.get=\tikzumlPackageDrawColor, fill/.get=\tikzumlPackageFillColor,% + text/.get=\tikzumlPackageTextColor% + }% + % + + % + \ifthenelse{\equal{\tikzumlPackageTypeTmp}{tikzumlEmpty}}{% + \def\tikzumlPackageType{}% + }{% + \expandafter\def\expandafter\tikzumlPackageType\expandafter{$\ll$\tikzumlPackageTypeTmp$\gg$ \\}% + }% + % + \ifnum\thetikzumlPackageLevel>0% + \let\tikzumlPackage@nameold\tikzumlPackage@fitname% + \def\tikzumlPackage@name{#2}% + \begingroup% + \def\_{@}\edef\x{\endgroup% + \def\noexpand\tikzumlPackage@fitname{\tikzumlPackage@name}}\x% + \let\tikzumlPackage@parentold\tikzumlPackage@parent% + \edef\tikzumlPackage@parent{\tikzumlPackage@parentold @@\tikzumlPackage@nameold}% + \else% + \def\tikzumlPackage@parent{}% + \def\tikzumlPackage@name{#2}% + \begingroup% + \def\_{@}\edef\x{\endgroup% + \def\noexpand\tikzumlPackage@fitname{\tikzumlPackage@name}}\x% + \fi% + % + \let\tikzumlPackage@nodeNameold\tikzumlPackage@nodeName% + % + \begingroup% + \def\_{_}\edef\x{\endgroup% + \def\noexpand\tikzumlPackage@nodeName{\tikzumlPackage@name}}\x% + % + \ifthenelse{\equal{\tikzumlPackageName}{tikzumlEmpty}}{}{% + \def\tikzumlPackage@nodeName{\tikzumlPackageName}% + }% + % + \StrSubstitute{\tikzumlPackage@nodeName}{.}{@POINT@}{\tikzumlPackage@nodeName}% + % + \expandafter\gdef\csname tikzumlPackageFit\tikzumlPackage@parent @@\tikzumlPackage@fitname\endcsname{}% + % + \setcounter{tikzumlPackageClassNum}{0}% + \setcounter{tikzumlPackageSubPackageNum}{0}% + \stepcounter{tikzumlPackageLevel}% + % + \begin{scope}[xshift=\tikzumlPackageXShift cm, yshift=\tikzumlPackageYShift cm]% +}{% + \addtocounter{tikzumlPackageLevel}{-1}% + \begin{pgfonlayer}{package\thetikzumlPackageLevel}% + % + % if contains no class, and not simple, one define a fictive node to enable the fit option + \ifnum\c@tikzumlPackageClassNum=0% + \ifnum\c@tikzumlPackageSubPackageNum=0% + \iftikzumlpackageWithoutCoords% + \node[inner sep=1.5ex, /tikzuml/package/style] (\tikzumlPackage@nodeName-root) {\phantom{\tikzumlPackage@nodeName}};% + \else% + \node[inner sep=1.5ex, /tikzuml/package/style] (\tikzumlPackage@nodeName-root) at (0,0) {\phantom{\tikzumlPackage@nodeName}};% + \fi% + \expandafter\xdef\csname tikzumlPackageFit\tikzumlPackage@parent @@\tikzumlPackage@fitname\endcsname{(\tikzumlPackage@nodeName-root)}% + \fi% + \fi% + % + \ifnum\c@tikzumlPackageLevel>0% + \def\tikzumlPackageFitTmp{\csname tikzumlPackageFit\tikzumlPackage@parent\endcsname}% + \expandafter\xdef\csname tikzumlPackageFit\tikzumlPackage@parent\endcsname{\tikzumlPackageFitTmp (\tikzumlPackage@nodeName) (\tikzumlPackage@nodeName-caption)}% + \stepcounter{tikzumlPackageSubPackageNum}% + \fi% + % + \node[draw=\tikzumlPackageDrawColor, fill=\tikzumlPackageFillColor, text=\tikzumlPackageTextColor, font=\tikzumlDefaultFont, inner sep=1.5ex, /tikzuml/package/style, fit = \csname tikzumlPackageFit\tikzumlPackage@parent @@\tikzumlPackage@fitname\endcsname] (\tikzumlPackage@nodeName) {};% + \node[draw=\tikzumlPackageDrawColor, fill=\tikzumlPackageFillColor, text=\tikzumlPackageTextColor, font=\tikzumlDefaultFont, minimum height=1.5em, outer ysep=-0.3, anchor=south west] (\tikzumlPackage@nodeName-caption) at (\tikzumlPackage@nodeName.north west) {\begin{tabular}{c} \tikzumlPackageType \textbf{\tikzumlPackage@name}\end{tabular}};% + \end{pgfonlayer}% + \end{scope}% +}% +% +% shortcut to define an empty package +\newcommand{\umlemptypackage}[2][]{\begin{umlpackage}[#1]{#2} \end{umlpackage}}% +% +% define a uml class +% args : name of the class +% attributes of the class +% operations of the class +% optional : x, y: coordinates of the class +% width: of the class node +% type: type of class (class, interface, typedef, enum) +% tags: tagged values of class +% template: template parameters +% simple: if used, class is empty and drawn with a rectangle +% circle: if used with simple, class is empty and drawn with a circle +% draw, fill, fill template, and text: colors +% style: to manage every default TikZ option +% no coords: to tell that the class position is defined relatively +% to another node (automatically used with TikZ options above, below, left, right, below left, ...) +\newcommand{\umlclass}[4][]{% + \pgfkeys{/tikzuml/class/.cd, x/.initial=\tikzumlDefaultX, y/.initial=\tikzumlDefaultX, width/.initial=\tikzumlClassDefaultWidth, type/.initial=\tikzumlClassDefaultType,% + tags/.initial={}, style/.style={},% + template/.initial={}, name/.initial=tikzumlEmpty,% + draw/.initial=\tikzumlDefaultDrawColor,% + fill template/.initial=\tikzumlClassTemplateFillColorDefaultFillColor,% + fill/.initial=\tikzumlClassDefaultFillColor,% + text/.initial=\tikzumlDefaultTextColor,% + simple/.is if=tikzumlclassSimpleStyle, circle/.is if=tikzumlclassCircleShape, no coords/.is if=tikzumlclassWithoutCoords,% + simple=false, circle=false, no coords=false,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{above}\OR% + \equal{\keyname}{left}\OR% + \equal{\keyname}{below}\OR% + \equal{\keyname}{right}\OR% + \equal{\keyname}{above left}\OR% + \equal{\keyname}{above right}\OR% + \equal{\keyname}{below left}\OR% + \equal{\keyname}{below right}}{% + \IfSubStr{\keyvalue}{ of }{% + \pgfkeys{/tikzuml/class/.cd, no coords}% + }{}% + }{}% + \ifx\keyvalue\pgfkeysnovalue% + \pgfkeys{/tikzuml/class/.cd, style/.append style/.expand once={\keyname}}% + \else% + \pgfkeys{/tikzuml/class/.cd, style/.append style/.expand twice={\expandafter\keyname\expandafter=\keyvalue}}% + \fi% + %\errmessage{TIKZUML ERROR : in umlclass, invalid option \keyname}% + }% + }% + \pgfkeys{/tikzuml/class/.cd,#1}% + % + \iftikzumlclassSimpleStyle% + \iftikzumlclassCircleShape% + \pgfkeys{/tikzuml/class/.cd, width/.initial=\tikzumlSimpleInterfaceDefaultWidth}% + \fi% + \fi% + % + \pgfkeys{/tikzuml/class/.cd, x/.get=\tikzumlClassX, y/.get=\tikzumlClassY, width/.get=\tikzumlClassMinimumWidth,% + type/.get=\tikzumlClassTypeTmp, tags/.get=\tikzumlClassTagsTmp, template/.get=\tikzumlClassTemplateFillColorParam,% + name/.get=\tikzumlClassName,% + draw/.get=\tikzumlClassDrawColor, fill/.get=\tikzumlClassFillColor,% + text/.get=\tikzumlClassTextColor, fill template/.get=\tikzumlClassTemplateFillColor% + }% + % + \ifthenelse{\equal{\tikzumlClassTypeTmp}{class}\OR\equal{\tikzumlClassTypeTmp}{abstract}}{% + \def\tikzumlClassType{}% + }{% + \expandafter\def\expandafter\tikzumlClassType\expandafter{$\ll$\tikzumlClassTypeTmp$\gg$ \\}% + }% + % + \ifthenelse{\equal{\tikzumlClassTagsTmp}{}}{% + \def\tikzumlClassTags{}% + }{% + \def\tikzumlClassTags{\\ \{\tikzumlClassTagsTmp\}}% + }% + % + \ifthenelse{\equal{\tikzumlClassTemplateFillColorParam}{}}{% + \def\tikzumlClassVPadding{}% + \def\tikzumlClassHPadding{}% + }{% + \def\tikzumlClassVPadding{\vspace{0.1em} \\}% + \def\tikzumlClassHPadding{\hspace{0.5ex} $ $}% + }% + % + \def\tikzumlClassName{#2}% + % + \begingroup% + \def\_{_}\edef\x{\endgroup% + \def\noexpand\tikzumlClassNodeName{\tikzumlClassName}}\x% + % + \ifthenelse{\equal{\tikzumlClassName}{tikzumlEmpty}}{}{% + \def\tikzumlClassNodeName{\tikzumlClassName}% + }% + % + \StrSubstitute{\tikzumlClassNodeName}{:}{@COLON@}[\tikzumlClassNodeName]% + \StrSubstitute{\tikzumlClassNodeName}{\_}{@UNDERSCORE@}[\tikzumlClassNodeName]% + % + \ifthenelse{\equal{\tikzumlClassTypeTmp}{abstract}}{% + \let\tikzumlClassNameOld\tikzumlClassName% + \def\tikzumlClassName{{\it \tikzumlClassNameOld}}% + }{}% + % + \def\tikzumlClassPos{\tikzumlClassX,\tikzumlClassY}% + \def\tikzumlClassAttributes{#3}% + \def\tikzumlClassOperations{#4}% + % + \iftikzumlclassSimpleStyle% + \iftikzumlclassWithoutCoords% + \iftikzumlclassCircleShape% + \node[tikzuml simpleinterface style, draw=\tikzumlClassDrawColor, fill=\tikzumlClassFillColor, text=\tikzumlClassTextColor, font=\tikzumlDefaultFont, minimum width=\tikzumlClassMinimumWidth, /tikzuml/class/style] (\tikzumlClassNodeName) {};% + \node[anchor=south] (\tikzumlClassNodeName-label) at (\tikzumlClassNodeName.north) {\begin{tabular}{c}\tikzumlClassVPadding \tikzumlClassType \tikzumlClassHPadding \textbf{\tikzumlClassName} \tikzumlClassHPadding \tikzumlClassTags \end{tabular}% + };% + \else% + \node[tikzuml simpleclass style, draw=\tikzumlClassDrawColor, fill=\tikzumlClassFillColor, text=\tikzumlClassTextColor, font=\tikzumlDefaultFont, minimum width=\tikzumlClassMinimumWidth, /tikzuml/class/style] (\tikzumlClassNodeName) {\begin{tabular}{c}\tikzumlClassVPadding \tikzumlClassType \tikzumlClassHPadding \textbf{\tikzumlClassName} \tikzumlClassHPadding \tikzumlClassTags \end{tabular}% + };% + \fi% + \else% + \iftikzumlclassCircleShape% + \node[tikzuml simpleinterface style, draw=\tikzumlClassDrawColor, fill=\tikzumlClassFillColor, text=\tikzumlClassTextColor, font=\tikzumlDefaultFont, minimum width=\tikzumlClassMinimumWidth, /tikzuml/class/style] (\tikzumlClassNodeName) at (\tikzumlClassPos) {}; + \node[anchor=south] (\tikzumlClassNodeName-label) at (\tikzumlClassNodeName.north){\begin{tabular}{c}\tikzumlClassVPadding \tikzumlClassType \tikzumlClassHPadding \textbf{\tikzumlClassName} \tikzumlClassHPadding \tikzumlClassTags \end{tabular}% + };% + \else% + \node[tikzuml simpleclass style, draw=\tikzumlClassDrawColor, fill=\tikzumlClassFillColor, text=\tikzumlClassTextColor, font=\tikzumlDefaultFont, minimum width=\tikzumlClassMinimumWidth, /tikzuml/class/style] (\tikzumlClassNodeName) at (\tikzumlClassPos) {\begin{tabular}{c}\tikzumlClassVPadding \tikzumlClassType \tikzumlClassHPadding \textbf{\tikzumlClassName} \tikzumlClassHPadding \tikzumlClassTags \end{tabular}% + };% + \fi% + \fi% + \else% + \iftikzumlclassWithoutCoords% + \node[tikzuml class style, draw=\tikzumlClassDrawColor, fill=\tikzumlClassFillColor, text=\tikzumlClassTextColor, font=\tikzumlDefaultFont, minimum width=\tikzumlClassMinimumWidth, /tikzuml/class/style] (\tikzumlClassNodeName) {\begin{tabular}{c}\tikzumlClassVPadding \tikzumlClassType \tikzumlClassHPadding \textbf{\tikzumlClassName} \tikzumlClassHPadding \tikzumlClassTags \end{tabular}% + \nodepart{second}% + \begin{tabular}{l}% + \tikzumlClassAttributes% + \end{tabular}% + \nodepart{third}% + \begin{tabular}{l}% + \tikzumlClassOperations% + \end{tabular}% + };% + \else% + \node[tikzuml class style, draw=\tikzumlClassDrawColor, fill=\tikzumlClassFillColor, text=\tikzumlClassTextColor, font=\tikzumlDefaultFont, minimum width=\tikzumlClassMinimumWidth, /tikzuml/class/style] (\tikzumlClassNodeName) at (\tikzumlClassPos) {\begin{tabular}{c}\tikzumlClassVPadding \tikzumlClassType \tikzumlClassHPadding \textbf{\tikzumlClassName} \tikzumlClassHPadding \tikzumlClassTags \end{tabular}% + \nodepart{second}% + \begin{tabular}{l}% + \tikzumlClassAttributes% + \end{tabular}% + \nodepart{third}% + \begin{tabular}{l}% + \tikzumlClassOperations% + \end{tabular}% + };% + \fi% + \fi% + % + \ifthenelse{\equal{\tikzumlClassTemplateFillColorParam}{}}{}{% + \draw (\tikzumlClassNodeName.north east) node[tikzuml template style, name=\tikzumlClassNodeName-template, draw=\tikzumlClassDrawColor, fill=\tikzumlClassTemplateFillColor, text=\tikzumlClassTextColor, font=\tikzumlDefaultFont] {\tikzumlClassTemplateFillColorParam};% + }% + % + % add to fit + \ifnum\c@tikzumlPackageLevel>0% + \edef\tikzumlPackageFitOld{\csname tikzumlPackageFit\tikzumlPackage@parent @@\tikzumlPackage@fitname\endcsname}% + \ifthenelse{\equal{\tikzumlClassTemplateFillColorParam}{}}{% + \expandafter\xdef\csname tikzumlPackageFit\tikzumlPackage@parent @@\tikzumlPackage@fitname\endcsname{\tikzumlPackageFitOld (\tikzumlClassNodeName)}% + }{% + \expandafter\xdef\csname tikzumlPackageFit\tikzumlPackage@parent @@\tikzumlPackage@fitname\endcsname{\tikzumlPackageFitOld (\tikzumlClassNodeName) (\tikzumlClassNodeName-template)}% + }% + \stepcounter{tikzumlPackageClassNum}% + \fi% + \ifnum\c@tikzumlComponentLevel>0% + \def\tikzumlComponentFitTmp{\csname tikzumlComponentFit\tikzumlComponent@parent @@\tikzumlComponent@fitname\endcsname}% + \ifthenelse{\equal{\tikzumlClassTemplateFillColorParam}{}}{% + \expandafter\xdef\csname tikzumlComponentFit\tikzumlComponent@parent @@\tikzumlComponent@fitname\endcsname{\tikzumlComponentFitTmp (\tikzumlClassNodeName)}% + }{% + \expandafter\xdef\csname tikzumlComponentFit\tikzumlComponent@parent @@\tikzumlComponent@fitname\endcsname{\tikzumlComponentFitTmp (\tikzumlClassNodeName) (\tikzumlClassNodeName-template)}% + }% + \stepcounter{tikzumlComponentSubComponentNum}% + \fi% +}% +% +% shortcuts for interface, enum and typedef environments +\newcommand{\umlabstract}[4][]{% + \pgfkeys{/tikzuml/class/.cd, .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{type}}{% + \errmessage{TIKZUML ERROR : in umlabstract, forbidden option type}% + }{}% + }% + }% + \pgfkeys{/tikzuml/class/.cd, #1}% + \umlclass[type=abstract,#1]{#2}{#3}{#4}% +}% +\newcommand{\umlinterface}[4][]{% + \pgfkeys{/tikzuml/class/.cd, .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{type}}{% + \errmessage{TIKZUML ERROR : in umlinterface, forbidden option type}% + }{}% + }% + }% + \pgfkeys{/tikzuml/class/.cd, #1}% + \umlclass[type=interface,#1]{#2}{#3}{#4}% +}% +\newcommand{\umltypedef}[4][]{% + \pgfkeys{/tikzuml/class/.cd, .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{type}}{% + \errmessage{TIKZUML ERROR : in umltypedef, forbidden option type}% + }{}% + }% + }% + \pgfkeys{/tikzuml/class/.cd, #1}% + \umlclass[type=typedef,#1]{#2}{#3}{#4}% +}% +\newcommand{\umlenum}[4][]{% + \pgfkeys{/tikzuml/class/.cd, .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{type}}{% + \errmessage{TIKZUML ERROR : in umlenum, forbidden option type}% + }{}% + }% + }% + \pgfkeys{/tikzuml/class/.cd, #1}% + \umlclass[type=enum,#1]{#2}{#3}{#4} +}% +% +% shortcut to define an empty class +\newcommand{\umlemptyclass}[2][]{\umlclass[#1]{#2}{}{}}% +\newcommand{\umlsimpleclass}[2][]{% + \pgfkeys{/tikzuml/class/.cd, .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{simple}}{% + \errmessage{TIKZUML ERROR : in umlsimpleclass, forbidden option simple}% + }{}% + }% + }% + \pgfkeys{/tikzuml/class/.cd, #1}% + \umlemptyclass[simple, #1]{#2}% +}% +% +\newcommand{\umlsimpleinterface}[2][]{% + \pgfkeys{/tikzuml/class/.cd, .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{simple}}{% + \errmessage{TIKZUML ERROR : in umlsimpleinterface, forbidden option simple}% + }{% + \ifthenelse{\equal{\keyname}{circle}}{% + \errmessage{TIKZUML ERROR : in umlsimpleinterface, forbidden option circle}% + }{}% + }% + }% + }% + \pgfkeys{/tikzuml/class/.cd, #1}% + \umlsimpleclass[circle, #1]{#2}% +}% +% underline the text for static arg +\newcommand{\umlstatic}[1]{\underline{#1}}% +\newcommand{\umlvirt}[1]{\textit{#1}}% +% +% define node for n-ary association +\newcommand{\umlNarynode}[2][]{% + \def\tikzumlNaryNodeAnchor{.north} + \def\tikzumlNaryNodeLabelPos{above} + \pgfkeys{/tikzuml/narynode/.cd, x/.initial=\tikzumlDefaultX, y/.initial=\tikzumlDefaultX, width/.initial=\tikzumlNaryNodeDefaultWidth, name/.initial=tikzumlEmpty,% + draw/.initial=\tikzumlDefaultDrawColor, fill/.initial=\tikzumlClassDefaultFillColor,% + text/.initial=\tikzumlDefaultTextColor, style/.style={},% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{above}}{% + \def\tikzumlNaryNodeAnchor{.north}% + \def\tikzumlNaryNodeLabelPos{above}% + }{% + \ifthenelse{\equal{\keyname}{above left}}{% + \def\tikzumlNaryNodeAnchor{.north west}% + \def\tikzumlNaryNodeLabelPos{above left}% + }{% + \ifthenelse{\equal{\keyname}{left}}{% + \def\tikzumlNaryNodeAnchor{.west}% + \def\tikzumlNaryNodeLabelPos{left}% + }{% + \ifthenelse{\equal{\keyname}{below left}}{% + \def\tikzumlNaryNodeAnchor{.south west}% + \def\tikzumlNaryNodeLabelPos{below left}% + }{% + \ifthenelse{\equal{\keyname}{below}}{% + \def\tikzumlNaryNodeAnchor{.south}% + \def\tikzumlNaryNodeLabelPos{below}% + }{% + \ifthenelse{\equal{\keyname}{below right}}{% + \def\tikzumlNaryNodeAnchor{.south east}% + \def\tikzumlNaryNodeLabelPos{below right}% + }{% + \ifthenelse{\equal{\keyname}{right}}{% + \def\tikzumlNaryNodeAnchor{.east}% + \def\tikzumlNaryNodeLabelPos{right}% + }{% + \ifthenelse{\equal{\keyname}{above right}}{% + \def\tikzumlNaryNodeAnchor{.north east}% + \def\tikzumlNaryNodeLabelPos{above right}% + }{% + \ifx\keyvalue\pgfkeysnovalue% + \pgfkeys{/tikzuml/narynode/.cd, style/.append style/.expand once={\keyname}}% + \else% + \pgfkeys{/tikzuml/narynode/.cd, style/.append style/.expand twice={\expandafter\keyname\expandafter=\keyvalue}}% + \fi% + %\errmessage{TIKZUML ERROR : in umlNarynode, invalid option \keyname}% + }% + }% + }% + }% + }% + }% + }% + }% + }% + }% + \pgfkeys{/tikzuml/narynode/.cd,#1}% + \pgfkeys{/tikzuml/narynode/.cd, x/.get=\tikzumlNaryNodeX, y/.get=\tikzumlNaryNodeY, width/.get=\tikzumlNaryNodeMinimumWidth,% + name/.get=\tikzumlNaryNodeName,% + draw/.get=\tikzumlNaryNodeDrawColor, fill/.get=\tikzumlNaryNodeFillColor,% + text/.get=\tikzumlNaryNodeTextColor% + }% + % + \def\tikzumlNaryName{#2}% + % + \ifthenelse{\equal{\tikzumlNaryNodeName}{tikzumlEmpty}}{% + \edef\tikzumlNaryNodeName{\tikzumlNaryName}% + }{% + \edef\tikzumlNaryNodeName{\tikzumlNaryNodeName}% + }% + % + \begingroup% + \def\_{_}\edef\x{\endgroup% + \def\noexpand\tikzumlNaryNode@nodeName{\tikzumlNaryNodeName}}\x% + % + \StrSubstitute{\tikzumlNaryNode@nodeName}{:}{@COLON@}[\tikzumlNaryNode@nodeName]% + \StrSubstitute{\tikzumlNaryNode@nodeName}{\_}{@UNDERSCORE@}[\tikzumlNaryNode@nodeName]% + % + \def\tikzumlNarynodePos{\tikzumlNaryNodeX,\tikzumlNaryNodeY}% + % + \node[tikzuml narynode style, draw=\tikzumlNaryNodeDrawColor, fill=\tikzumlNaryNodeFillColor, text=\tikzumlNaryNodeTextColor, font=\tikzumlDefaultFont, minimum width=\tikzumlNaryNodeMinimumWidth, minimum height=\tikzumlNaryNodeMinimumWidth, /tikzuml/narynode/style] (\tikzumlNaryNode@nodeName) at (\tikzumlNarynodePos) {};% + \draw (\tikzumlNaryNode@nodeName\tikzumlNaryNodeAnchor) node[\tikzumlNaryNodeLabelPos] {\tikzumlNaryName};% + % + % add to fit + \ifnum\c@tikzumlPackageLevel>0% + \edef\tikzumlPackageFitOld{\csname tikzumlPackageFit\tikzumlPackage@parent @@\tikzumlPackage@fitname\endcsname}% + \expandafter\xdef\csname tikzumlPackageFit\tikzumlPackage@parent @@\tikzumlPackage@fitname\endcsname{\tikzumlPackageFitOld (\tikzumlNaryNode@nodeName)}% + \stepcounter{tikzumlPackageClassNum}% + \fi% +}% +% +\newdimen\tikzumlNestingSymbolSize% +% +% main command to define a relation between two classes +% args : src class +% dest class +% optional : geometry: geometry of the line +% weight: barycentric weight of the middle part when geometry is a 3-line +% arm1, arm2: lengths of first or last part when geometry id a 3-line +% arg1, arg2, arg: name of the src/dest/dest side class type attribute defined by the relation +% mult1, mult2, mult: multiplicity of the src/dest/dest side class type attribute defined by the relation +% pos1, pos2, pos: position of the src/dest/dest side class type attribute defined by the relation +% align1, align2, align: text justification of the src/dest/dest side class type attribute defined by the relation +% anchor1, anchor2: src/dest anchors on linked classes +% angle1, angle2, loopsize: start angle, end angle and size of the relation (only if recursive) +% stereo: stereotype of the relation +% pos stereo: position of the stereotype on the relation +% style: style of the relation (association, aggregation, composition, inherit, ...) +% name: rootname used for naming nodes of the relation +% recursive mode: type of recursive arrow (transition for state diagrams, or default) +% recursive direction start/end: when transition relation, start/end directions of the relation arrow +\newcommand{\umlrelation}[3][]{% + \pgfkeys{/tikzuml/relation/.cd, geometry/.initial=\tikzumlRelationDefaultGeometry, weight/.initial=\tikzumlRelationDefaultWeight,% + arm1/.initial=auto, arm2/.initial=auto,% + arg1/.initial={}, arg2/.initial={}, arg/.initial={},% + mult1/.initial={}, mult2/.initial={}, mult/.initial={},% + pos1/.initial=\tikzumlRelationDefaultPosO, pos2/.initial=\tikzumlRelationDefaultPosT, pos/.initial=tikzumlEmpty,% + align1/.initial={}, align2/.initial={}, align/.initial={},% + anchor1/.initial=tikzumlEmpty, anchor2/.initial=tikzumlEmpty,% + angle1/.initial=\tikzumlRelationDefaultAngleO, angle2/.initial=\tikzumlRelationDefaultAngleT, loopsize/.initial=\tikzumlRelationDefaultLoopSize,% + stereo/.initial={}, pos stereo/.initial=\tikzumlRelationDefaultPosStereo,% + style/.initial=->, style2/.style={}, name/.initial=relation-\thetikzumlRelationNum,% + recursive mode/.initial=default, recursive direction start/.initial=right,% + recursive direction end/.initial=bottom,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{with port}% + \OR\equal{\keyname}{interface}% + \OR\equal{\keyname}{padding}% + \OR\equal{\keyname}{width}% + \OR\equal{\keyname}{first arm}% + \OR\equal{\keyname}{second arm}% + \OR\equal{\keyname}{middle arm}% + \OR\equal{\keyname}{last arm}% + \OR\equal{\keyname}{distance}}{}{% + \ifx\keyvalue\pgfkeysnovalue% + \pgfkeys{/tikzuml/relation/.cd, style2/.append style/.expand once={\keyname}}% + \else% + \pgfkeys{/tikzuml/relation/.cd, style2/.append style/.expand twice={\expandafter\keyname\expandafter=\keyvalue}}% + \fi% + %\errmessage{TIKZUML ERROR : in umlrelation, invalid option \keyname}% + }% + }% + }% + \pgfkeys{/tikzuml/relation/.cd,#1}% + \pgfkeys{/tikzuml/relation/.cd, geometry/.get=\tikzumlRelationGeometry, weight/.get=\tikzumlRelationWeight,% + arm1/.get=\tikzumlRelationArmO, arm2/.get=\tikzumlRelationArmT,% + arg1/.get=\tikzumlRelationAttrName, arg2/.get=\tikzumlRelationAttrNameTO, arg/.get=\tikzumlRelationAttrNameTT,% + mult1/.get=\tikzumlRelationMultiplicity, mult2/.get=\tikzumlRelationMultiplicityTO, mult/.get=\tikzumlRelationMultiplicityTT,% + pos1/.get=\tikzumlRelationPosition, pos2/.get=\tikzumlRelationPositionTO, pos/.get=\tikzumlRelationPositionTT,% + align1/.get=\tikzumlRelationAlign, align2/.get=\tikzumlRelationAlignTO, align/.get=\tikzumlRelationAlignTT,% + anchor1/.get=\tikzumlRelationSrcAnchor, anchor2/.get=\tikzumlRelationDestAnchor,% + angle1/.get=\tikzumlRelationStartAngle, angle2/.get=\tikzumlRelationEndAngle, loopsize/.get=\tikzumlRelationLoopSize,% + stereo/.get=\tikzumlRelationStereoType, pos stereo/.get=\tikzumlRelationPositionStereotype,% + style/.get=\tikzumlRelationStyle, name/.get=\tikzumlRelationName,% + recursive mode/.get=\tikzumlRelationRecursiveMode,% + recursive direction start/.get=\tikzumlRelationRecursiveDirectionStart,% + recursive direction end/.get=\tikzumlRelationRecursiveDirectionEnd% + }% + % + \def\tikzumlSrcClassName{#2}% + % + % managing \_ in class names for node names + \begingroup% + \def\_{_}\edef\x{\endgroup% + \def\noexpand\tikzumlSrcClassNodeName{\tikzumlSrcClassName}}\x% + % + \StrSubstitute{\tikzumlSrcClassNodeName}{:}{@COLON@}[\tikzumlSrcClassNodeName]% + \StrSubstitute{\tikzumlSrcClassNodeName}{\_}{@UNDERSCORE@}[\tikzumlSrcClassNodeName]% + % + \def\tikzumlDestClassName{#3}% + \begingroup% + \def\_{_}\edef\x{\endgroup% + \def\noexpand\tikzumlDestClassNodeName{\tikzumlDestClassName}}\x% + % + \StrSubstitute{\tikzumlDestClassNodeName}{:}{@COLON@}[\tikzumlDestClassNodeName]% + \StrSubstitute{\tikzumlDestClassNodeName}{\_}{@UNDERSCORE@}[\tikzumlDestClassNodeName]% + % + % managing alias keys + \def\tikzumlRelationAttrNameT{\tikzumlRelationAttrNameTO\tikzumlRelationAttrNameTT}% + \def\tikzumlRelationMultiplicityT{\tikzumlRelationMultiplicityTO\tikzumlRelationMultiplicityTT}% + \def\tikzumlRelationAlignT{\tikzumlRelationAlignTO\tikzumlRelationAlignTT}% + \def\posAttrName{}% + \def\posMultiplicity{}% + \def\posAttrNameT{}% + \def\posMultiplicityT{}% + % + \ifthenelse{\equal{\tikzumlRelationPositionTT}{tikzumlEmpty}}{% + \def\tikzumlRelationPositionT{\tikzumlRelationPositionTO}% + }{% + \def\tikzumlRelationPositionT{\tikzumlRelationPositionTT}% + }% + % + \def\attrAlign{}% + \def\multAlign{}% + \def\attrAlignT{}% + \def\multAlignT{}% + % + \ifthenelse{\equal{\tikzumlRelationAlign}{left}}{% + \def\attrAlign{above right}% + \def\multAlign{below right}% + }{% + \ifthenelse{\equal{\tikzumlRelationAlign}{right}}{% + \def\attrAlign{above left}% + \def\multAlign{below left}% + }{}% + }% + % + \ifthenelse{\equal{\tikzumlRelationAlignT}{left}}{% + \def\attrAlignT{above right}% + \def\multAlignT{below right}% + }{% + \ifthenelse{\equal{\tikzumlRelationAlignT}{right}}{% + \def\attrAlignT{above left}% + \def\multAlignT{below left}% + }{}% + }% + % + % def stereotype + \ifthenelse{\equal{\tikzumlRelationStereoType}{}}{% + \def\stereotype{}% + }{% + \def\stereotype{$\ll$\tikzumlRelationStereoType$\gg$}% + }% + + % def anchors macros + \ifthenelse{\equal{\tikzumlRelationSrcAnchor}{tikzumlEmpty}}{% + \def\tikzumlRelationSrcAnchor{}% + }{% + \let\tikzumlRelationSrcAnchorold\tikzumlRelationSrcAnchor% + \def\tikzumlRelationSrcAnchor{.\tikzumlRelationSrcAnchorold}% + }% + % + \ifthenelse{\equal{\tikzumlRelationDestAnchor}{tikzumlEmpty}}{% + \def\tikzumlRelationDestAnchor{}% + }{% + \let\tikzumlRelationDestAnchorold\tikzumlRelationDestAnchor% + \def\tikzumlRelationDestAnchor{.\tikzumlRelationDestAnchorold}% + }% + % + \setcounter{pos}{100*\real{\tikzumlRelationPosition}}% + \setcounter{posT}{100*\real{\tikzumlRelationPositionT}}% + \setcounter{posStereo}{100*\real{\tikzumlRelationPositionStereotype}}% + % + \pgfmathsetmacro{\tikzumlRelationWeightT}{1.0-\tikzumlRelationWeight}% + % + %\newcounter{tikzumlControlNodesNum}% + %\setcounter{tikzumlControlNodesNum}{0}% + % + \node[inner sep=0] (\tikzumlRelationName-middle) at (barycentric cs:\tikzumlSrcClassNodeName=\tikzumlRelationWeightT,\tikzumlDestClassNodeName=\tikzumlRelationWeight) {};% + % + % straight line + \ifthenelse{\equal{\tikzumlRelationGeometry}{--}}% + {% + \ifthenelse{\equal{\tikzumlSrcClassNodeName}{\tikzumlDestClassNodeName}}{% + \def\arcNum{1}% + \def\arcNumT{1}% + % + \ifthenelse{\equal{\tikzumlRelationRecursiveMode}{default}}{% + \xdef\tikzumlLastArc{node[pos=\tikzumlRelationPosition, \posAttrName, \attrAlign] {\tikzumlRelationAttrName}% + node[pos=\tikzumlRelationPosition, swap, \posMultiplicity, \multAlign] {\tikzumlRelationMultiplicity}% + node[pos=\tikzumlRelationPositionT, \posAttrNameT, \attrAlignT] {\tikzumlRelationAttrNameT}% + node[pos=\tikzumlRelationPositionT, swap, \posMultiplicityT, \multAlignT] {\tikzumlRelationMultiplicityT}% + node[pos=\tikzumlRelationPositionStereotype, anchor=center] {\stereotype} }% + \xdef\tikzumlPath{(\tikzumlSrcClassNodeName) edge[in=\tikzumlRelationEndAngle, out=\tikzumlRelationStartAngle, distance=\tikzumlRelationLoopSize] \tikzumlLastArc% + node[midway, inner sep=0, name=\tikzumlRelationName-1, anchor=center] {} (\tikzumlDestClassNodeName) }% + }{% + \ifthenelse{\equal{\tikzumlRelationRecursiveMode}{transition}}{% + \xdef\tikzumlFirstArc{node[midway, inner sep=0, name=\tikzumlRelationName-1, anchor=center] {}}% + \xdef\tikzumlMidOneArc{node[midway, inner sep=0, name=\tikzumlRelationName-3, anchor=center] {}}% + % + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionStart}{\tikzumlRelationRecursiveDirectionEnd}}{% + \def\numArcs{3}% + \xdef\tikzumlLastArc{node[midway, inner sep=0, name=\tikzumlRelationName-5, anchor=center] {}}% + % + \begin{pgfonlayer}{connections}% + \draw (\tikzumlSrcClassNodeName) edge[in=\tikzumlRelationEndAngle, out=\tikzumlRelationStartAngle, distance=\tikzumlRelationLoopSize, draw=none] % + node[midway, inner sep=0, name=\tikzumlRelationName-tmp, anchor=center] {} (\tikzumlDestClassNodeName);% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionStart}{right}\OR\equal{\tikzumlRelationRecursiveDirectionStart}{left}}{% + \node[inner sep=0, name=\tikzumlRelationName-2] at (\tikzumlSrcClassNodeName.\tikzumlRelationStartAngle -| \tikzumlRelationName-tmp) {};% + \node[inner sep=0, name=\tikzumlRelationName-4] at (\tikzumlDestClassNodeName.\tikzumlRelationEndAngle -| \tikzumlRelationName-tmp) {};% + }{% + \node[inner sep=0, name=\tikzumlRelationName-2] at (\tikzumlSrcClassNodeName.\tikzumlRelationStartAngle |- \tikzumlRelationName-tmp) {};% + \node[inner sep=0, name=\tikzumlRelationName-4] at (\tikzumlDestClassNodeName.\tikzumlRelationEndAngle |- \tikzumlRelationName-tmp) {};% + }% + \end{pgfonlayer}% + }{% + \def\numArcs{4}% + \xdef\tikzumlMidTwoArc{node[midway, inner sep=0, name=\tikzumlRelationName-5, anchor=center] {}}% + \xdef\tikzumlLastArc{node[midway, inner sep=0, name=\tikzumlRelationName-7, anchor=center] {}}% + % + \begin{pgfonlayer}{connections}% + \draw (\tikzumlSrcClassNodeName) edge[in=\tikzumlRelationEndAngle, out=\tikzumlRelationStartAngle, distance=\tikzumlRelationLoopSize, draw=none] % + node[midway, name=\tikzumlRelationName-4, anchor=center] {} (\tikzumlDestClassNodeName);% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionStart}{right}\OR\equal{\tikzumlRelationRecursiveDirectionStart}{left}}{% + \node[inner sep=0, name=\tikzumlRelationName-2] at (\tikzumlSrcClassNodeName.\tikzumlRelationStartAngle -| \tikzumlRelationName-4) {};% + \node[inner sep=0, name=\tikzumlRelationName-6] at (\tikzumlDestClassNodeName.\tikzumlRelationEndAngle |- \tikzumlRelationName-4) {};% + }{% + \node[inner sep=0, name=\tikzumlRelationName-2] at (\tikzumlSrcClassNodeName.\tikzumlRelationStartAngle |- \tikzumlRelationName-4) {};% + \node[inner sep=0, name=\tikzumlRelationName-6] at (\tikzumlDestClassNodeName.\tikzumlRelationEndAngle -| \tikzumlRelationName-4) {};% + }% + \end{pgfonlayer}% + }% + % + \ifnum\numArcs=4% + \ifnum\theposStereo>300% + \pgfmathsetmacro{\tikzumlRelationPositionStereotype}{(\theposStereo-300)/100}% + \xdef\tikzumlLastArc{\tikzumlLastArc node[pos=\tikzumlRelationPositionStereotype, anchor=center] {\stereotype}}% + \else% + \ifnum\theposStereo<100% + \xdef\tikzumlFirstArc{\tikzumlFirstArc node[pos=\tikzumlRelationPositionStereotype, anchor=center] {\stereotype}}% + \else% + \ifnum\theposStereo>200% + \pgfmathsetmacro{\tikzumlRelationPositionStereotype}{(\theposStereo-200)/100}% + \xdef\tikzumlMidTwoArc{\tikzumlMidTwoArc node[pos=\tikzumlRelationPositionStereotype, anchor=center] {\stereotype}}% + \else% + \pgfmathsetmacro{\tikzumlRelationPositionStereotype}{(\theposStereo-100)/100}% + \xdef\tikzumlMidOneArc{\tikzumlMidOneArc node[pos=\tikzumlRelationPositionStereotype, anchor=center] {\stereotype}}% + \fi% + \fi% + \fi% + % + \ifthenelse{\thepos=300\OR\thepos=100}{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionStart}{right}}{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionEnd}{bottom}}{% + \def\posAttrName{above right}% + \def\posMultiplicity{below left}% + }{% + \def\posAttrName{above left}% + \def\posMultiplicity{below right}% + }% + }{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionStart}{left}}{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionEnd}{bottom}}{% + \def\posAttrName{above left}% + \def\posMultiplicity{below right}% + }{% + \def\posAttrName{above right}% + \def\posMultiplicity{below left}% + }% + }{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionStart}{top}}{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionEnd}{left}}{% + \def\posAttrName{above right}% + \def\posMultiplicity{below left}% + }{% + \def\posAttrName{above left}% + \def\posMultiplicity{below right}% + }% + }{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionEnd}{left}}{% + \def\posAttrName{above left}% + \def\posMultiplicity{below right}% + }{% + \def\posAttrName{above right}% + \def\posMultiplicity{below left}% + }% + }% + }% + }% + }{}% + % + \ifthenelse{\thepos=200}{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionStart}{right}}{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionEnd}{bottom}}{% + \def\posAttrName{above left}% + \def\posMultiplicity{below right}% + }{% + \def\posAttrName{above right}% + \def\posMultiplicity{below left}% + }% + }{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionStart}{left}}{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionEnd}{bottom}}{% + \def\posAttrName{above right}% + \def\posMultiplicity{below left}% + }{% + \def\posAttrName{above left}% + \def\posMultiplicity{below right}% + }% + }{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionStart}{top}}{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionEnd}{left}}{% + \def\posAttrName{above left}% + \def\posMultiplicity{below right}% + }{% + \def\posAttrName{above right}% + \def\posMultiplicity{below left}% + }% + }{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionEnd}{left}}{% + \def\posAttrName{above right}% + \def\posMultiplicity{below left}% + }{% + \def\posAttrName{above left}% + \def\posMultiplicity{below right}% + }% + }% + }% + }% + }{}% + % + \ifnum\thepos>300% + \pgfmathsetmacro{\tikzumlRelationPosition}{(\thepos-300)/100}% + \xdef\tikzumlLastArc{\tikzumlLastArc node[pos=\tikzumlRelationPosition, \posAttrName, \attrAlign] {\tikzumlRelationAttrName}% + node[pos=\tikzumlRelationPosition, swap, \posMultiplicity, \multAlign] {\tikzumlRelationMultiplicity}% + }% + \else% + \ifnum\thepos<100% + \xdef\tikzumlFirstArc{\tikzumlFirstArc node[pos=\tikzumlRelationPosition, \posAttrName, \attrAlign] {\tikzumlRelationAttrName}% + node[pos=\tikzumlRelationPosition, swap, \posMultiplicity, \multAlign] {\tikzumlRelationMultiplicity}% + }% + \else% + \ifnum\thepos>200% + \pgfmathsetmacro{\tikzumlRelationPosition}{(\thepos-200)/100}% + \xdef\tikzumlMidTwoArc{\tikzumlMidTwoArc node[pos=\tikzumlRelationPosition, \posAttrName, \attrAlign] {\tikzumlRelationAttrName}% + node[pos=\tikzumlRelationPosition, swap, \posMultiplicity, \multAlign] {\tikzumlRelationMultiplicity}% + }% + \else% + \pgfmathsetmacro{\tikzumlRelationPosition}{(\thepos-100)/100}% + \xdef\tikzumlMidOneArc{\tikzumlMidOneArc node[pos=\tikzumlRelationPosition, \posAttrName, \attrAlign] {\tikzumlRelationAttrName}% + node[pos=\tikzumlRelationPosition, swap, \posMultiplicity, \multAlign] {\tikzumlRelationMultiplicity}% + }% + \fi% + \fi% + \fi% + % + \ifthenelse{\theposT=300\OR\theposT=100}{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionStart}{right}}{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionEnd}{bottom}}{% + \def\posAttrNameT{above right}% + \def\posMultiplicityT{below left}% + }{% + \def\posAttrNameT{above left}% + \def\posMultiplicityT{below right}% + }% + }{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionStart}{left}}{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionEnd}{bottom}}{% + \def\posAttrNameT{above left}% + \def\posMultiplicityT{below right}% + }{% + \def\posAttrNameT{above right}% + \def\posMultiplicityT{below left}% + }% + }{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionStart}{top}}{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionEnd}{left}}{% + \def\posAttrNameT{above right}% + \def\posMultiplicityT{below left}% + }{% + \def\posAttrNameT{above left}% + \def\posMultiplicityT{below right}% + }% + }{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionEnd}{left}}{% + \def\posAttrNameT{above left}% + \def\posMultiplicityT{below right}% + }{% + \def\posAttrNameT{above right}% + \def\posMultiplicityT{below left}% + }% + }% + }% + }% + }{}% + \ifthenelse{\theposT=200}{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionStart}{right}}{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionEnd}{bottom}}{% + \def\posAttrNameT{above left}% + \def\posMultiplicity{below right}% + }{% + \def\posAttrNameT{above right}% + \def\posMultiplicityT{below left}% + }% + }{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionStart}{left}}{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionEnd}{bottom}}{% + \def\posAttrNameT{above right}% + \def\posMultiplicityT{below left}% + }{% + \def\posAttrNameT{above left}% + \def\posMultiplicityT{below right}% + }% + }{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionStart}{top}}{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionEnd}{left}}{% + \def\posAttrNameT{above left}% + \def\posMultiplicityT{below right}% + }{% + \def\posAttrNameT{above right}% + \def\posMultiplicityT{below left}% + }% + }{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionEnd}{left}}{% + \def\posAttrNameT{above right}% + \def\posMultiplicityT{below left}% + }{% + \def\posAttrNameT{above left}% + \def\posMultiplicityT{below right}% + }% + }% + }% + }% + }{}% + % + \ifnum\theposT>300% + \pgfmathsetmacro{\tikzumlRelationPositionT}{(\theposT-300)/100}% + \xdef\tikzumlLastArc{\tikzumlLastArc node[pos=\tikzumlRelationPositionT, \posAttrNameT, \attrAlignT] {\tikzumlRelationAttrNameT}% + node[pos=\tikzumlRelationPositionT, swap, \posMultiplicityT, \multAlignT] {\tikzumlRelationMultiplicityT}% + }% + \else% + \ifnum\theposT<100% + \xdef\tikzumlFirstArc{\tikzumlFirstArc node[pos=\tikzumlRelationPositionT, \posAttrNameT, \attrAlignT] {\tikzumlRelationAttrNameT}% + node[pos=\tikzumlRelationPositionT, swap, \posMultiplicityT, \multAlignT] {\tikzumlRelationMultiplicityT}% + }% + \else% + \ifnum\theposT>200% + \pgfmathsetmacro{\tikzumlRelationPositionT}{(\theposT-200)/100}% + \xdef\tikzumlMidTwoArc{\tikzumlMidTwoArc node[pos=\tikzumlRelationPositionT, \posAttrNameT, \attrAlignT] {\tikzumlRelationAttrNameT}% + node[pos=\tikzumlRelationPositionT, swap, \posMultiplicityT, \multAlignT] {\tikzumlRelationMultiplicityT}% + }% + \else% + \pgfmathsetmacro{\tikzumlRelationPositionT}{(\theposT-100)/100}% + \xdef\tikzumlMidOneArc{\tikzumlMidOneArc node[pos=\tikzumlRelationPositionT, \posAttrNameT, \attrAlignT] {\tikzumlRelationAttrNameT}% + node[pos=\tikzumlRelationPositionT, swap, \posMultiplicityT, \multAlignT] {\tikzumlRelationMultiplicityT}% + }% + \fi% + \fi% + \fi% + \else% + \ifnum\theposStereo>200% + \pgfmathsetmacro{\tikzumlRelationPositionStereotype}{(\theposStereo-200)/100}% + \xdef\tikzumlLastArc{\tikzumlLastArc node[pos=\tikzumlRelationPositionStereotype, anchor=center] {\stereotype} }% + \else% + \ifnum\theposStereo<100% + \xdef\tikzumlFirstArc{\tikzumlFirstArc node[pos=\tikzumlRelationPositionStereotype, anchor=center] {\stereotype} }% + \else% + \pgfmathsetmacro{\tikzumlRelationPositionStereotype}{(\theposStereo-100)/100}% + \xdef\tikzumlMidOneArc{\tikzumlMidOneArc node[pos=\tikzumlRelationPositionStereotype, anchor=center] {\stereotype} }% + \fi% + \fi% + % + \ifthenelse{\thepos=100}{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionStart}{right}}{% + \ifthenelse{\tikzumlRelationEndAngle<\tikzumlRelationStartAngle}{% + \def\posAttrName{above right}% + \def\posMultiplicity{below left}% + }{% + \def\posAttrName{above left}% + \def\posMultiplicity{below right}% + }% + }{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionStart}{left}}{% + \ifthenelse{\tikzumlRelationEndAngle<\tikzumlRelationStartAngle}{% + \def\posAttrName{above left}% + \def\posMultiplicity{below right}% + }{% + \def\posAttrName{above right}% + \def\posMultiplicity{below left}% + }% + }{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionStart}{top}}{% + \ifthenelse{\tikzumlRelationEndAngle<\tikzumlRelationStartAngle}{% + \def\posAttrName{above right}% + \def\posMultiplicity{below left}% + }{% + \def\posAttrName{above left}% + \def\posMultiplicity{below right}% + }% + }{% + \ifthenelse{\tikzumlRelationEndAngle<\tikzumlRelationStartAngle}{% + \def\posAttrName{above left}% + \def\posMultiplicity{below right}% + }{% + \def\posAttrName{above right}% + \def\posMultiplicity{below left}% + }% + }% + }% + }% + }{}% + % + \ifthenelse{\thepos=200}{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionStart}{right}}{% + \ifthenelse{\tikzumlRelationEndAngle<\tikzumlRelationStartAngle}{% + \def\posAttrName{above left}% + \def\posMultiplicity{below right}% + }{% + \def\posAttrName{above right}% + \def\posMultiplicity{below left}% + }% + }{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionStart}{left}}{% + \ifthenelse{\tikzumlRelationEndAngle<\tikzumlRelationStartAngle}{% + \def\posAttrName{above right}% + \def\posMultiplicity{below left}% + }{% + \def\posAttrName{above left}% + \def\posMultiplicity{below right}% + }% + }{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionStart}{top}}{% + \ifthenelse{\tikzumlRelationEndAngle<\tikzumlRelationStartAngle}{% + \def\posAttrName{above left}% + \def\posMultiplicity{below right}% + }{% + \def\posAttrName{above right}% + \def\posMultiplicity{below left}% + }% + }{% + \ifthenelse{\tikzumlRelationEndAngle<\tikzumlRelationStartAngle}{% + \def\posAttrName{above right}% + \def\posMultiplicity{below left}% + }{% + \def\posAttrName{above left}% + \def\posMultiplicity{below right}% + }% + }% + }% + }% + }{}% + % + \ifnum\thepos>200% + \pgfmathsetmacro{\tikzumlRelationPosition}{(\thepos-200)/100}% + \xdef\tikzumlLastArc{\tikzumlLastArc node[pos=\tikzumlRelationPosition, \posAttrName, \attrAlign] {\tikzumlRelationAttrName}% + node[pos=\tikzumlRelationPosition, swap, \posMultiplicity, \multAlign] {\tikzumlRelationMultiplicity}% + }% + \else% + \ifnum\thepos<100% + \xdef\tikzumlFirstArc{\tikzumlFirstArc node[pos=\tikzumlRelationPosition, \posAttrName, \attrAlign] {\tikzumlRelationAttrName}% + node[pos=\tikzumlRelationPosition, swap, \posMultiplicity, \multAlign] {\tikzumlRelationMultiplicity}% + }% + \else% + \pgfmathsetmacro{\tikzumlRelationPosition}{(\thepos-100)/100}% + \xdef\tikzumlMidOneArc{\tikzumlMidOneArc node[pos=\tikzumlRelationPosition, \posAttrName, \attrAlign] {\tikzumlRelationAttrName}% + node[pos=\tikzumlRelationPosition, swap, \posMultiplicity, \multAlign] {\tikzumlRelationMultiplicity}% + }% + \fi% + \fi% + % + \ifthenelse{\theposT=100}{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionStart}{right}}{% + \ifthenelse{\tikzumlRelationEndAngle<\tikzumlRelationStartAngle}{% + \def\posAttrNameT{above right}% + \def\posMultiplicityT{below left}% + }{% + \def\posAttrNameT{above left}% + \def\posMultiplicityT{below right}% + }% + }{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionStart}{left}}{% + \ifthenelse{\tikzumlRelationEndAngle<\tikzumlRelationStartAngle}{% + \def\posAttrNameT{above left}% + \def\posMultiplicityT{below right}% + }{% + \def\posAttrNameT{above right}% + \def\posMultiplicityT{below left}% + }% + }{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionStart}{top}}{% + \ifthenelse{\tikzumlRelationEndAngle<\tikzumlRelationStartAngle}{% + \def\posAttrNameT{above right}% + \def\posMultiplicityT{below left}% + }{% + \def\posAttrNameT{above left}% + \def\posMultiplicityT{below right}% + }% + }{% + \ifthenelse{\tikzumlRelationEndAngle<\tikzumlRelationStartAngle}{% + \def\posAttrNameT{above left}% + \def\posMultiplicityT{below right}% + }{% + \def\posAttrNameT{above right}% + \def\posMultiplicityT{below left}% + }% + }% + }% + }% + }{}% + % + \ifthenelse{\theposT=200}{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionStart}{right}}{% + \ifthenelse{\tikzumlRelationEndAngle<\tikzumlRelationStartAngle}{% + \def\posAttrNameT{above left}% + \def\posMultiplicityT{below right}% + }{% + \def\posAttrNameT{above right}% + \def\posMultiplicityT{below left}% + }% + }{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionStart}{left}}{% + \ifthenelse{\tikzumlRelationEndAngle<\tikzumlRelationStartAngle}{% + \def\posAttrNameT{above right}% + \def\posMultiplicityT{below left}% + }{% + \def\posAttrNameT{above left}% + \def\posMultiplicityT{below right}% + }% + }{% + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionStart}{top}}{% + \ifthenelse{\tikzumlRelationEndAngle<\tikzumlRelationStartAngle}{% + \def\posAttrNameT{above left}% + \def\posMultiplicityT{below right}% + }{% + \def\posAttrNameT{above right}% + \def\posMultiplicityT{below left}% + }% + }{% + \ifthenelse{\tikzumlRelationEndAngle<\tikzumlRelationStartAngle}{% + \def\posAttrNameT{above right}% + \def\posMultiplicityT{below left}% + }{% + \def\posAttrNameT{above left}% + \def\posMultiplicityT{below right}% + }% + }% + }% + }% + }{}% + % + \ifnum\theposT>200% + \pgfmathsetmacro{\tikzumlRelationPositionT}{(\theposT-200)/100}% + \xdef\tikzumlLastArc{\tikzumlLastArc node[pos=\tikzumlRelationPositionT, \posAttrNameT, \attrAlignT] {\tikzumlRelationAttrNameT}% + node[pos=\tikzumlRelationPositionT, swap, \posMultiplicityT, \multAlignT] {\tikzumlRelationMultiplicityT}% + }% + \else% + \ifnum\theposT<100% + \xdef\tikzumlFirstArc{\tikzumlFirstArc node[pos=\tikzumlRelationPositionT, \posAttrNameT, \attrAlignT] {\tikzumlRelationAttrNameT}% + node[pos=\tikzumlRelationPositionT, swap, \posMultiplicityT, \multAlignT] {\tikzumlRelationMultiplicityT}% + }% + \else% + \pgfmathsetmacro{\tikzumlRelationPositionT}{(\theposT-100)/100}% + \xdef\tikzumlMidOneArc{\tikzumlMidOneArc node[pos=\tikzumlRelationPositionT, \posAttrNameT, \attrAlignT] {\tikzumlRelationAttrNameT}% + node[pos=\tikzumlRelationPositionT, swap, \posMultiplicityT, \multAlignT] {\tikzumlRelationMultiplicityT}% + }% + \fi% + \fi% + \fi% + % + \ifthenelse{\equal{\tikzumlRelationRecursiveDirectionStart}{\tikzumlRelationRecursiveDirectionEnd}}{% + \xdef\tikzumlPath{(\tikzumlSrcClassNodeName.\tikzumlRelationStartAngle) -- \tikzumlFirstArc (\tikzumlRelationName-2.center) -- \tikzumlMidOneArc (\tikzumlRelationName-4.center) -- \tikzumlLastArc (\tikzumlDestClassNodeName.\tikzumlRelationEndAngle) }% + \ifnum\thetikzumlStateLevel>0% + \def\tikzumlStateFitTmp{\csname tikzumlStateFit\tikzumlState@parent @@\tikzumlState@fitname\endcsname}% + \expandafter\xdef\csname tikzumlStateFit\tikzumlState@parent @@\tikzumlState@fitname\endcsname{\tikzumlStateFitTmp (\tikzumlRelationName-1) (\tikzumlRelationName-2) (\tikzumlRelationName-3) (\tikzumlRelationName-4) (\tikzumlRelationName-5)}% + \fi% + }{% + \xdef\tikzumlPath{(\tikzumlSrcClassNodeName.\tikzumlRelationStartAngle) -- \tikzumlFirstArc (\tikzumlRelationName-2.center) -- \tikzumlMidOneArc (\tikzumlRelationName-4.center) -- \tikzumlMidTwoArc (\tikzumlRelationName-6.center) -- \tikzumlLastArc (\tikzumlDestClassNodeName.\tikzumlRelationEndAngle) }% + \ifnum\thetikzumlStateLevel>0% + \def\tikzumlStateFitTmp{\csname tikzumlStateFit\tikzumlState@parent @@\tikzumlState@fitname\endcsname}% + \expandafter\xdef\csname tikzumlStateFit\tikzumlState@parent @@\tikzumlState@fitname\endcsname{\tikzumlStateFitTmp (\tikzumlRelationName-1) (\tikzumlRelationName-2) (\tikzumlRelationName-3) (\tikzumlRelationName-4) (\tikzumlRelationName-5) (\tikzumlRelationName-6) (\tikzumlRelationName-7)}% + \fi% + }% + }{}% + }% + }{% + \def\arcNum{1}% + \def\arcNumT{1}% + % + \node[inner sep=0] (\tikzumlRelationName-1) at (\tikzumlRelationName-middle) {};% + \xdef\tikzumlLastArc{node[pos=\tikzumlRelationPosition, \posAttrName, \attrAlign] {\tikzumlRelationAttrName}% + node[pos=\tikzumlRelationPosition, swap, \posMultiplicity, \multAlign] {\tikzumlRelationMultiplicity}% + node[pos=\tikzumlRelationPositionT, \posAttrNameT, \attrAlignT] {\tikzumlRelationAttrNameT}% + node[pos=\tikzumlRelationPositionT, swap, \posMultiplicityT, \multAlignT] {\tikzumlRelationMultiplicityT}% + node[pos=\tikzumlRelationPositionStereotype, anchor=center] {\stereotype} }% + \xdef\tikzumlPath{(\tikzumlSrcClassNodeName\tikzumlRelationSrcAnchor) -- \tikzumlLastArc (\tikzumlDestClassNodeName\tikzumlRelationDestAnchor) }% + \ifnum\thetikzumlStateLevel>0% + \def\tikzumlStateFitTmp{\csname tikzumlStateFit\tikzumlState@parent @@\tikzumlState@fitname\endcsname}% + \expandafter\xdef\csname tikzumlStateFit\tikzumlState@parent @@\tikzumlState@fitname\endcsname{\tikzumlStateFitTmp (\tikzumlRelationName-1) }% + \fi% + }% + }{% + % first vertical then horizontal line + \ifthenelse{\equal{\tikzumlRelationGeometry}{|-}}% + {% + %\setcounter{tikzumlControlNodesNum}{1}% + % + \def\tikzumlFirstArc{node[midway, inner sep=0, name=\tikzumlRelationName-1, anchor=center] {} }% + \def\tikzumlLastArc{node[midway, inner sep=0, name=\tikzumlRelationName-3, anchor=center]{} }% + % + \begin{pgfonlayer}{connections}% + \node[inner sep=0] (\tikzumlRelationName-2) at (\tikzumlSrcClassNodeName\tikzumlRelationSrcAnchor |- \tikzumlDestClassNodeName\tikzumlRelationDestAnchor) {};% + \end{pgfonlayer}% + % + \ifnum\theposStereo>100% + \pgfmathsetmacro{\tikzumlRelationPositionStereotype}{(\theposStereo-100)/100}% + \xdef\tikzumlLastArc{\tikzumlLastArc node[pos=\tikzumlRelationPositionStereotype, anchor=center] {\stereotype} }% + \else% + \xdef\tikzumlFirstArc{\tikzumlFirstArc node[pos=\tikzumlRelationPositionStereotype, anchor=center] {\stereotype} }% + \fi% + % + \ifnum\thepos>100% + \pgfmathsetmacro{\tikzumlRelationPosition}{(\thepos-100)/100}% + \def\arcNum{2}% + \else% + \def\arcNum{1}% + \ifnum\thepos=100% + \def\posAttrName{above left}% + \def\posMultiplicity{below right}% + \fi% + \fi% + % + \ifnum\arcNum=1% + \xdef\tikzumlFirstArc{\tikzumlFirstArc node[pos=\tikzumlRelationPosition, \posAttrName, \attrAlign] {\tikzumlRelationAttrName}% + node[pos=\tikzumlRelationPosition, swap, \posMultiplicity, \multAlign] {\tikzumlRelationMultiplicity} }% + \fi% + \ifnum\arcNum=2% + \xdef\tikzumlLastArc{\tikzumlLastArc node[pos=\tikzumlRelationPosition, \posAttrName, \attrAlign] {\tikzumlRelationAttrName}% + node[pos=\tikzumlRelationPosition, swap, \posMultiplicity, \multAlign] {\tikzumlRelationMultiplicity} }% + \fi% + % + \ifnum\theposT>100% + \pgfmathsetmacro{\tikzumlRelationPositionT}{(\theposT-100)/100}% + \def\arcNumT{2}% + \else% + \def\arcNumT{1}% + \ifnum\theposT=100% + \def\posAttrNameT{above left}% + \def\posMultiplicityT{below right}% + \fi% + \fi% + % + \ifnum\arcNumT=1% + \xdef\tikzumlFirstArc{\tikzumlFirstArc node[pos=\tikzumlRelationPositionT, \posAttrNameT, \attrAlignT] {\tikzumlRelationAttrNameT}% + node[pos=\tikzumlRelationPositionT, swap, \posMultiplicityT, \multAlignT] {\tikzumlRelationMultiplicityT} }% + \fi% + \ifnum\arcNumT=2% + \xdef\tikzumlLastArc{\tikzumlLastArc node[pos=\tikzumlRelationPositionT, \posAttrNameT, \attrAlignT] {\tikzumlRelationAttrNameT}% + node[pos=\tikzumlRelationPositionT, swap, \posMultiplicityT, \multAlignT] {\tikzumlRelationMultiplicityT} }% + \fi% + % + \xdef\tikzumlPath{(\tikzumlSrcClassNodeName\tikzumlRelationSrcAnchor) -- \tikzumlFirstArc (\tikzumlRelationName-2.base) -- \tikzumlLastArc (\tikzumlDestClassNodeName\tikzumlRelationDestAnchor) }% + \ifnum\thetikzumlStateLevel>0% + \def\tikzumlStateFitTmp{\csname tikzumlStateFit\tikzumlState@parent @@\tikzumlState@fitname\endcsname}% + \expandafter\xdef\csname tikzumlStateFit\tikzumlState@parent @@\tikzumlState@fitname\endcsname{\tikzumlStateFitTmp (\tikzumlRelationName-1) (\tikzumlRelationName-2) (\tikzumlRelationName-3) }% + \fi% + }{% + % first horizontal then vertical line + \ifthenelse{\equal{\tikzumlRelationGeometry}{-|}}% + {% + %\setcounter{tikzumlControlNodesNum}{1}% + % + \def\tikzumlFirstArc{node[midway, inner sep=0, name=\tikzumlRelationName-1, anchor=center]{} }% + \def\tikzumlLastArc{node[midway, inner sep=0, name=\tikzumlRelationName-3, anchor=center] {} }% + % + \begin{pgfonlayer}{connections}% + \node[inner sep=0] (\tikzumlRelationName-2) at (\tikzumlSrcClassNodeName\tikzumlRelationSrcAnchor -| \tikzumlDestClassNodeName\tikzumlRelationDestAnchor) {};% + \end{pgfonlayer}% + % + \ifnum\theposStereo>100% + \pgfmathsetmacro{\tikzumlRelationPositionStereotype}{(\theposStereo-100)/100}% + \xdef\tikzumlLastArc{\tikzumlLastArc node[pos=\tikzumlRelationPositionStereotype, anchor=center] {\stereotype} }% + \else% + \xdef\tikzumlFirstArc{\tikzumlFirstArc node[pos=\tikzumlRelationPositionStereotype, anchor=center] {\stereotype} }% + \fi% + % + \ifnum\thepos>100% + \pgfmathsetmacro{\tikzumlRelationPosition}{(\thepos-100)/100}% + \def\arcNum{2}% + \else% + \def\arcNum{1}% + \ifnum\thepos=100% + \def\posAttrName{above left}% + \def\posMultiplicity{below right}% + \fi% + \fi% + % + \ifnum\arcNum=1% + \xdef\tikzumlFirstArc{\tikzumlFirstArc node[pos=\tikzumlRelationPosition, \posAttrName, \attrAlign] {\tikzumlRelationAttrName}% + node[pos=\tikzumlRelationPosition, swap, \posMultiplicity, \multAlign] {\tikzumlRelationMultiplicity} }% + \fi% + \ifnum\arcNum=2% + \xdef\tikzumlLastArc{\tikzumlLastArc node[pos=\tikzumlRelationPosition, \posAttrName, \attrAlign] {\tikzumlRelationAttrName}% + node[pos=\tikzumlRelationPosition, swap, \posMultiplicity, \multAlign] {\tikzumlRelationMultiplicity} }% + \fi% + % + \ifnum\theposT>100% + \pgfmathsetmacro{\tikzumlRelationPositionT}{(\theposT-100)/100}% + \def\arcNumT{2}% + \else% + \def\arcNumT{1}% + \ifnum\theposT=100% + \def\posAttrNameT{above left}% + \def\posMultiplicityT{below right}% + \fi% + \fi% + % + \ifnum\arcNumT=1% + \xdef\tikzumlFirstArc{\tikzumlFirstArc node[pos=\tikzumlRelationPositionT, \posAttrNameT, \attrAlignT] {\tikzumlRelationAttrNameT}% + node[pos=\tikzumlRelationPositionT, swap, \posMultiplicityT, \multAlignT] {\tikzumlRelationMultiplicityT} }% + \fi% + \ifnum\arcNumT=2% + \xdef\tikzumlLastArc{\tikzumlLastArc node[pos=\tikzumlRelationPositionT, \posAttrNameT, \attrAlignT] {\tikzumlRelationAttrNameT}% + node[pos=\tikzumlRelationPositionT, swap, \posMultiplicityT, \multAlignT] {\tikzumlRelationMultiplicityT} }% + \fi% + % + \xdef\tikzumlPath{(\tikzumlSrcClassNodeName\tikzumlRelationSrcAnchor) -- \tikzumlFirstArc (\tikzumlRelationName-2.base) -- \tikzumlLastArc (\tikzumlDestClassNodeName\tikzumlRelationDestAnchor) }% + \ifnum\thetikzumlStateLevel>0% + \def\tikzumlStateFitTmp{\csname tikzumlStateFit\tikzumlState@parent @@\tikzumlState@fitname\endcsname}% + \expandafter\xdef\csname tikzumlStateFit\tikzumlState@parent @@\tikzumlState@fitname\endcsname{\tikzumlStateFitTmp (\tikzumlRelationName-1) (\tikzumlRelationName-2) (\tikzumlRelationName-3) }% + \fi% + }{% + % first vertical, then horizontal, finally vertical line + \ifthenelse{\equal{\tikzumlRelationGeometry}{|-|}}% + {% + %\setcounter{tikzumlControlNodesNum}{2}% + % + \def\tikzumlFirstArc{node[midway, inner sep=0, name=\tikzumlRelationName-1, anchor=center] {} }% + \def\tikzumlLastArc{node[midway, inner sep=0, name=\tikzumlRelationName-5, anchor=center] {} }% + \def\tikzumlMidOneArc{ }% + % + \begin{pgfonlayer}{connections}% + % + \ifthenelse{\equal{\tikzumlRelationArmO}{auto}}{% + \ifthenelse{\equal{\tikzumlRelationArmT}{auto}}{% + \node[inner sep=0] (\tikzumlRelationName-3) at (\tikzumlRelationName-middle) {};% + \node[inner sep=0] (\tikzumlRelationName-2) at (\tikzumlSrcClassNodeName\tikzumlRelationSrcAnchor |- \tikzumlRelationName-3) {};% + \node[inner sep=0] (\tikzumlRelationName-4) at (\tikzumlRelationName-3 -| \tikzumlDestClassNodeName\tikzumlRelationDestAnchor) {};% + }{% + \draw (\tikzumlDestClassNodeName\tikzumlRelationDestAnchor)+(0,\tikzumlRelationArmT) node[inner sep=0, name=\tikzumlRelationName-4] {};% + \node[inner sep=0] (\tikzumlRelationName-2) at (\tikzumlRelationName-4 -| \tikzumlSrcClassNodeName\tikzumlRelationSrcAnchor) {};% + \node[inner sep=0] (\tikzumlRelationName-3) at (barycentric cs:\tikzumlRelationName-2=0.5,\tikzumlRelationName-4=0.5) {};% + }% + }{% + \draw (\tikzumlSrcClassNodeName\tikzumlRelationSrcAnchor)+(0,\tikzumlRelationArmO) node[inner sep=0, name=\tikzumlRelationName-2] {};% + \node[inner sep=0] (\tikzumlRelationName-4) at (\tikzumlRelationName-2 -| \tikzumlDestClassNodeName\tikzumlRelationDestAnchor) {};% + \node[inner sep=0] (\tikzumlRelationName-3) at (barycentric cs:\tikzumlRelationName-2=0.5,\tikzumlRelationName-4=0.5) {};% + }% + \end{pgfonlayer}% + % + \ifnum\theposStereo>200% + \pgfmathsetmacro{\tikzumlRelationPositionStereotype}{(\theposStereo-200)/100}% + \xdef\tikzumlLastArc{\tikzumlLastArc node[pos=\tikzumlRelationPositionStereotype, anchor=center] {\stereotype} }% + \else% + \ifnum\theposStereo<100% + \xdef\tikzumlFirstArc{\tikzumlFirstArc node[pos=\tikzumlRelationPositionStereotype, anchor=center] {\stereotype} }% + \else% + \pgfmathsetmacro{\tikzumlRelationPositionStereotype}{(\theposStereo-100)/100}% + \xdef\tikzumlMidOneArc{\tikzumlMidOneArc node[pos=\tikzumlRelationPositionStereotype, anchor=center] {\stereotype} }% + \fi% + \fi% + % + \ifthenelse{\thepos=200\OR\thepos=100}{% + \def\posAttrName{above left}% + \def\posMultiplicity{below right}% + }{}% + % + \ifthenelse{\thepos>200}{% + \pgfmathsetmacro{\tikzumlRelationPosition}{(\thepos-200)/100}% + \def\arcNum{3}% + }{% + \ifthenelse{\thepos<100}{% + \def\arcNum{1}% + }{% + \pgfmathsetmacro{\tikzumlRelationPosition}{(\thepos-100)/100}% + \def\arcNum{2}% + }% + }% + % + \ifnum\arcNum=1% + \xdef\tikzumlFirstArc{\tikzumlFirstArc node[pos=\tikzumlRelationPosition, \posAttrName, \attrAlign] {\tikzumlRelationAttrName}% + node[pos=\tikzumlRelationPosition, swap, \posMultiplicity, \multAlign] {\tikzumlRelationMultiplicity} }% + \fi% + \ifnum\arcNum=2% + \xdef\tikzumlMidOneArc{\tikzumlMidOneArc node[pos=\tikzumlRelationPosition, \posAttrName, \attrAlign] {\tikzumlRelationAttrName}% + node[pos=\tikzumlRelationPosition, swap, \posMultiplicity, \multAlign] {\tikzumlRelationMultiplicity} }% + \fi% + \ifnum\arcNum=3% + \xdef\tikzumlLastArc{\tikzumlLastArc node[pos=\tikzumlRelationPosition, \posAttrName, \attrAlign] {\tikzumlRelationAttrName}% + node[pos=\tikzumlRelationPosition, swap, \posMultiplicity, \multAlign] {\tikzumlRelationMultiplicity} }% + \fi% + % + \ifthenelse{\theposT=200\OR\theposT=100}{% + \def\posAttrNameT{above left}% + \def\posMultiplicityT{below right}% + }{}% + % + \ifthenelse{\theposT>200}{% + \pgfmathsetmacro{\tikzumlRelationPositionT}{(\theposT-200)/100}% + \def\arcNumT{3}% + }{% + \ifthenelse{\theposT<100}{% + \def\arcNumT{1}% + }{% + \pgfmathsetmacro{\tikzumlRelationPositionT}{(\theposT-100)/100}% + \def\arcNumT{2}% + }% + }% + % + \ifnum\arcNumT=1% + \xdef\tikzumlFirstArc{\tikzumlFirstArc node[pos=\tikzumlRelationPositionT, \posAttrNameT, \attrAlignT] {\tikzumlRelationAttrNameT}% + node[pos=\tikzumlRelationPositionT, swap, \posMultiplicityT, \multAlignT] {\tikzumlRelationMultiplicityT} }% + \fi% + \ifnum\arcNumT=2% + \xdef\tikzumlMidOneArc{\tikzumlMidOneArc node[pos=\tikzumlRelationPositionT, \posAttrNameT, \attrAlignT] {\tikzumlRelationAttrNameT}% + node[pos=\tikzumlRelationPositionT, swap, \posMultiplicityT, \multAlignT] {\tikzumlRelationMultiplicityT} }% + \fi% + \ifnum\arcNumT=3% + \xdef\tikzumlLastArc{\tikzumlLastArc node[pos=\tikzumlRelationPositionT, \posAttrNameT, \attrAlignT] {\tikzumlRelationAttrNameT}% + node[pos=\tikzumlRelationPositionT, swap, \posMultiplicityT, \multAlignT] {\tikzumlRelationMultiplicityT} }% + \fi% + % + \xdef\tikzumlPath{(\tikzumlSrcClassNodeName\tikzumlRelationSrcAnchor) -- \tikzumlFirstArc (\tikzumlRelationName-2.base) -- \tikzumlMidOneArc (\tikzumlRelationName-4.base) -- \tikzumlLastArc (\tikzumlDestClassNodeName\tikzumlRelationDestAnchor) }% + \ifnum\thetikzumlStateLevel>0% + \def\tikzumlStateFitTmp{\csname tikzumlStateFit\tikzumlState@parent @@\tikzumlState@fitname\endcsname}% + \expandafter\xdef\csname tikzumlStateFit\tikzumlState@parent @@\tikzumlState@fitname\endcsname{\tikzumlStateFitTmp (\tikzumlRelationName-1) (\tikzumlRelationName-2) (\tikzumlRelationName-3) (\tikzumlRelationName-4) (\tikzumlRelationName-5) }% + \fi% + }{% + % first horizontal, then vertical, finally horizontal line + \ifthenelse{\equal{\tikzumlRelationGeometry}{-|-}}% + {% + %\setcounter{tikzumlControlNodesNum}{2}% + % + \def\tikzumlFirstArc{node[midway, inner sep=0, name=\tikzumlRelationName-1, anchor=center] {} }% + \def\tikzumlLastArc{node[midway, inner sep=0, name=\tikzumlRelationName-5, anchor=center] {} }% + \def\tikzumlMidOneArc{}% + % + \begin{pgfonlayer}{connections}% + % + \ifthenelse{\equal{\tikzumlRelationArmO}{auto}}{% + \ifthenelse{\equal{\tikzumlRelationArmT}{auto}}{% + \node[inner sep=0] (\tikzumlRelationName-3) at (\tikzumlRelationName-middle) {};% + \node[inner sep=0] (\tikzumlRelationName-2) at (\tikzumlSrcClassNodeName\tikzumlRelationSrcAnchor -| \tikzumlRelationName-3) {};% + \node[inner sep=0] (\tikzumlRelationName-4) at (\tikzumlRelationName-3 |- \tikzumlDestClassNodeName\tikzumlRelationDestAnchor) {};% + }{% + \draw (\tikzumlDestClassNodeName\tikzumlRelationDestAnchor)+(\tikzumlRelationArmT,0) node[inner sep=0, name=\tikzumlRelationName-4] {};% + \node[inner sep=0] (\tikzumlRelationName-2) at (\tikzumlRelationName-4 |- \tikzumlSrcClassNodeName\tikzumlRelationSrcAnchor) {};% + \node[inner sep=0] (\tikzumlRelationName-3) at (barycentric cs:\tikzumlRelationName-2=0.5,\tikzumlRelationName-4=0.5) {};% + }% + }{% + \draw (\tikzumlSrcClassNodeName\tikzumlRelationSrcAnchor)+(\tikzumlRelationArmO,0) node[inner sep=0, name=\tikzumlRelationName-2] {};% + \node[inner sep=0] (\tikzumlRelationName-4) at (\tikzumlRelationName-2 |- \tikzumlDestClassNodeName\tikzumlRelationDestAnchor) {};% + \node[inner sep=0] (\tikzumlRelationName-3) at (barycentric cs:\tikzumlRelationName-2=0.5,\tikzumlRelationName-4=0.5) {};% + }% + \end{pgfonlayer}% + % + \ifnum\theposStereo>200% + \pgfmathsetmacro{\tikzumlRelationPositionStereotype}{(\theposStereo-200)/100}% + \xdef\tikzumlLastArc{\tikzumlLastArc node[pos=\tikzumlRelationPositionStereotype, anchor=center] {\stereotype} }% + \else% + \ifnum\theposStereo<100% + \xdef\tikzumlFirstArc{\tikzumlFirstArc node[pos=\tikzumlRelationPositionStereotype, anchor=center] {\stereotype} }% + \else% + \pgfmathsetmacro{\tikzumlRelationPositionStereotype}{(\theposStereo-100)/100}% + \xdef\tikzumlMidOneArc{\tikzumlMidOneArc node[pos=\tikzumlRelationPositionStereotype, anchor=center] {\stereotype} }% + \fi% + \fi% + % + \ifthenelse{\thepos=200\OR\thepos=100}{% + \def\posAttrName{above left}% + \def\posMultiplicity{below right}% + }{}% + % + \ifthenelse{\thepos>200}{% + \pgfmathsetmacro{\tikzumlRelationPosition}{(\thepos-200)/100}% + \def\arcNum{3}% + }{% + \ifthenelse{\thepos<100}{% + \def\arcNum{1}% + }{% + \pgfmathsetmacro{\tikzumlRelationPosition}{(\thepos-100)/100}% + \def\arcNum{2}% + }% + }% + % + \ifnum\arcNum=1% + \xdef\tikzumlFirstArc{\tikzumlFirstArc node[pos=\tikzumlRelationPosition, \posAttrName, \attrAlign] {\tikzumlRelationAttrName}% + node[pos=\tikzumlRelationPosition, swap, \posMultiplicity, \multAlign] {\tikzumlRelationMultiplicity} }% + \fi% + \ifnum\arcNum=2% + \xdef\tikzumlMidOneArc{\tikzumlMidOneArc node[pos=\tikzumlRelationPosition, \posAttrName, \attrAlign] {\tikzumlRelationAttrName}% + node[pos=\tikzumlRelationPosition, swap, \posMultiplicity, \multAlign] {\tikzumlRelationMultiplicity} }% + \fi% + \ifnum\arcNum=3% + \xdef\tikzumlLastArc{\tikzumlLastArc node[pos=\tikzumlRelationPosition, \posAttrName, \attrAlign] {\tikzumlRelationAttrName}% + node[pos=\tikzumlRelationPosition, swap, \posMultiplicity, \multAlign] {\tikzumlRelationMultiplicity} }% + \fi% + % + \ifthenelse{\theposT=200\OR\theposT=100}{% + \def\posAttrNameT{above left}% + \def\posMultiplicityT{below right}% + }{}% + % + \ifthenelse{\theposT>200}{% + \pgfmathsetmacro{\tikzumlRelationPositionT}{(\theposT-200)/100}% + \def\arcNumT{3}% + }{% + \ifthenelse{\theposT<100}{% + \def\arcNumT{1}% + }{% + \pgfmathsetmacro{\tikzumlRelationPositionT}{(\theposT-100)/100}% + \def\arcNumT{2}% + }% + }% + % + \ifnum\arcNumT=1% + \xdef\tikzumlFirstArc{\tikzumlFirstArc node[pos=\tikzumlRelationPositionT, \posAttrNameT, \attrAlignT] {\tikzumlRelationAttrNameT}% + node[pos=\tikzumlRelationPositionT, swap, \posMultiplicityT, \multAlignT] {\tikzumlRelationMultiplicityT} }% + \fi% + \ifnum\arcNumT=2% + \xdef\tikzumlMidOneArc{\tikzumlMidOneArc node[pos=\tikzumlRelationPositionT, \posAttrNameT, \attrAlignT] {\tikzumlRelationAttrNameT}% + node[pos=\tikzumlRelationPositionT, swap, \posMultiplicityT, \multAlignT] {\tikzumlRelationMultiplicityT} }% + \fi% + \ifnum\arcNumT=3% + \xdef\tikzumlLastArc{\tikzumlLastArc node[pos=\tikzumlRelationPositionT, \posAttrNameT, \attrAlignT] {\tikzumlRelationAttrNameT}% + node[pos=\tikzumlRelationPositionT, swap, \posMultiplicityT, \multAlignT] {\tikzumlRelationMultiplicityT} }% + \fi% + % + \xdef\tikzumlPath{(\tikzumlSrcClassNodeName\tikzumlRelationSrcAnchor) -- \tikzumlFirstArc (\tikzumlRelationName-2.base) -- \tikzumlMidOneArc (\tikzumlRelationName-4.base) -- \tikzumlLastArc (\tikzumlDestClassNodeName\tikzumlRelationDestAnchor) }% + \ifnum\thetikzumlStateLevel>0% + \def\tikzumlStateFitTmp{\csname tikzumlStateFit\tikzumlState@parent @@\tikzumlState@fitname\endcsname}% + \expandafter\xdef\csname tikzumlStateFit\tikzumlState@parent @@\tikzumlState@fitname\endcsname{\tikzumlStateFitTmp (\tikzumlRelationName-1) (\tikzumlRelationName-2) (\tikzumlRelationName-3) (\tikzumlRelationName-4) (\tikzumlRelationName-5) }% + \fi% + }{% + \errmessage{TIKZUML ERROR : Unknown geometry value !!! It should be in the following list : --, |-, -|, |-|, -|-}% + }% + }% + }% + }% + }% + % + \begin{pgfonlayer}{connections}% + \ifthenelse{\equal{\tikzumlRelationStyle}{tikzuml nesting style}}{% + \pgfarrowsdeclare{nested}{nested}{...} + { + \tikzumlNestingSymbolSize=0.2pt% + \advance\tikzumlNestingSymbolSize by .5\pgflinewidth% + \pgfsetdash{}{0pt} % do not dash + \pgfsetroundjoin % fix join + \pgfsetroundcap % fix cap + \pgfpathmoveto{\pgfpoint{-16*\tikzumlNestingSymbolSize}{0pt}}% + \pgfpatharc{180}{90}{8*\tikzumlNestingSymbolSize}% + \pgfpatharc{90}{0}{8*\tikzumlNestingSymbolSize}% + \pgfpatharc{0}{-90}{8*\tikzumlNestingSymbolSize}% + \pgfpatharc{-90}{-180}{8*\tikzumlNestingSymbolSize}% + \pgfpathmoveto{\pgfpoint{-8*\tikzumlNestingSymbolSize}{8*\tikzumlNestingSymbolSize}}% + \pgfpathlineto{\pgfpoint{-8*\tikzumlNestingSymbolSize}{-8*\tikzumlNestingSymbolSize}}% + \pgfusepathqstroke% + }% + \draw[auto, nested-, font=\tikzumlDefaultFont, \tikzumlRelationStyle, /tikzuml/relation/style2] \tikzumlPath ;% + }{ + \draw[auto, font=\tikzumlDefaultFont, \tikzumlRelationStyle, /tikzuml/relation/style2] \tikzumlPath ;% + } + \end{pgfonlayer}% + % + \stepcounter{tikzumlRelationNum}% +}% +% +% shortcuts of \umlrelation +\newcommand{\umlHVrelation}[3][]{% + \pgfkeys{/tikzuml/HVrelation/.cd,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{geometry}}{% + \errmessage{TIKZUML ERROR : in umlHVrelation, forbidden option geometry}% + }{}% + }}% + \pgfkeys{/tikzuml/HVrelation/.cd, #1}% + \umlrelation[geometry=-|, #1]{#2}{#3}% +}% +% +\newcommand{\umlVHrelation}[3][]{% + \pgfkeys{/tikzuml/VHrelation/.cd,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{geometry}}{% + \errmessage{TIKZUML ERROR : in umlVHrelation, forbidden option geometry}% + }{}% + }}% + \pgfkeys{/tikzuml/VHrelation/.cd, #1}% + \umlrelation[geometry=|-, #1]{#2}{#3}% +}% +% +\newcommand{\umlHVHrelation}[3][]{% + \pgfkeys{/tikzuml/HVHrelation/.cd,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{geometry}}{% + \errmessage{TIKZUML ERROR : in umlHVHrelation, forbidden option geometry}% + }{}% + }}% + \pgfkeys{/tikzuml/HVHrelation/.cd, #1}% + \umlrelation[geometry=-|-, #1]{#2}{#3}% +}% +% +\newcommand{\umlVHVrelation}[3][]{% + \pgfkeys{/tikzuml/VHVrelation/.cd,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{geometry}}{% + \errmessage{TIKZUML ERROR : in umlVHVrelation, forbidden option geometry}% + }{}% + }}% + \pgfkeys{/tikzuml/VHVrelation/.cd, #1}% + \umlrelation[geometry=|-|, #1]{#2}{#3}% +}% +% +% +% shortcuts for relations +\newcommand{\umlinherit}[3][]{\umlrelation[style={tikzuml inherit style}, #1]{#2}{#3}}% +\newcommand{\umlimpl}[3][]{\umlrelation[style={tikzuml implements style}, #1]{#2}{#3}}% +\newcommand{\umlreal}[3][]{\umlrelation[style={tikzuml implements style}, #1]{#2}{#3}}% +\newcommand{\umlassoc}[3][]{\umlrelation[style={tikzuml association style}, #1]{#2}{#3}}% +\newcommand{\umlbiassoc}[3][]{\umlrelation[style={tikzuml bidirectional association style}, #1]{#2}{#3}}% +\newcommand{\umluniassoc}[3][]{\umlrelation[style={tikzuml unidirectional association style}, #1]{#2}{#3}}% +\newcommand{\umlaggreg}[3][]{\umlrelation[style={tikzuml aggregation style}, #1]{#2}{#3}}% +\newcommand{\umluniaggreg}[3][]{\umlrelation[style={tikzuml unidirectional aggregation style}, #1]{#2}{#3}}% +\newcommand{\umlcompo}[3][]{\umlrelation[style={tikzuml composition style}, #1]{#2}{#3}}% +\newcommand{\umlunicompo}[3][]{\umlrelation[style={tikzuml unidirectional composition style}, #1]{#2}{#3}}% +\newcommand{\umlimport}[3][]{\umlrelation[style={tikzuml import style}, #1]{#2}{#3}}% +\newcommand{\umlnest}[3][]{\umlrelation[style={tikzuml nesting style}, #1]{#2}{#3}}% +\newcommand{\umldep}[3][]{\umlrelation[style={tikzuml dependency style}, #1]{#2}{#3}}% +\newcommand{\umlfriend}[3][]{% + \pgfkeys{/tikzuml/friendrelation/.cd,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{stereo}}{% + \errmessage{TIKZUML ERROR : in umlfriend, forbidden option stereo}% + }{}% + }}% + \pgfkeys{/tikzuml/friendrelation/.cd, #1}% + \umlrelation[stereo=friend, style={tikzuml dependency style}, #1]{#2}{#3}% +}% +% +\newcommand{\umlHVinherit}[3][]{\umlHVrelation[style={tikzuml inherit style}, #1]{#2}{#3}}% +\newcommand{\umlHVimpl}[3][]{\umlHVrelation[style={tikzuml implements style}, #1]{#2}{#3}}% +\newcommand{\umlHVreal}[3][]{\umlHVrelation[style={tikzuml implements style}, #1]{#2}{#3}}% +\newcommand{\umlHVassoc}[3][]{\umlHVrelation[style={tikzuml association style}, #1]{#2}{#3}}% +\newcommand{\umlHVuniassoc}[3][]{\umlHVrelation[style={tikzuml unidirectional association style}, #1]{#2}{#3}}% +\newcommand{\umlHVaggreg}[3][]{\umlHVrelation[style={tikzuml aggregation style}, #1]{#2}{#3}}% +\newcommand{\umlHVuniaggreg}[3][]{\umlHVrelation[style={tikzuml unidirectional aggregation style}, #1]{#2}{#3}}% +\newcommand{\umlHVcompo}[3][]{\umlHVrelation[style={tikzuml composition style}, #1]{#2}{#3}}% +\newcommand{\umlHVunicompo}[3][]{\umlHVrelation[style={tikzuml unidirectional composition style}, #1]{#2}{#3}}% +\newcommand{\umlHVimport}[3][]{\umlHVrelation[style={tikzuml import style}, #1]{#2}{#3}}% +\newcommand{\umlHVnest}[3][]{\umlHVrelation[style={tikzuml nesting style}, #1]{#2}{#3}}% +\newcommand{\umlHVdep}[3][]{\umlHVrelation[style={tikzuml dependency style}, #1]{#2}{#3}}% +\newcommand{\umlHVfriend}[3][]{% + \pgfkeys{/tikzuml/friendrelation/.cd,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{stereo}}{% + \errmessage{TIKZUML ERROR : in umlHVfriend, forbidden option stereo}% + }{% + \ifthenelse{\equal{\keyname}{geometry}}{% + \errmessage{TIKZUML ERROR : in umlHVfriend, forbidden option geometry}% + }{}% + }% + }}% + \pgfkeys{/tikzuml/friendrelation/.cd, #1}% + \umlrelation[geometry=-|, stereo=friend, style={tikzuml dependency style}, #1]{#2}{#3}% +}% +% +\newcommand{\umlVHinherit}[3][]{\umlVHrelation[style={tikzuml inherit style}, #1]{#2}{#3}}% +\newcommand{\umlVHimpl}[3][]{\umlVHrelation[style={tikzuml implements style}, #1]{#2}{#3}}% +\newcommand{\umlVHreal}[3][]{\umlVHrelation[style={tikzuml implements style}, #1]{#2}{#3}}% +\newcommand{\umlVHassoc}[3][]{\umlVHrelation[style={tikzuml association style}, #1]{#2}{#3}}% +\newcommand{\umlVHuniassoc}[3][]{\umlVHrelation[style={tikzuml unidirectional association style}, #1]{#2}{#3}}% +\newcommand{\umlVHaggreg}[3][]{\umlVHrelation[style={tikzuml aggregation style}, #1]{#2}{#3}}% +\newcommand{\umlVHuniaggreg}[3][]{\umlVHrelation[style={tikzuml unidirectional aggregation style}, #1]{#2}{#3}}% +\newcommand{\umlVHcompo}[3][]{\umlVHrelation[style={tikzuml composition style}, #1]{#2}{#3}}% +\newcommand{\umlVHunicompo}[3][]{\umlVHrelation[style={tikzuml unidirectional composition style}, #1]{#2}{#3}}% +\newcommand{\umlVHimport}[3][]{\umlVHrelation[style={tikzuml import style}, #1]{#2}{#3}}% +\newcommand{\umlVHnest}[3][]{\umlVHrelation[style={tikzuml nesting style}, #1]{#2}{#3}}% +\newcommand{\umlVHdep}[3][]{\umlVHrelation[style={tikzuml dependency style}, #1]{#2}{#3}}% +\newcommand{\umlVHfriend}[3][]{% + \pgfkeys{/tikzuml/friendrelation/.cd,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{stereo}}{% + \errmessage{TIKZUML ERROR : in umlVHfriend, forbidden option stereo}% + }{% + \ifthenelse{\equal{\keyname}{geometry}}{% + \errmessage{TIKZUML ERROR : in umlVHfriend, forbidden option geometry}% + }{}% + }% + }}% + \pgfkeys{/tikzuml/friendrelation/.cd, #1}% + \umlrelation[geometry=|-, stereo=friend, style={tikzuml dependency style}, #1]{#2}{#3}% +}% +% +\newcommand{\umlHVHinherit}[3][]{\umlHVHrelation[style={tikzuml inherit style}, #1]{#2}{#3}}% +\newcommand{\umlHVHimpl}[3][]{\umlHVHrelation[style={tikzuml implements style}, #1]{#2}{#3}}% +\newcommand{\umlHVHreal}[3][]{\umlHVHrelation[style={tikzuml implements style}, #1]{#2}{#3}}% +\newcommand{\umlHVHassoc}[3][]{\umlHVHrelation[style={tikzuml association style}, #1]{#2}{#3}}% +\newcommand{\umlHVHuniassoc}[3][]{\umlHVHrelation[style={tikzuml unidirectional association style}, #1]{#2}{#3}}% +\newcommand{\umlHVHaggreg}[3][]{\umlHVHrelation[style={tikzuml aggregation style}, #1]{#2}{#3}}% +\newcommand{\umlHVHuniaggreg}[3][]{\umlHVHrelation[style={tikzuml unidirectional aggregation style}, #1]{#2}{#3}}% +\newcommand{\umlHVHcompo}[3][]{\umlHVHrelation[style={tikzuml composition style}, #1]{#2}{#3}}% +\newcommand{\umlHVHunicompo}[3][]{\umlHVHrelation[style={tikzuml unidirectional composition style}, #1]{#2}{#3}}% +\newcommand{\umlHVHimport}[3][]{\umlHVHrelation[style={tikzuml import style}, #1]{#2}{#3}}% +\newcommand{\umlHVHnest}[3][]{\umlHVHrelation[style={tikzuml nesting style}, #1]{#2}{#3}}% +\newcommand{\umlHVHdep}[3][]{\umlHVHrelation[style={tikzuml dependency style}, #1]{#2}{#3}}% +\newcommand{\umlHVHfriend}[3][]{% + \pgfkeys{/tikzuml/friendrelation/.cd,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{stereo}}{% + \errmessage{TIKZUML ERROR : in umlHVHfriend, forbidden option stereo}% + }{% + \ifthenelse{\equal{\keyname}{geometry}}{% + \errmessage{TIKZUML ERROR : in umlHVHfriend, forbidden option geometry}% + }{}% + }% + }}% + \pgfkeys{/tikzuml/friendrelation/.cd, #1}% + \umlrelation[geometry=-|-, stereo=friend, style={tikzuml dependency style}, #1]{#2}{#3}% +}% +% +\newcommand{\umlVHVinherit}[3][]{\umlVHVrelation[style={tikzuml inherit style}, #1]{#2}{#3}}% +\newcommand{\umlVHVimpl}[3][]{\umlVHVrelation[style={tikzuml implements style}, #1]{#2}{#3}}% +\newcommand{\umlVHVreal}[3][]{\umlVHVrelation[style={tikzuml implements style}, #1]{#2}{#3}}% +\newcommand{\umlVHVassoc}[3][]{\umlVHVrelation[style={tikzuml association style}, #1]{#2}{#3}}% +\newcommand{\umlVHVuniassoc}[3][]{\umlVHVrelation[style={tikzuml unidirectional association style}, #1]{#2}{#3}}% +\newcommand{\umlVHVaggreg}[3][]{\umlVHVrelation[style={tikzuml aggregation style}, #1]{#2}{#3}}% +\newcommand{\umlVHVuniaggreg}[3][]{\umlVHVrelation[style={tikzuml unidirectional aggregation style}, #1]{#2}{#3}}% +\newcommand{\umlVHVcompo}[3][]{\umlVHVrelation[style={tikzuml composition style}, #1]{#2}{#3}}% +\newcommand{\umlVHVunicompo}[3][]{\umlVHVrelation[style={tikzuml unidirectional composition style}, #1]{#2}{#3}}% +\newcommand{\umlVHVimport}[3][]{\umlVHVrelation[style={tikzuml import style}, #1]{#2}{#3}}% +\newcommand{\umlVHVnest}[3][]{\umlVHVrelation[style={tikzuml nesting style}, #1]{#2}{#3}}% +\newcommand{\umlVHVdep}[3][]{\umlVHVrelation[style={tikzuml dependency style}, #1]{#2}{#3}}% +\newcommand{\umlVHVfriend}[3][]{% + \pgfkeys{/tikzuml/friendrelation/.cd,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{stereo}}{% + \errmessage{TIKZUML ERROR : in umlVHVfriend, forbidden option stereo}% + }{% + \ifthenelse{\equal{\keyname}{geometry}}{% + \errmessage{TIKZUML ERROR : in umlVHVfriend, forbidden option geometry}% + }{}% + }% + }}% + \pgfkeys{/tikzuml/friendrelation/.cd, #1}% + \umlrelation[geometry=|-|, stereo=friend, style={tikzuml dependency style}, #1]{#2}{#3}% +}% +% +% define a node +\newcommand{\umlnode}[2]{% + \node (#2) at (#1) {};% +}% +% +% main command to define a relation between two classes through a control node +% args : src class +% control node +% dest class +% optional : arg1, arg2, arg: name of the src/dest/dest side class type attribute defined by the relation +% mult1, mult2, mult: multiplicity of the src/dest/dest side class type attribute defined by the relation +% pos1, pos2, pos: position of the src/dest/dest side class type attribute defined by the relation +% align1, align2, align: text justification of the src/dest/dest side class type attribute defined by the relation +% anchor1, anchor2: src/dest anchors on linked classes +% stereo: stereotype of the relation +% pos stereo: position of the stereotype on the relation +% style: style of the relation (association, aggregation, composition, inherit, ...) +% name: rootname used for naming nodes of the relation +\newcommand{\umlCNrelation}[4][]{% + \pgfkeys{/tikzuml/relation/.cd, arg1/.initial={}, arg2/.initial={}, arg/.initial={},% + mult1/.initial={}, mult2/.initial={}, mult/.initial={},% + pos1/.initial=0.2, pos2/.initial=0.8, pos/.initial=tikzumlEmpty,% + align1/.initial={}, align2/.initial={}, align/.initial={},% + anchor1/.initial=tikzumlEmpty, anchor2/.initial=tikzumlEmpty,% + stereo/.initial={}, pos stereo/.initial=1,% + style/.initial=->, name/.initial=relation-\thetikzumlRelationNum,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \errmessage{TIKZUML ERROR : in umlCNrelation, invalid option \keyname}% + }% + }% + \pgfkeys{/tikzuml/relation/.cd,#1}% + \pgfkeys{/tikzuml/relation/.cd, arg1/.get=\tikzumlCNRelationAttrName, arg2/.get=\tikzumlCNRelationAttrNameTO, arg/.get=\tikzumlCNRelationAttrNameTT,% + mult1/.get=\tikzumlCNRelationMultiplicity, mult2/.get=\tikzumlCNRelationMultiplicityTO, mult/.get=\tikzumlCNRelationMultiplicityTT,% + pos1/.get=\tikzumlCNRelationPosition, pos2/.get=\tikzumlCNRelationPositionTO, pos/.get=\tikzumlCNRelationPositionTT,% + align1/.get=\tikzumlCNRelationAlign, align2/.get=\tikzumlCNRelationAlignTO, align/.get=\tikzumlCNRelationAlignTT,% + anchor1/.get=\tikzumlCNRelationSrcAnchor, anchor2/.get=\tikzumlCNRelationDestAnchor,% + stereo/.get=\tikzumlCNRelationStereoType, pos stereo/.get=\tikzumlCNRelationPositionStereotype,% + style/.get=\tikzumlCNRelationStyle, name/.get=\tikzumlCNRelationName% + }% + % + % managing \_ in class names for node names + \def\tikzumlSrcClassName{#2}% + \begingroup% + \def\_{_}\edef\x{\endgroup% + \def\noexpand\tikzumlSrcClassNodeName{\tikzumlSrcClassName}}\x% + % + \StrSubstitute{\tikzumlSrcClassNodeName}{:}{@COLON@}[\tikzumlSrcClassNodeName]% + \StrSubstitute{\tikzumlSrcClassNodeName}{\_}{@UNDERSCORE@}[\tikzumlSrcClassNodeName]% + % + \def\tikzumlDestClassName{#4}% + \begingroup% + \def\_{_}\edef\x{\endgroup% + \def\noexpand\tikzumlDestClassNodeName{\tikzumlDestClassName}}\x% + % + \StrSubstitute{\tikzumlDestClassNodeName}{:}{@COLON@}[\tikzumlDestClassNodeName]% + \StrSubstitute{\tikzumlDestClassNodeName}{\_}{@UNDERSCORE@}[\tikzumlDestClassNodeName]% + % + % managing alias keys + \def\tikzumlCNRelationAttrNameT{\tikzumlCNRelationAttrNameTO\tikzumlCNRelationAttrNameTT}% + \def\tikzumlCNRelationMultiplicityT{\tikzumlCNRelationMultiplicityTO\tikzumlCNRelationMultiplicityTT}% + \def\tikzumlCNRelationAlignT{\tikzumlCNRelationAlignTO\tikzumlCNRelationAlignTT}% + \def\orientationT{\orientationTO\orientationTT}% + % + \ifthenelse{\equal{\tikzumlCNRelationPositionTT}{tikzumlEmpty}}{% + \def\tikzumlCNRelationPositionT{\tikzumlCNRelationPositionTO}% + }{% + \def\tikzumlCNRelationPositionT{\tikzumlCNRelationPositionTT}% + }% + % + \def\attrAlign{}% + \def\multAlign{}% + \def\attrAlignT{}% + \def\multAlignT{}% + % + \ifthenelse{\equal{\tikzumlCNRelationAlign}{left}}{% + \def\attrAlign{above right}% + \def\multAlign{below right}% + }{% + \ifthenelse{\equal{\tikzumlCNRelationAlign}{right}}{% + \def\attrAlign{above left}% + \def\multAlign{below left}% + }{}% + }% + % + \ifthenelse{\equal{\tikzumlCNRelationAlignT}{left}}{% + \def\attrAlignT{above right}% + \def\multAlignT{below right}% + }{% + \ifthenelse{\equal{\tikzumlCNRelationAlignT}{right}}{% + \def\attrAlignT{above left}% + \def\multAlignT{below left}% + }{}% + }% + % + % def stereotype + \ifthenelse{\equal{\tikzumlCNRelationStereoType}{}}{% + \def\stereotype{}% + }{% + \def\stereotype{$\ll$\tikzumlCNRelationStereoType$\gg$}% + }% + % + % def anchors macros + \ifthenelse{\equal{\tikzumlCNRelationSrcAnchor}{tikzumlEmpty}}{% + \def\tikzumlCNRelationSrcAnchor{}% + }{% + \let\tikzumlCNRelationSrcAnchorold\tikzumlCNRelationSrcAnchor% + \def\tikzumlCNRelationSrcAnchor{.\tikzumlCNRelationSrcAnchorold}% + }% + % + \ifthenelse{\equal{\tikzumlCNRelationDestAnchor}{tikzumlEmpty}}{% + \def\tikzumlCNRelationDestAnchor{}% + }{% + \let\tikzumlCNRelationDestAnchorold\tikzumlCNRelationDestAnchor% + \def\tikzumlCNRelationDestAnchor{.\tikzumlCNRelationDestAnchorold}% + }% + % + \setcounter{pos}{100*\real{\tikzumlCNRelationPosition}}% + \setcounter{posT}{100*\real{\tikzumlCNRelationPositionT}}% + \setcounter{posStereo}{100*\real{\tikzumlCNRelationPositionStereotype}}% + % + % straight line + %\setcounter{tikzumlControlNodesNum}{1}% + % + \def\tikzumlFirstArc{node[midway, name=\tikzumlCNRelationName-1, anchor=center] {} }% + \def\tikzumlLastArc{node[midway, name=\tikzumlCNRelationName-3, anchor=center]{} }% + \def\posAttrName{}% + \def\posMultiplicity{}% + \def\posAttrNameT{}% + \def\posMultiplicityT{}% + % + \begin{pgfonlayer}{connections}% + \node (\tikzumlCNRelationName-2) at (#3) {};% + \end{pgfonlayer}% + % + \ifnum\theposStereo>100% + \pgfmathsetmacro{\tikzumlCNRelationPositionStereotype}{(\theposStereo-100)/100}% + \xdef\tikzumlLastArc{\tikzumlLastArc node[pos=\tikzumlCNRelationPositionStereotype, anchor=center] {\stereotype} }% + \else% + \xdef\tikzumlFirstArc{\tikzumlFirstArc node[pos=\tikzumlCNRelationPositionStereotype, anchor=center] {\stereotype} }% + \fi% + % + \ifnum\thepos>100% + \pgfmathsetmacro{\tikzumlCNRelationPosition}{(\thepos-100)/100}% + \def\arcNum{2}% + \else% + \def\arcNum{1}% + \ifnum\thepos=100% + \def\posAttrName{above left}% + \def\posMultiplicity{below right}% + \fi% + \fi% + % + \ifnum\arcNum=1% + \xdef\tikzumlFirstArc{\tikzumlFirstArc node[pos=\tikzumlCNRelationPosition, \posAttrName, \attrAlign] {\tikzumlCNRelationAttrName}% + node[pos=\tikzumlCNRelationPosition, swap, \posMultiplicity, \multAlign] {\tikzumlCNRelationMultiplicity} }% + \fi% + \ifnum\arcNum=2% + \xdef\tikzumlLastArc{\tikzumlLastArc node[pos=\tikzumlCNRelationPosition, \posAttrName, \attrAlign] {\tikzumlCNRelationAttrName}% + node[pos=\tikzumlCNRelationPosition, swap, \posMultiplicity, \multAlign] {\tikzumlCNRelationMultiplicity} }% + \fi% + % + \ifnum\theposT>100% + \pgfmathsetmacro{\tikzumlCNRelationPositionT}{(\theposT-100)/100}% + \def\arcNumT{2}% + \else% + \def\arcNumT{1}% + \ifnum\theposT=100% + \def\posAttrNameT{above left}% + \def\posMultiplicityT{below right}% + \fi% + \fi% + % + \ifnum\arcNumT=1% + \xdef\tikzumlFirstArc{\tikzumlFirstArc node[pos=\tikzumlCNRelationPositionT, \posAttrNameT, \attrAlignT] {\tikzumlCNRelationAttrNameT}% + node[pos=\tikzumlCNRelationPositionT, swap, \posMultiplicityT, \multAlignT] {\tikzumlCNRelationMultiplicityT} }% + \fi% + \ifnum\arcNumT=2% + \xdef\tikzumlLastArc{\tikzumlLastArc node[pos=\tikzumlCNRelationPositionT, \posAttrNameT, \attrAlignT] {\tikzumlCNRelationAttrNameT}% + node[pos=\tikzumlCNRelationPositionT, swap, \posMultiplicityT, \multAlignT] {\tikzumlCNRelationMultiplicityT} }% + \fi% + % + \xdef\tikzumlPath{(\tikzumlSrcClassNodeName\tikzumlCNRelationSrcAnchor) -- \tikzumlFirstArc (\tikzumlCNRelationName-2.base) -- \tikzumlLastArc (\tikzumlDestClassNodeName\tikzumlCNRelationDestAnchor) }% + + \begin{pgfonlayer}{connections}% + \ifthenelse{\equal{\tikzumlCNRelationStyle}{tikzuml nesting style}}{% + \pgfarrowsdeclare{nested}{nested}{...} + { + \tikzumlNestingSymbolSize=0.2pt% + \advance\tikzumlNestingSymbolSize by .5\pgflinewidth% + \pgfsetdash{}{0pt} % do not dash + \pgfsetroundjoin % fix join + \pgfsetroundcap % fix cap + \pgfpathmoveto{\pgfpoint{-16*\tikzumlNestingSymbolSize}{0pt}}% + \pgfpatharc{180}{90}{8*\tikzumlNestingSymbolSize}% + \pgfpatharc{90}{0}{8*\tikzumlNestingSymbolSize}% + \pgfpatharc{0}{-90}{8*\tikzumlNestingSymbolSize}% + \pgfpatharc{-90}{-180}{8*\tikzumlNestingSymbolSize}% + \pgfpathmoveto{\pgfpoint{-8*\tikzumlNestingSymbolSize}{8*\tikzumlNestingSymbolSize}}% + \pgfpathlineto{\pgfpoint{-8*\tikzumlNestingSymbolSize}{-8*\tikzumlNestingSymbolSize}}% + \pgfusepathqstroke% + }% + \draw[auto, \tikzumlCNRelationStyle, nested-, font=\tikzumlDefaultFont] \tikzumlPath ;% + }{ + \draw[auto, \tikzumlCNRelationStyle, font=\tikzumlDefaultFont] \tikzumlPath ;% + } + \end{pgfonlayer}% + % + \stepcounter{tikzumlRelationNum}% +}% +% +% shortcuts for cnrelations +\newcommand{\umlCNinherit}[4][]{\umlCNrelation[style={tikzuml inherit style}, #1]{#2}{#3}{#4}}% +\newcommand{\umlCNimpl}[4][]{\umlCNrelation[style={tikzuml implements style}, #1]{#2}{#3}{#4}}% +\newcommand{\umlCNreal}[4][]{\umlCNrelation[style={tikzuml implements style}, #1]{#2}{#3}{#4}}% +\newcommand{\umlCNassoc}[4][]{\umlCNrelation[style={tikzuml association style}, #1]{#2}{#3}{#4}}% +\newcommand{\umlCNuniassoc}[4][]{\umlCNrelation[style={tikzuml unidirectional association style}, #1]{#2}{#3}{#4}}% +\newcommand{\umlCNaggreg}[4][]{\umlCNrelation[style={tikzuml aggregation style}, #1]{#2}{#3}{#4}}% +\newcommand{\umlCNuniaggreg}[4][]{\umlCNrelation[style={tikzuml unidirectional aggregation style}, #1]{#2}{#3}{#4}}% +\newcommand{\umlCNcompo}[4][]{\umlCNrelation[style={tikzuml composition style}, #1]{#2}{#3}{#4}}% +\newcommand{\umlCNunicompo}[4][]{\umlCNrelation[style={tikzuml unidirectional composition style}, #1]{#2}{#3}{#4}}% +\newcommand{\umlCNimport}[4][]{\umlCNrelation[style={tikzuml import style}, #1]{#2}{#3}{#4}}% +\newcommand{\umlCNnest}[4][]{\umlCNrelation[style={tikzuml nesting style}, #1]{#2}{#3}{#4}}% +\newcommand{\umlCNdep}[4][]{\umlCNrelation[style={tikzuml dependency style}, #1]{#2}{#3}{#4}}% +\newcommand{\umlCNfriend}[4][]{% + \pgfkeys{/tikzuml/friendrelation/.cd,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{stereo}}{% + \errmessage{TIKZUML ERROR : in umlCNfriend, forbidden option stereo}% + }{}% + }}% + \pgfkeys{/tikzuml/friendrelation/.cd, #1}% + \umlCNrelation[stereo=friend, style={tikzuml dependency style}, #1]{#2}{#3}{#4}% +}% +% +% define a note +% arg : attached class +% label of the note +% optional : x,y: coordinates of the note +% width: width of the note +% geometry: geometry of the relation between the note and what it is about +% weight: barycentric weight for a 3-line relation +% arm: length of the first arm +% anchor1, anchor2: anchors of the relation +% draw, fill, text: colors +% style: to manage every default TikZ option +% no coords: to tell that the note position is defined relatively +% to another node (automatically used with TikZ options above, below, left, right, below left, ...) +\newcommand{\umlnote}[3][]{% + \pgfkeys{/tikzuml/note/.cd, x/.initial=\tikzumlDefaultX, y/.initial=\tikzumlDefaultX, width/.initial=\tikzumlNoteDefaultWidth,% + geometry/.initial=\tikzumlRelationDefaultGeometry,% + weight/.initial=\tikzumlRelationDefaultWeight, arm/.initial=auto, style/.style={},% + anchor1/.initial=tikzumlEmpty, anchor2/.initial=tikzumlEmpty,% + draw/.initial=\tikzumlDefaultDrawColor, fill/.initial=\tikzumlNoteDefaultFillColor,% + text/.initial=\tikzumlDefaultTextColor,% + no coords/.is if=tikzumlnoteWithoutCoords,% + no coords=false,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{above}\OR% + \equal{\keyname}{left}\OR% + \equal{\keyname}{below}\OR% + \equal{\keyname}{right}\OR% + \equal{\keyname}{above left}\OR% + \equal{\keyname}{above right}\OR% + \equal{\keyname}{below left}\OR% + \equal{\keyname}{below right}}{% + \IfSubStr{\keyvalue}{ of }{% + \pgfkeys{/tikzuml/note/.cd, no coords}% + }{}% + }{}% + \ifx\keyvalue\pgfkeysnovalue% + \pgfkeys{/tikzuml/note/.cd, style/.append style/.expand once={\keyname}}% + \else% + \pgfkeys{/tikzuml/note/.cd, style/.append style/.expand twice={\expandafter\keyname\expandafter=\keyvalue}}% + \fi% + %\errmessage{TIKZUML ERROR : in umlnote, invalid option \keyname}% + }% + }% + \pgfkeys{/tikzuml/note/.cd, #1}% + \pgfkeys{/tikzuml/note/.cd, x/.get=\tikzumlNoteX, y/.get=\tikzumlNoteY, width/.get=\tikzumlNoteTextWidth,% + geometry/.get=\tikzumlNoteGeometry,% + weight/.get=\tikzumlNoteWeight, arm/.get=\tikzumlNoteArm,% + anchor1/.get=\tikzumlNoteSrcAnchor, anchor2/.get=\tikzumlNoteDestAnchor,% + draw/.get=\tikzumlNoteDrawColor, fill/.get=\tikzumlNoteFillColor,% + text/.get=\tikzumlNoteTextColor% + }% + % + \def\tikzumlClassName{#2}% + \begingroup% + \def\_{_}\edef\x{\endgroup% + \def\noexpand\tikzumlClassNodeName{\tikzumlClassName}}\x% + % + % def anchors macros + \ifthenelse{\equal{\tikzumlNoteSrcAnchor}{tikzumlEmpty}}{% + \def\tikzumlNoteSrcAnchor{}% + }{% + \let\tikzumlNoteSrcAnchorold\tikzumlNoteSrcAnchor% + \def\tikzumlNoteSrcAnchor{.\tikzumlNoteSrcAnchorold}% + }% + % + \ifthenelse{\equal{\tikzumlNoteDestAnchor}{tikzumlEmpty}}{% + \def\tikzumlNoteDestAnchor{}% + }{% + \let\tikzumlNoteDestAnchorold\tikzumlNoteDestAnchor% + \def\tikzumlNoteDestAnchor{.\tikzumlNoteDestAnchorold}% + }% + % + \iftikzumlnoteWithoutCoords% + \node[text=\tikzumlNoteTextColor, text width=\tikzumlNoteTextWidth, font=\tikzumlDefaultFont, outer sep=0, inner xsep=1ex, inner ysep=3ex, /tikzuml/note/style] (note-\thetikzumlNoteNum-coord) {#3};% + \else% + \node[text=\tikzumlNoteTextColor, text width=\tikzumlNoteTextWidth, font=\tikzumlDefaultFont, outer sep=0, inner xsep=1ex, inner ysep=3ex, /tikzuml/note/style] (note-\thetikzumlNoteNum-coord) at (\tikzumlNoteX, \tikzumlNoteY) {#3};% + \fi% + \draw (note-\thetikzumlNoteNum-coord.north east) node[name=note-\thetikzumlNoteNum-right-top, below=2ex, coordinate] {};% + \draw (note-\thetikzumlNoteNum-coord.north east) node[name=note-\thetikzumlNoteNum-top-right, left=2ex, coordinate] {};% + \draw[draw=\tikzumlNoteDrawColor, fill=\tikzumlNoteFillColor] (note-\thetikzumlNoteNum-coord.south west) -- (note-\thetikzumlNoteNum-coord.south east) -- (note-\thetikzumlNoteNum-right-top.base) -- (note-\thetikzumlNoteNum-top-right.base) -- (note-\thetikzumlNoteNum-coord.north west) -- cycle;% + \node[text=\tikzumlNoteTextColor, text width=\tikzumlNoteTextWidth, outer sep=0, inner xsep=1ex, inner ysep=3ex, font=\tikzumlDefaultFont] (note-\thetikzumlNoteNum) at (note-\thetikzumlNoteNum-coord) {#3};% + \draw[draw=\tikzumlNoteDrawColor] (note-\thetikzumlNoteNum-right-top) -| (note-\thetikzumlNoteNum-top-right);% + % + \pgfmathsetmacro{\tikzumlNoteWeightT}{1.0-\tikzumlNoteWeight}% + \node (note-\thetikzumlNoteNum-middle) at (barycentric cs:note-\thetikzumlNoteNum-coord=\tikzumlNoteWeight,\tikzumlClassNodeName=\tikzumlNoteWeightT) {};% + % + \ifthenelse{\equal{\tikzumlNoteGeometry}{--}% + \OR\equal{\tikzumlNoteGeometry}{-|}% + \OR\equal{\tikzumlNoteGeometry}{|-}}{% + \edef\tikzumlnotepath{\tikzumlNoteGeometry} + }{% + \ifthenelse{\equal{\tikzumlNoteGeometry}{-|-}}{% + \ifthenelse{\equal{\tikzumlNoteArm}{auto}}{% + \edef\tikzumlnotepath{-- (note-\thetikzumlNoteNum-coord\tikzumlNoteSrcAnchor -| note-\thetikzumlNoteNum-middle.center) -- (note-\thetikzumlNoteNum-middle.center) -- (note-\thetikzumlNoteNum-middle.center |- \tikzumlClassNodeName\tikzumlNoteDestAnchor) --}% + }{% + \draw (note-\thetikzumlNoteNum-coord\tikzumlNoteSrcAnchor)+(\tikzumlNoteArm,0) node[name=note-\thetikzumlNoteNum-tmp] {}; + \edef\tikzumlnotepath{-- (note-\thetikzumlNoteNum-tmp.center) |-}% + }% + }{% + \ifthenelse{\equal{\tikzumlNoteGeometry}{|-|}}{% + \ifthenelse{\equal{\tikzumlNoteArm}{auto}}{% + \edef\tikzumlnotepath{-- (note-\thetikzumlNoteNum-coord\tikzumlNoteSrcAnchor |- note-\thetikzumlNoteNum-middle.center) -- (note-\thetikzumlNoteNum-middle.center) -- (note-\thetikzumlNoteNum-middle.center -| \tikzumlClassNodeName\tikzumlNoteDestAnchor) --}% + }{% + \draw (note-\thetikzumlNoteNum-coord\tikzumlNoteSrcAnchor)+(0,\tikzumlNoteArm) node[name=note-\thetikzumlNoteNum-tmp] {}; + \edef\tikzumlnotepath{-- (note-\thetikzumlNoteNum-tmp.center) -|}% + }% + + }{% + \errmessage{TIKZUML ERROR : Unknown geometry value !!! It should be in the following list : --, |-, -|, |-|, -|-}% + }% + }% + }% + % + \begin{pgfonlayer}{connections}% + \draw[dashed] (note-\thetikzumlNoteNum-coord\tikzumlNoteSrcAnchor) \tikzumlnotepath (\tikzumlClassNodeName\tikzumlNoteDestAnchor);% + \end{pgfonlayer}% + % + \stepcounter{tikzumlNoteNum}% +}% +% +% shortcuts for note with geometry +\newcommand{\umlHVnote}[3][]{% + \pgfkeys{/tikzuml/note/.cd,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{geometry}}{% + \errmessage{TIKZUML ERROR : in umlHVnote, forbidden option geometry}% + }{}% + }}% + \pgfkeys{/tikzuml/note/.cd, #1}% + \umlnote[geometry=-|, #1]{#2}{#3}% +}% +\newcommand{\umlVHnote}[3][]{% + \pgfkeys{/tikzuml/note/.cd,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{geometry}}{% + \errmessage{TIKZUML ERROR : in umlVHnote, forbidden option geometry}% + }{}% + }}% + \pgfkeys{/tikzuml/note/.cd, #1}% + \umlnote[geometry=|-, #1]{#2}{#3}% +}% +\newcommand{\umlVHVnote}[3][]{% + \pgfkeys{/tikzuml/note/.cd,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{geometry}}{% + \errmessage{TIKZUML ERROR : in umlVHVnote, forbidden option geometry}% + }{}% + }}% + \pgfkeys{/tikzuml/note/.cd, #1}% + \umlnote[geometry=|-|, #1]{#2}{#3}% +}% +\newcommand{\umlHVHnote}[3][]{% + \pgfkeys{/tikzuml/note/.cd,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{geometry}}{% + \errmessage{TIKZUML ERROR : in umlHVHnote, forbidden option geometry}% + }{}% + }}% + \pgfkeys{/tikzuml/note/.cd, #1}% + \umlnote[geometry=-|-, #1]{#2}{#3}% +}% +% +% define a uml association class (command) +% args : name of the class +% attributes of the class +% operations of the class +% optional : x,y: coordinates of the class +% width: width of the class node +% type: type of of class (class, interface, typedef, enum) +% template: template parameters +% name: name of the class node +% geometry: geometry of the line +% weight: barycentric weight of the middle part when geometry is a 3-line +% arm: length of first part when geometry id a 3-line +% anchor1, anchor2: src/dest anchors on linked classes +% style: style of the association class (association, aggregation, composition, inherit, ...) +% draw, fill, fill template, text: colors +% style: to manage every default TikZ option +% no coords: to tell that the class position is defined relatively +% to another node (automatically used with TikZ options above, below, left, right, below left, ...) +\newcommand{\umlassocclass}[5][]{% + \pgfkeys{/tikzuml/assocclass/.cd, x/.initial=\tikzumlDefaultX, y/.initial=\tikzumlDefaultX, + width/.initial=\tikzumlClassDefaultWidth, type/.initial=\tikzumlClassDefaultType, style/.style={},% + template/.initial={}, name/.initial=tikzumlEmpty, geometry/.initial=\tikzumlRelationDefaultGeometry,% + weight/.initial=\tikzumlRelationDefaultWeight, arm/.initial=auto,% + anchor1/.initial=tikzumlEmpty, anchor2/.initial=tikzumlEmpty,% + draw/.initial=\tikzumlDefaultDrawColor,% + fill template/.initial=\tikzumlClassTemplateFillColorDefaultFillColor,% + fill/.initial=\tikzumlClassDefaultFillColor,% + text/.initial=\tikzumlDefaultTextColor,% + no coords/.is if=tikzumlassocclassWithoutCoords,% + no coords=false,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{above}\OR% + \equal{\keyname}{left}\OR% + \equal{\keyname}{below}\OR% + \equal{\keyname}{right}\OR% + \equal{\keyname}{above left}\OR% + \equal{\keyname}{above right}\OR% + \equal{\keyname}{below left}\OR% + \equal{\keyname}{below right}}{% + \IfSubStr{\keyvalue}{ of }{% + \pgfkeys{/tikzuml/assocclass/.cd, no coords}% + }{}% + }{}% + \ifx\keyvalue\pgfkeysnovalue% + \pgfkeys{/tikzuml/assocclass/.cd, style/.append style/.expand once={\keyname}}% + \else% + \pgfkeys{/tikzuml/assocclass/.cd, style/.append style/.expand twice={\expandafter\keyname\expandafter=\keyvalue}}% + \fi% + %\errmessage{TIKZUML ERROR : in umlassocclass, invalid option \keyname}% + }% + }% + % + \pgfkeys{/tikzuml/assocclass/.cd,#1}% + \pgfkeys{/tikzuml/assocclass/.cd, x/.get=\tikzumlAssocClassX, y/.get=\tikzumlAssocClassY,% + width/.get=\tikzumlAssocClassMinimumWidth, type/.get=\tikzumlAssocClassTypeTmp,% + template/.get=\tikzumlAssocClassTemplateParam,% + name/.get=\tikzumlAssocClassName, geometry/.get=\tikzumlAssocClassGeometry,% + weight/.get=\tikzumlAssocClassWeight, arm/.get=\tikzumlAssocClassArm,% + anchor1/.get=\tikzumlAssocClassSrcAnchor,% + anchor2/.get=\tikzumlAssocClassDestAnchor,% + draw/.get=\tikzumlAssocClassDrawColor, fill/.get=\tikzumlAssocClassFillColor,% + text/.get=\tikzumlAssocClassTextColor, fill template/.get=\tikzumlAssocClassTemplateFillColor% + }% + % + \ifthenelse{\equal{\tikzumlAssocClassTypeTmp}{class}\OR\equal{\tikzumlAssocClassTypeTmp}{abstract}}{% + \def\tikzumlAssocClassType{}% + }{% + \def\tikzumlAssocClassType{$\ll$\tikzumlAssocClassTypeTmp$\gg$ \\}% + }% + % + \ifthenelse{\equal{\tikzumlAssocClassTemplateParam}{}}{% + \def\tikzumlAssocClassVPadding{}% + \def\tikzumlAssocClassHPadding{}% + }{% + \def\tikzumlAssocClassVPadding{\vspace{0.1em} \\}% + \def\tikzumlAssocClassHPadding{\hspace{0.5ex} $ $}% + }% + % + \def\tikzumlAssocClassName{#2}% + \def\tikzumlAssocClassRelationName{#3}% + % + \begingroup% + \def\_{_}\edef\x{\endgroup% + \def\noexpand\tikzumlAssocClassNodeName{\tikzumlAssocClassName}}\x% + % + \begingroup% + \def\_{_}\edef\x{\endgroup% + \def\noexpand\tikzumlAssocClassRelationNodeName{\tikzumlAssocClassRelationName}}\x% + % + \ifthenelse{\equal{\tikzumlAssocClassName}{tikzumlEmpty}}{}{% + \def\tikzumlAssocClassNodeName{\tikzumlAssocClassName}% + }% + % + \StrSubstitute{\tikzumlAssocClassNodeName}{:}{@COLON@}[\tikzumlAssocClassNodeName]% + \StrSubstitute{\tikzumlAssocClassNodeName}{\_}{@UNDERSCORE@}[\tikzumlAssocClassNodeName]% + % + \ifthenelse{\equal{\tikzumlAssocClassTypeTmp}{abstract}}{% + \let\tikzumlAssocClassNameOld\tikzumlAssocClassName% + \def\tikzumlAssocClassName{{\it \tikzumlAssocClassNameOld}}% + }{}% + % + \def\tikzumlAssocClassPos{\tikzumlAssocClassX,\tikzumlAssocClassY}% + \def\tikzumlAssocClassAttributes{#4}% + \def\tikzumlAssocClassOperations{#5}% + % + % def anchors macros + \ifthenelse{\equal{\tikzumlAssocClassSrcAnchor}{tikzumlEmpty}}{% + \def\tikzumlAssocClassSrcAnchor{}% + }{% + \let\tikzumlAssocClassSrcAnchorold\tikzumlAssocClassSrcAnchor% + \def\tikzumlAssocClassSrcAnchor{.\tikzumlAssocClassSrcAnchorold}% + }% + % + \ifthenelse{\equal{\tikzumlAssocClassDestAnchor}{tikzumlEmpty}}{% + \def\tikzumlAssocClassDestAnchor{}% + }{% + \let\tikzumlAssocClassDestAnchorold\tikzumlAssocClassDestAnchor% + \def\tikzumlAssocClassDestAnchor{.\tikzumlAssocClassDestAnchorold}% + }% + % + \iftikzumlassocclassWithoutCoords% + \node[tikzuml class style, draw=\tikzumlAssocClassDrawColor, fill=\tikzumlAssocClassFillColor, text=\tikzumlAssocClassTextColor, font=\tikzumlDefaultFont, minimum width=\tikzumlAssocClassMinimumWidth, /tikzuml/assocclass/style] (\tikzumlAssocClassNodeName) {\begin{tabular}{c}\tikzumlAssocClassVPadding \tikzumlAssocClassType \tikzumlAssocClassHPadding \textbf{\tikzumlAssocClassName} \tikzumlAssocClassHPadding \end{tabular}% + \nodepart{second}% + \begin{tabular}{l}% + \tikzumlAssocClassAttributes% + \end{tabular}% + \nodepart{third}% + \begin{tabular}{l}% + \tikzumlAssocClassOperations% + \end{tabular}% + };% + \else% + \node[tikzuml class style, draw=\tikzumlAssocClassDrawColor, fill=\tikzumlAssocClassFillColor, text=\tikzumlAssocClassTextColor, font=\tikzumlDefaultFont, minimum width=\tikzumlAssocClassMinimumWidth, /tikzuml/assocclass/style] (\tikzumlAssocClassNodeName) at (\tikzumlAssocClassPos) {\begin{tabular}{c}\tikzumlAssocClassVPadding \tikzumlAssocClassType \tikzumlAssocClassHPadding \textbf{\tikzumlAssocClassName} \tikzumlAssocClassHPadding \end{tabular}% + \nodepart{second}% + \begin{tabular}{l}% + \tikzumlAssocClassAttributes% + \end{tabular}% + \nodepart{third}% + \begin{tabular}{l}% + \tikzumlAssocClassOperations% + \end{tabular}% + };% + \fi% + % + \ifthenelse{\equal{\tikzumlAssocClassTemplateParam}{}}{}{% + \draw (\tikzumlAssocClassNodeName.north east) node[tikzuml template style, name=\tikzumlAssocClassNodeName-template, draw=\tikzumlAssocClassDrawColor, fill=\tikzumlAssocClassTemplateFillColor, text=\tikzumlAssocClassTextColor, font=\tikzumlDefaultFont] {\tikzumlAssocClassTemplateParam};% + }% + % + \pgfmathsetmacro{\tikzumlAssocClassWeightT}{1.0-\tikzumlAssocClassWeight} + \node (\tikzumlAssocClassNodeName-middle) at (barycentric cs:\tikzumlAssocClassNodeName=\tikzumlAssocClassWeight,\tikzumlAssocClassRelationNodeName=\tikzumlAssocClassWeightT) {};% + % + \ifthenelse{\equal{\tikzumlAssocClassGeometry}{--}\OR\equal{\tikzumlAssocClassGeometry}{-|}\OR\equal{\tikzumlAssocClassGeometry}{|-}}{% + \edef\tikzumlassocclasspath{\tikzumlAssocClassGeometry} + }{% + \ifthenelse{\equal{\tikzumlAssocClassGeometry}{-|-}}{% + \ifthenelse{\equal{\tikzumlAssocClassArm}{auto}}{% + \edef\tikzumlassocclasspath{-- (\tikzumlAssocClassNodeName\tikzumlAssocClassSrcAnchor -| \tikzumlAssocClassNodeName-middle.center) -- (\tikzumlAssocClassNodeName-middle.center) -- (\tikzumlAssocClassNodeName-middle.center |- \tikzumlAssocClassRelationNodeName\tikzumlAssocClassDestAnchor) --}% + }{% + \draw (\tikzumlAssocClassNodeName\tikzumlAssocClassSrcAnchor)+(\tikzumlAssocClassArm,0) node[name=\tikzumlAssocClassNodeName-tmp] {}; + \edef\tikzumlnotepath{-- (\tikzumlAssocClassNodeName-tmp.center) |-}% + }% + }{% + \ifthenelse{\equal{\tikzumlAssocClassGeometry}{|-|}}{% + \ifthenelse{\equal{\tikzumlAssocClassArm}{auto}}{% + \edef\tikzumlassocclasspath{-- (\tikzumlAssocClassNodeName\tikzumlAssocClassSrcAnchor |- \tikzumlAssocClassNodeName-middle.center) -- (\tikzumlAssocClassNodeName-middle.center) -- (\tikzumlAssocClassNodeName-middle.center -| \tikzumlAssocClassRelationNodeName\tikzumlAssocClassDestAnchor) --}% + }{% + \draw (\tikzumlAssocClassNodeName\tikzumlAssocClassSrcAnchor)+(0,\tikzumlAssocClassArm) node[name=\tikzumlAssocClassNodeName-tmp] {}; + \edef\tikzumlassocclasspath{-- (\thetikzumlAssocClassNodeName-tmp.center) -|}% + }% + + }{% + \errmessage{TIKZUML ERROR : Unknown geometry value !!! It should be in the following list : --, |-, -|, |-|, -|-}% + }% + }% + }% + % + \begin{pgfonlayer}{connections}% + \draw[dashed] (\tikzumlAssocClassNodeName\tikzumlAssocClassSrcAnchor) \tikzumlassocclasspath (\tikzumlAssocClassRelationNodeName\tikzumlAssocClassDestAnchor);% + \end{pgfonlayer}% + % + % add to fit + \ifnum\c@tikzumlPackageLevel>0% + \edef\tikzumlPackageFitOld{\csname tikzumlPackageFit\tikzumlPackage@parent @@\tikzumlPackage@fitname\endcsname}% + \ifthenelse{\equal{\tikzumlAssocClassTemplateParam}{}}{% + \expandafter\xdef\csname tikzumlPackageFit\tikzumlPackage@parent @@\tikzumlPackage@fitname\endcsname{\tikzumlPackageFitOld (\tikzumlAssocClassNodeName)(\tikzumlAssocClassNodeName-middle)}% + }{% + \expandafter\xdef\csname tikzumlPackageFit\tikzumlPackage@parent @@\tikzumlPackage@fitname\endcsname{\tikzumlPackageFitOld (\tikzumlAssocClassNodeName) (\tikzumlAssocClassNodeName-template)(\tikzumlAssocClassNodeName-middle)}% + }% + \stepcounter{tikzumlPackageClassNum}% + \fi% +}% +% +% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% use case diagrams % +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +\tikzstyle{tikzuml usecase style}=[ellipse, text centered]% +\tikzstyle{tikzuml actor style}=[ellipse, inner sep=0, outer sep=0]% +% +\newcounter{tikzumlSystemUseCaseNum}% +\newcounter{tikzumlSystemLevel}% +\newcounter{tikzumlUseCaseNum}% +\newcounter{tikzumlActorNum}% +% +\newif\iftikzumlusecaseWithoutCoords% +\newif\iftikzumlactorWithoutCoords% +% +% define a system +% arg : name +% optional : x, y: coordinates of the system +% draw, fill, text: colors +\newenvironment{umlsystem}[2][]{% + \gdef\tikzumlSystemFit{}% + \def\tikzumlSystemName{#2}% + \setcounter{tikzumlSystemUseCaseNum}{0}% + % + \pgfkeys{/tikzuml/system/.cd, x/.initial=\tikzumlDefaultX, y/.initial=\tikzumlDefaultX,% + draw/.initial=\tikzumlDefaultDrawColor, fill/.initial=\tikzumlSystemDefaultFillColor,% + text/.initial=\tikzumlDefaultTextColor,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \errmessage{TIKZUML ERROR : in umlsystem, invalid option \keyname}% + }% + }% + % + \pgfkeys{/tikzuml/system/.cd, #1}% + \pgfkeys{/tikzuml/system/.cd, x/.get=\tikzumlSystemXShift, y/.get=\tikzumlSystemYShift,% + draw/.get=\tikzumlSystemDrawColor, fill/.get=\tikzumlSystemFillColor,% + text/.get=\tikzumlSystemTextColor}% + % + \stepcounter{tikzumlSystemLevel}% + % + \begin{scope}[xshift=\tikzumlSystemXShift cm, yshift=\tikzumlSystemYShift cm]% +}{% + \addtocounter{tikzumlSystemLevel}{-1}% + % if contains no usecase, one define a fictive node to enable the fit option + \ifnum\c@tikzumlSystemUseCaseNum=0% + \node[inner xsep=10ex, inner ysep=1em] (\tikzumlSystemName-root) at (0,0) {};% + \xdef\tikzumlSystemFit{(\tikzumlSystemName-root)}% + \fi% + % + \begin{pgfonlayer}{background}% + \node[inner ysep=1em, inner xsep=2ex, fit = \tikzumlSystemFit] (\tikzumlSystemName-tmp) {};% + \node[text=\tikzumlSystemTextColor, font=\tikzumlDefaultFont] (\tikzumlSystemName-caption-tmp) at (\tikzumlSystemName-tmp.north) {\tikzumlSystemName};% + \node[draw=\tikzumlSystemDrawColor, fill=\tikzumlSystemFillColor, text=\tikzumlSystemTextColor, font=\tikzumlDefaultFont, inner ysep=1em, inner xsep=2ex, fit = (\tikzumlSystemName-tmp) (\tikzumlSystemName-caption-tmp)] (\tikzumlSystemName) {};% + \node[text=\tikzumlSystemTextColor, font=\tikzumlDefaultFont] (\tikzumlSystemName-caption) at (\tikzumlSystemName-caption-tmp.north) {\tikzumlSystemName};% + \end{pgfonlayer}% + \end{scope}% + % +}% +% +% define a use case +% arg : label of the use case +% optional : x, y: coordinates of the use case +% name: name of the node +% width: node width +% draw, fill, text: colors +% style: to manage every default TikZ option +% no coords: to tell that the use case position is defined relatively +% to another node (automatically used with TikZ options above, below, left, right, below left, ...) +\newcommand{\umlusecase}[2][]{% + \stepcounter{tikzumlUseCaseNum}% + \pgfkeys{/tikzuml/usecase/.cd, x/.initial=\tikzumlDefaultX, y/.initial=\tikzumlDefaultX, width/.initial=auto,% + name/.initial=usecase-\thetikzumlUseCaseNum,% + draw/.initial=\tikzumlDefaultDrawColor, fill/.initial=\tikzumlUseCaseDefaultFillColor,% + text/.initial=\tikzumlDefaultTextColor, style/.style={},% + no coords/.is if=tikzumlusecaseWithoutCoords,% + no coords=false,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{above}\OR% + \equal{\keyname}{left}\OR% + \equal{\keyname}{below}\OR% + \equal{\keyname}{right}\OR% + \equal{\keyname}{above left}\OR% + \equal{\keyname}{above right}\OR% + \equal{\keyname}{below left}\OR% + \equal{\keyname}{below right}}{% + \IfSubStr{\keyvalue}{ of }{% + \pgfkeys{/tikzuml/usecase/.cd, no coords}% + }{}% + }{}% + \ifx\keyvalue\pgfkeysnovalue% + \pgfkeys{/tikzuml/usecase/.cd, style/.append style/.expand once={\keyname}}% + \else% + \pgfkeys{/tikzuml/usecase/.cd, style/.append style/.expand twice={\expandafter\keyname\expandafter=\keyvalue}}% + \fi% + %\errmessage{TIKZUML ERROR : in umlusecase, invalid option \keyname}% + }% + }% + \pgfkeys{/tikzuml/usecase/.cd, #1}% + \pgfkeys{/tikzuml/usecase/.cd, x/.get=\tikzumlUseCaseX, y/.get=\tikzumlUseCaseY, width/.get=\tikzumlUseCaseTextWidth,% + name/.get=\tikzumlUseCaseName,% + draw/.get=\tikzumlUseCaseDrawColor, fill/.get=\tikzumlUseCaseFillColor,% + text/.get=\tikzumlUseCaseTextColor% + }% + % + \def\tikzumlUseCaseText{#2}% + % + \def\tikzumlUseCasePos{\tikzumlUseCaseX,\tikzumlUseCaseY}% + % + \ifthenelse{\equal{\tikzumlUseCaseTextWidth}{auto}}{% + \iftikzumlusecaseWithoutCoords% + \node[tikzuml usecase style, draw=\tikzumlUseCaseDrawColor, fill=\tikzumlUseCaseFillColor, text=\tikzumlUseCaseTextColor, font=\tikzumlDefaultFont, /tikzuml/usecase/style] (\tikzumlUseCaseName) {\tikzumlUseCaseText};% + \else% + \node[tikzuml usecase style, draw=\tikzumlUseCaseDrawColor, fill=\tikzumlUseCaseFillColor, text=\tikzumlUseCaseTextColor, font=\tikzumlDefaultFont, /tikzuml/usecase/style] (\tikzumlUseCaseName) at (\tikzumlUseCasePos) {\tikzumlUseCaseText};% + \fi% + }{% + \iftikzumlusecaseWithoutCoords% + \node[tikzuml usecase style, draw=\tikzumlUseCaseDrawColor, fill=\tikzumlUseCaseFillColor, text=\tikzumlUseCaseTextColor, font=\tikzumlDefaultFont, text width=\tikzumlUseCaseTextWidth, /tikzuml/usecase/style] (\tikzumlUseCaseName) {\tikzumlUseCaseText};% + \else% + \node[tikzuml usecase style, draw=\tikzumlUseCaseDrawColor, fill=\tikzumlUseCaseFillColor, text=\tikzumlUseCaseTextColor, font=\tikzumlDefaultFont, text width=\tikzumlUseCaseTextWidth, /tikzuml/usecase/style] (\tikzumlUseCaseName) at (\tikzumlUseCasePos) {\tikzumlUseCaseText};% + \fi% + }% + % + % add to fit + \ifnum\c@tikzumlSystemLevel>0% + \let\tikzumlSystemFitOld\tikzumlSystemFit% + \xdef\tikzumlSystemFit{\tikzumlSystemFitOld (\tikzumlUseCaseName)}% + \stepcounter{tikzumlSystemUseCaseNum}% + \fi% +}% +% +% define the actor symbol +% optional : global tikzpicture styles +\newcommand{\picturedactor}[1]{% + \pgfkeys{/tikzuml/picactor/.cd, scale/.initial=1, .unknown/.code={}}% + \pgfkeys{/tikzuml/picactor/.cd,#1}% + \pgfkeys{/tikzuml/picactor/.cd, scale/.get=\tikzumlPicturedActorScale}% + % + \begin{tikzpicture}[#1]% + \coordinate (head) at (0,4ex);% + \coordinate (left-hand) at (-2ex,2ex);% + \coordinate (right-hand) at (2ex,2ex);% + \coordinate (left-foot) at (-2ex,-2ex);% + \coordinate (right-foot) at (2ex,-2ex);% + \coordinate (empty) at (0,-3ex);% + \draw (empty) (0,0) -- (head);% + \draw (left-hand) -- (right-hand);% + \draw (0,0) -- (left-foot) (0,0) -- (right-foot);% + \node[fill, draw, circle, inner sep=\tikzumlPicturedActorScale*0.3333ex, minimum size=\tikzumlPicturedActorScale*2ex, anchor=base] at (head) {};% + \end{tikzpicture}% +}% +% +% define an actor +% arg : var name +% optional : x, y: coordinates of the actor +% scale: scale factor of the actor symbol +% below: distance between the actor symbol and its name below +% draw, text: colors +% style: to manage every default TikZ option +% no coords: to tell that the actor position is defined relatively +% to another node (automatically used with TikZ options above, below, left, right, below left, ...) +\newcommand{\umlactor}[2][]{% + \stepcounter{tikzumlActorNum}% + \pgfkeys{/tikzuml/actor/.cd, x/.initial=\tikzumlDefaultX, y/.initial=\tikzumlDefaultX, scale/.initial=1, below/.initial=\tikzumlActorDefaultBelow,% + draw/.initial=\tikzumlDefaultDrawColor, text/.initial=\tikzumlDefaultTextColor,% + style/.style={},% + no coords/.is if=tikzumlactorWithoutCoords,% + no coords=false,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{above}\OR% + \equal{\keyname}{left}\OR% + \equal{\keyname}{below}\OR% + \equal{\keyname}{right}\OR% + \equal{\keyname}{above left}\OR% + \equal{\keyname}{above right}\OR% + \equal{\keyname}{below left}\OR% + \equal{\keyname}{below right}}{% + \IfSubStr{\keyvalue}{ of }{% + \pgfkeys{/tikzuml/actor/.cd, no coords}% + }{}% + }{}% + \ifx\keyvalue\pgfkeysnovalue% + \pgfkeys{/tikzuml/actor/.cd, style/.append style/.expand once={\keyname}}% + \else% + \pgfkeys{/tikzuml/actor/.cd, style/.append style/.expand twice={\expandafter\keyname\expandafter=\keyvalue}}% + \fi% + %\errmessage{TIKZUML ERROR : in umlactor, invalid option \keyname}% + }% + }% + \pgfkeys{/tikzuml/actor/.cd, #1}% + \pgfkeys{/tikzuml/actor/.cd,% + x/.get=\tikzumlActorX, y/.get=\tikzumlActorY, scale/.get=\tikzumlActorScale,% + below/.get=\tikzumlActorBelow,% + draw/.get=\tikzumlActorDrawColor, text/.get=\tikzumlActorTextColor}% + % + \def\tikzumlActorName{#2}% + \begingroup% + \def\_{_}\edef\x{\endgroup% + \def\noexpand\tikzumlActorNodeName{\tikzumlActorName}}\x% + % + \def\tikzumlActorPos{\tikzumlActorX,\tikzumlActorY}% + % + \iftikzumlactorWithoutCoords% + \node[tikzuml actor style, text=\tikzumlActorTextColor, font=\tikzumlDefaultFont, /tikzuml/actor/style] (\tikzumlActorNodeName) {\picturedactor{scale=\tikzumlActorScale, fill=white, draw=\tikzumlActorDrawColor, thick}};% + \else% + \node[tikzuml actor style, text=\tikzumlActorTextColor, font=\tikzumlDefaultFont, /tikzuml/actor/style] (\tikzumlActorNodeName) at (\tikzumlActorPos) {\picturedactor{scale=\tikzumlActorScale, fill=white, draw=\tikzumlActorDrawColor, thick}};% + \fi% + \node[text=\tikzumlActorTextColor, font=\tikzumlDefaultFont, below=\tikzumlActorScale*\tikzumlActorBelow] at (\tikzumlActorNodeName) {\tikzumlActorName};% + % +}% + +% shortcuts for include and extend relation +\newcommand{\umlinclude}[3][]{% + \pgfkeys{/tikzuml/includerelation/.cd,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{stereo}}{% + \errmessage{TIKZUML ERROR : in umlinclude, forbidden option stereo}% + }{% + \ifthenelse{\equal{\keyname}{geometry}}{% + \errmessage{TIKZUML ERROR : in umlinclude, forbidden option geometry}% + }{}% + }% + }}% + \pgfkeys{/tikzuml/includerelation/.cd, #1}% + \umlrelation[stereo=include, style={tikzuml dependency style}, #1]{#2}{#3}% +}% +\newcommand{\umlextend}[3][]{% + \pgfkeys{/tikzuml/extendrelation/.cd,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{stereo}}{% + \errmessage{TIKZUML ERROR : in umlextend, forbidden option stereo}% + }{% + \ifthenelse{\equal{\keyname}{geometry}}{% + \errmessage{TIKZUML ERROR : in umlextend, forbidden option geometry}% + }{}% + }% + }}% + \pgfkeys{/tikzuml/extendrelation/.cd, #1}% + \umlrelation[stereo=extend, style={tikzuml dependency style}, #1]{#2}{#3}% +}% +% +\newcommand{\umlHVinclude}[3][]{% + \pgfkeys{/tikzuml/includerelation/.cd,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{stereo}}{% + \errmessage{TIKZUML ERROR : in umlHVinclude, forbidden option stereo}% + }{% + \ifthenelse{\equal{\keyname}{geometry}}{% + \errmessage{TIKZUML ERROR : in umlHVinclude, forbidden option geometry}% + }{}% + }% + }}% + \pgfkeys{/tikzuml/includerelation/.cd, #1}% + \umlrelation[geometry=-|, stereo=include, style={tikzuml dependency style}, #1]{#2}{#3}% +}% +\newcommand{\umlHVextend}[3][]{% + \pgfkeys{/tikzuml/extendrelation/.cd,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{stereo}}{% + \errmessage{TIKZUML ERROR : in umlHVextend, forbidden option stereo}% + }{% + \ifthenelse{\equal{\keyname}{geometry}}{% + \errmessage{TIKZUML ERROR : in umlHVextend, forbidden option geometry}% + }{}% + }% + }}% + \pgfkeys{/tikzuml/extendrelation/.cd, #1}% + \umlrelation[geometry=-|, stereo=extend, style={tikzuml dependency style}, #1]{#2}{#3}% +}% +% +\newcommand{\umlVHinclude}[3][]{% + \pgfkeys{/tikzuml/includerelation/.cd,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{stereo}}{% + \errmessage{TIKZUML ERROR : in umlVHinclude, forbidden option stereo}% + }{% + \ifthenelse{\equal{\keyname}{geometry}}{% + \errmessage{TIKZUML ERROR : in umlVHinclude, forbidden option geometry}% + }{}% + }% + }}% + \pgfkeys{/tikzuml/includerelation/.cd, #1}% + \umlrelation[geometry=|-, stereo=include, style={tikzuml dependency style}, #1]{#2}{#3}% +}% +\newcommand{\umlVHextend}[3][]{% + \pgfkeys{/tikzuml/extendrelation/.cd,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{stereo}}{% + \errmessage{TIKZUML ERROR :in umlVHextend, forbidden option stereo}% + }{% + \ifthenelse{\equal{\keyname}{geometry}}{% + \errmessage{TIKZUML ERROR : in umlVHextend, forbidden option geometry}% + }{}% + }% + }}% + \pgfkeys{/tikzuml/extendrelation/.cd, #1}% + \umlrelation[geometry=|-, stereo=extend, style={tikzuml dependency style}, #1]{#2}{#3}% +}% +% +\newcommand{\umlHVHinclude}[3][]{% + \pgfkeys{/tikzuml/includerelation/.cd,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{stereo}}{% + \errmessage{TIKZUML ERROR : in umlHVHinclude, forbidden option stereo}% + }{% + \ifthenelse{\equal{\keyname}{geometry}}{% + \errmessage{TIKZUML ERROR : in umlHVHinclude, forbidden option geometry}% + }{}% + }% + }}% + \pgfkeys{/tikzuml/includerelation/.cd, #1}% + \umlrelation[geometry=-|-, stereo=include, style={tikzuml dependency style}, #1]{#2}{#3}% +}% +\newcommand{\umlHVHextend}[3][]{% + \pgfkeys{/tikzuml/extendrelation/.cd,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{stereo}}{% + \errmessage{TIKZUML ERROR : in umlHVHextend, forbidden option stereo}% + }{% + \ifthenelse{\equal{\keyname}{geometry}}{% + \errmessage{TIKZUML ERROR : in umlHVHextend, forbidden option geometry}% + }{}% + }% + }}% + \pgfkeys{/tikzuml/extendrelation/.cd, #1}% + \umlrelation[geometry=-|-, stereo=extend, style={tikzuml dependency style}, #1]{#2}{#3}% +}% +% +\newcommand{\umlVHVinclude}[3][]{% + \pgfkeys{/tikzuml/includerelation/.cd,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{stereo}}{% + \errmessage{TIKZUML ERROR : in umlVHVinclude, forbidden option stereo}% + }{% + \ifthenelse{\equal{\keyname}{geometry}}{% + \errmessage{TIKZUML ERROR : in umlVHVinclude, forbidden option geometry}% + }{}% + }% + }}% + \pgfkeys{/tikzuml/includerelation/.cd, #1}% + \umlrelation[geometry=|-|, stereo=include, style={tikzuml dependency style}, #1]{#2}{#3}% +}% +\newcommand{\umlVHVextend}[3][]{% + \pgfkeys{/tikzuml/extendrelation/.cd,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{stereo}}{% + \errmessage{TIKZUML ERROR : in umlVHVextend, forbidden option stereo}% + }{% + \ifthenelse{\equal{\keyname}{geometry}}{% + \errmessage{TIKZUML ERROR : in umlVHVextend, forbidden option geometry}% + }{}% + }% + }}% + \pgfkeys{/tikzuml/extendrelation/.cd, #1}% + \umlrelation[geometry=|-|, stereo=extend, style={tikzuml dependency style}, #1]{#2}{#3}% +}% +% +\newcommand{\umlCNinclude}[4][]{% + \pgfkeys{/tikzuml/includerelation/.cd,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{stereo}}{% + \errmessage{TIKZUML ERROR : in umlCNinclude, forbidden option stereo}% + }{}% + }}% + \pgfkeys{/tikzuml/includerelation/.cd, #1}% + \umlCNrelation[stereo=include, style={tikzuml dependency style}, #1]{#2}{#3}{#4}% +}% +\newcommand{\umlCNextend}[4][]{% + \pgfkeys{/tikzuml/extendrelation/.cd,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{stereo}}{% + \errmessage{TIKZUML ERROR : in umlCNextend, forbidden option stereo}% + }{}% + }}% + \pgfkeys{/tikzuml/extendrelation/.cd, #1}% + \umlCNrelation[stereo=extend, style={tikzuml dependency style}, #1]{#2}{#3}{#4}% +}% +% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% state diagrams % +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +\tikzstyle{tikzuml state style}=[rectangle split, rectangle split parts=2, rounded corners, inner xsep=1.5ex]% +\tikzstyle{tikzuml transition style}=[color=\tikzumlDefaultDrawColor, rounded corners, -angle 45]% +% +\newcounter{tikzumlStateJoinNum}% +\newcounter{tikzumlStateDecisionNum}% +\newcounter{tikzumlStateInitialNum}% +\newcounter{tikzumlStateFinalNum}% +\newcounter{tikzumlStateEnterNum}% +\newcounter{tikzumlStateExitNum}% +\newcounter{tikzumlStateEndNum}% +\newcounter{tikzumlStateHistoryNum}% +\newcounter{tikzumlStateDeepHistoryNum}% +\newcounter{tikzumlStateLevel}% +\newcounter{tikzumlStateSubStateNum}% +\newcounter{tikzumlStateText}% +% +\newif\iftikzumlstatejoinWithoutCoords% +\newif\iftikzumlstatedecisionWithoutCoords% +\newif\iftikzumlstateinitialWithoutCoords% +\newif\iftikzumlstatefinalWithoutCoords% +\newif\iftikzumlstateenterWithoutCoords% +\newif\iftikzumlstateexitWithoutCoords% +\newif\iftikzumlstateendWithoutCoords% +\newif\iftikzumlstatehistoryWithoutCoords% +\newif\iftikzumlstatedeephistoryWithoutCoords% +\newif\iftikzumlstateWithoutCoords% +% +% define a uml join state +% args : name of the state +% optional : x,y: coordinates of the state +% width: width of the state node +% name: name of the state node +% color: color of the join symbol +% style: to manage every default TikZ option +% no coords: to tell that the state position is defined relatively +% to another node (automatically used with TikZ options above, below, left, right, below left, ...) +\newcommand{\umlstatejoin}[1][]{% + \pgfkeys{/tikzuml/statejoin/.cd, x/.initial=\tikzumlDefaultX, y/.initial=\tikzumlDefaultX, width/.initial=\tikzumlStateJoinDefaultWidth,% + name/.initial=statejoin-\thetikzumlStateJoinNum,% + color/.initial=\tikzumlDefaultDrawColor, style/.style={},% + no coords/.is if=tikzumlstatejoinWithoutCoords,% + no coords=false,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{above}\OR% + \equal{\keyname}{left}\OR% + \equal{\keyname}{below}\OR% + \equal{\keyname}{right}\OR% + \equal{\keyname}{above left}\OR% + \equal{\keyname}{above right}\OR% + \equal{\keyname}{below left}\OR% + \equal{\keyname}{below right}}{% + \IfSubStr{\keyvalue}{ of }{% + \pgfkeys{/tikzuml/statejoin/.cd, no coords}% + }{}% + }{}% + \ifx\keyvalue\pgfkeysnovalue% + \pgfkeys{/tikzuml/statejoin/.cd, style/.append style/.expand once={\keyname}}% + \else% + \pgfkeys{/tikzuml/statejoin/.cd, style/.append style/.expand twice={\expandafter\keyname\expandafter=\keyvalue}}% + \fi% + %\errmessage{TIKZUML ERROR : in umlstatejoin, invalid option \keyname}% + }% + }% + \pgfkeys{/tikzuml/statejoin/.cd, #1}% + \pgfkeys{/tikzuml/statejoin/.cd, x/.get=\tikzumlStateJoinX, y/.get=\tikzumlStateJoinY, width/.get=\tikzumlStateJoinMinimumWidth,% + name/.get=\tikzumlStateJoinName, color/.get=\tikzumlStateJoinColor% + }% + % + \def\tikzumlStateJoinPos{\tikzumlStateJoinX,\tikzumlStateJoinY}% + % + \iftikzumlstatejoinWithoutCoords% + \node[circle, minimum size=\tikzumlStateJoinMinimumWidth, draw=\tikzumlStateJoinColor, fill=\tikzumlStateJoinColor, /tikzuml/statejoin/style] (\tikzumlStateJoinName) {};% + \else% + \node[circle, minimum size=\tikzumlStateJoinMinimumWidth, draw=\tikzumlStateJoinColor, fill=\tikzumlStateJoinColor, /tikzuml/statejoin/style] (\tikzumlStateJoinName) at (\tikzumlStateJoinPos) {};% + \fi% + % + % add to fit + \ifnum\c@tikzumlStateLevel>0% + \edef\tikzumlStateFitOld{\csname tikzumlStateFit\tikzumlState@parent @@\tikzumlState@fitname\endcsname}% + \expandafter\xdef\csname tikzumlStateFit\tikzumlState@parent @@\tikzumlState@fitname\endcsname{\tikzumlStateFitOld (\tikzumlStateJoinName)}% + \stepcounter{tikzumlStateSubStateNum}% + \fi% + \stepcounter{tikzumlStateJoinNum}% +}% +% +% define a uml decision state +% args : name of the state +% optional : x,y: coordinates of the state +% width: width of the state node +% name: name of the state node +% color: color of the join symbol +% style: to manage every default TikZ option +% no coords: to tell that the state position is defined relatively +% to another node (automatically used with TikZ options above, below, left, right, below left, ...) +\newcommand{\umlstatedecision}[1][]{% + \pgfkeys{/tikzuml/statedecision/.cd, x/.initial=\tikzumlDefaultX, y/.initial=\tikzumlDefaultX, width/.initial=\tikzumlStateDecisionDefaultWidth,% + name/.initial=statedecision-\thetikzumlStateDecisionNum,% + color/.initial=\tikzumlDefaultDrawColor, style/.style={},% + no coords/.is if=tikzumlstatedecisionWithoutCoords,% + no coords=false,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{above}\OR% + \equal{\keyname}{left}\OR% + \equal{\keyname}{below}\OR% + \equal{\keyname}{right}\OR% + \equal{\keyname}{above left}\OR% + \equal{\keyname}{above right}\OR% + \equal{\keyname}{below left}\OR% + \equal{\keyname}{below right}}{% + \IfSubStr{\keyvalue}{ of }{% + \pgfkeys{/tikzuml/statedecision/.cd, no coords}% + }{}% + }{}% + \ifx\keyvalue\pgfkeysnovalue% + \pgfkeys{/tikzuml/statedecision/.cd, style/.append style/.expand once={\keyname}}% + \else% + \pgfkeys{/tikzuml/statedecision/.cd, style/.append style/.expand twice={\expandafter\keyname\expandafter=\keyvalue}}% + \fi% + %\errmessage{TIKZUML ERROR : in umlstatedecision, invalid option \keyname}% + }% + }% + \pgfkeys{/tikzuml/statedecision/.cd, #1}% + \pgfkeys{/tikzuml/statedecision/.cd, x/.get=\tikzumlStateDecisionX, y/.get=\tikzumlStateDecisionY, width/.get=\tikzumlStateDecisionMinimumWidth,% + name/.get=\tikzumlStateDecisionName, color/.get=\tikzumlStateDecisionColor% + }% + % + \def\tikzumlStateDecisionPos{\tikzumlStateDecisionX,\tikzumlStateDecisionY}% + % + \iftikzumlstatedecisionWithoutCoords% + \node[rectangle, rotate=45, minimum size=\tikzumlStateDecisionMinimumWidth, draw=\tikzumlStateDecisionColor, /tikzuml/statedecision/style] (\tikzumlStateDecisionName) {};% + \else% + \node[rectangle, rotate=45, minimum size=\tikzumlStateDecisionMinimumWidth, draw=\tikzumlStateDecisionColor, /tikzuml/statedecision/style] (\tikzumlStateDecisionName) at (\tikzumlStateDecisionPos) {};% + \fi% + % + % add to fit + \ifnum\c@tikzumlStateLevel>0% + \edef\tikzumlStateFitOld{\csname tikzumlStateFit\tikzumlState@parent @@\tikzumlState@fitname\endcsname}% + \expandafter\xdef\csname tikzumlStateFit\tikzumlState@parent @@\tikzumlState@fitname\endcsname{\tikzumlStateFitOld (\tikzumlStateDecisionName)}% + \stepcounter{tikzumlStateSubStateNum}% + \fi% + \stepcounter{tikzumlStateDecisionNum}% +}% +% +% define a uml initial state +% args : name of the state +% optional : x,y: coordinates of the state +% width: width of the state node +% name: name of the state node +% entry, do, exit: entry/do/exit action of the state +% color: color of the join symbol +% style: to manage every default TikZ option +% no coords: to tell that the state position is defined relatively +% to another node (automatically used with TikZ options above, below, left, right, below left, ...) +\newcommand{\umlstateinitial}[1][]{% + \pgfkeys{/tikzuml/stateinitial/.cd, x/.initial=\tikzumlDefaultX, y/.initial=\tikzumlDefaultX, width/.initial=\tikzumlStateInitialDefaultWidth,% + name/.initial=stateinitial-\thetikzumlStateInitialNum,% + color/.initial=\tikzumlDefaultDrawColor, style/.style={},% + no coords/.is if=tikzumlstateinitialWithoutCoords,% + no coords=false,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{above}\OR% + \equal{\keyname}{left}\OR% + \equal{\keyname}{below}\OR% + \equal{\keyname}{right}\OR% + \equal{\keyname}{above left}\OR% + \equal{\keyname}{above right}\OR% + \equal{\keyname}{below left}\OR% + \equal{\keyname}{below right}}{% + \IfSubStr{\keyvalue}{ of }{% + \pgfkeys{/tikzuml/stateinitial/.cd, no coords}% + }{}% + }{}% + \ifx\keyvalue\pgfkeysnovalue% + \pgfkeys{/tikzuml/stateinitial/.cd, style/.append style/.expand once={\keyname}}% + \else% + \pgfkeys{/tikzuml/stateinitial/.cd, style/.append style/.expand twice={\expandafter\keyname\expandafter=\keyvalue}}% + \fi% + %\errmessage{TIKZUML ERROR : in umlstateinitial, invalid option \keyname}% + }% + }% + \pgfkeys{/tikzuml/stateinitial/.cd, #1}% + \pgfkeys{/tikzuml/stateinitial/.cd, x/.get=\tikzumlStateInitialX, y/.get=\tikzumlStateInitialY, width/.get=\tikzumlStateInitialMinimumWidth,% + name/.get=\tikzumlStateInitialName, color/.get=\tikzumlStateInitialColor% + }% + % + \def\tikzumlStateInitialPos{\tikzumlStateInitialX,\tikzumlStateInitialY}% + % + \iftikzumlstateinitialWithoutCoords% + \node[circle, minimum size=\tikzumlStateInitialMinimumWidth, fill=\tikzumlStateInitialColor, /tikzuml/stateinitial/style] (\tikzumlStateInitialName) {};% + \else% + \node[circle, minimum size=\tikzumlStateInitialMinimumWidth, fill=\tikzumlStateInitialColor, /tikzuml/stateinitial/style] (\tikzumlStateInitialName) at (\tikzumlStateInitialPos) {};% + \fi% + % + % add to fit + \ifnum\c@tikzumlStateLevel>0% + \edef\tikzumlStateFitOld{\csname tikzumlStateFit\tikzumlState@parent @@\tikzumlState@fitname\endcsname}% + \expandafter\xdef\csname tikzumlStateFit\tikzumlState@parent @@\tikzumlState@fitname\endcsname{\tikzumlStateFitOld (\tikzumlStateInitialName)}% + \stepcounter{tikzumlStateSubStateNum}% + \fi% + \stepcounter{tikzumlStateInitialNum}% +}% +% +% define a uml final state +% args : name of the state +% optional : x,y: coordinates of the state +% width: width of the state node +% name: name of the state node +% color: color of the join symbol +% style: to manage every default TikZ option +% no coords: to tell that the state position is defined relatively +% to another node (automatically used with TikZ options above, below, left, right, below left, ...) +\newcommand{\umlstatefinal}[1][]{% + \pgfkeys{/tikzuml/statefinal/.cd, x/.initial=\tikzumlDefaultX, y/.initial=\tikzumlDefaultX, width/.initial=\tikzumlStateFinalDefaultWidth,% + name/.initial=statefinal-\thetikzumlStateFinalNum,% + color/.initial=\tikzumlDefaultDrawColor, style/.style={},% + no coords/.is if=tikzumlstatefinalWithoutCoords,% + no coords=false,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{above}\OR% + \equal{\keyname}{left}\OR% + \equal{\keyname}{below}\OR% + \equal{\keyname}{right}\OR% + \equal{\keyname}{above left}\OR% + \equal{\keyname}{above right}\OR% + \equal{\keyname}{below left}\OR% + \equal{\keyname}{below right}}{% + \IfSubStr{\keyvalue}{ of }{% + \pgfkeys{/tikzuml/statefinal/.cd, no coords}% + }{}% + }{}% + \ifx\keyvalue\pgfkeysnovalue% + \pgfkeys{/tikzuml/statefinal/.cd, style/.append style/.expand once={\keyname}}% + \else% + \pgfkeys{/tikzuml/statefinal/.cd, style/.append style/.expand twice={\expandafter\keyname\expandafter=\keyvalue}}% + \fi% + %\errmessage{TIKZUML ERROR : in umlstatefinal, invalid option \keyname}% + }% + }% + \pgfkeys{/tikzuml/statefinal/.cd, #1}% + \pgfkeys{/tikzuml/statefinal/.cd, x/.get=\tikzumlStateFinalX, y/.get=\tikzumlStateFinalY, width/.get=\tikzumlStateFinalMinimumWidth,% + name/.get=\tikzumlStateFinalName, color/.get=\tikzumlStateFinalColor% + }% + % + \def\tikzumlStateFinalPos{\tikzumlStateFinalX,\tikzumlStateFinalY}% + % + \iftikzumlstatefinalWithoutCoords% + \node[circle, minimum size=\tikzumlStateFinalMinimumWidth, draw=\tikzumlStateFinalColor, fill=\tikzumlStateFinalColor, double, double distance=0.1cm, /tikzuml/statefinal/style] (\tikzumlStateFinalName) {};% + \else% + \node[circle, minimum size=\tikzumlStateFinalMinimumWidth, draw=\tikzumlStateFinalColor, fill=\tikzumlStateFinalColor, double, double distance=0.1cm, /tikzuml/statefinal/style] (\tikzumlStateFinalName) at (\tikzumlStateFinalPos) {};% + \fi% + % + % add to fit + \ifnum\c@tikzumlStateLevel>0% + \edef\tikzumlStateFitOld{\csname tikzumlStateFit\tikzumlState@parent @@\tikzumlState@fitname\endcsname}% + \expandafter\xdef\csname tikzumlStateFit\tikzumlState@parent @@\tikzumlState@fitname\endcsname{\tikzumlStateFitOld (\tikzumlStateFinalName)}% + \stepcounter{tikzumlStateSubStateNum}% + \fi% + \stepcounter{tikzumlStateFinalNum}% +}% +% +% define a uml enter state +% args : name of the state +% optional : x,y: coordinates of the state +% width: width of the state node +% name: name of the state node +% color: color of the join symbol +% style: to manage every default TikZ option +% no coords: to tell that the state position is defined relatively +% to another node (automatically used with TikZ options above, below, left, right, below left, ...) +\newcommand{\umlstateenter}[1][]{% + \pgfkeys{/tikzuml/stateenter/.cd, x/.initial=\tikzumlDefaultX, y/.initial=\tikzumlDefaultX, width/.initial=\tikzumlStateEnterDefaultWidth,% + name/.initial=stateenter-\thetikzumlStateEnterNum,% + color/.initial=\tikzumlDefaultDrawColor, style/.style={},% + no coords/.is if=tikzumlstateenterWithoutCoords,% + no coords=false,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{above}\OR% + \equal{\keyname}{left}\OR% + \equal{\keyname}{below}\OR% + \equal{\keyname}{right}\OR% + \equal{\keyname}{above left}\OR% + \equal{\keyname}{above right}\OR% + \equal{\keyname}{below left}\OR% + \equal{\keyname}{below right}}{% + \IfSubStr{\keyvalue}{ of }{% + \pgfkeys{/tikzuml/stateenter/.cd, no coords}% + }{}% + }{}% + \ifx\keyvalue\pgfkeysnovalue% + \pgfkeys{/tikzuml/stateenter/.cd, style/.append style/.expand once={\keyname}}% + \else% + \pgfkeys{/tikzuml/stateenter/.cd, style/.append style/.expand twice={\expandafter\keyname\expandafter=\keyvalue}}% + \fi% + %\errmessage{TIKZUML ERROR : in umlstateenter, invalid option \keyname}% + }% + }% + \pgfkeys{/tikzuml/stateenter/.cd, #1}% + \pgfkeys{/tikzuml/stateenter/.cd, x/.get=\tikzumlStateEnterX, y/.get=\tikzumlStateEnterY, width/.get=\tikzumlStateEnterMinimumWidth,% + name/.get=\tikzumlStateEnterName, color/.get=\tikzumlStateEnterColor% + }% + % + \def\tikzumlStateEnterPos{\tikzumlStateEnterX,\tikzumlStateEnterY}% + % + \iftikzumlstateenterWithoutCoords% + \node[circle, minimum size=\tikzumlStateEnterMinimumWidth, draw=\tikzumlStateEnterColor, /tikzuml/stateenter/style] (\tikzumlStateEnterName) {};% + \else% + \node[circle, minimum size=\tikzumlStateEnterMinimumWidth, draw=\tikzumlStateEnterColor, /tikzuml/stateenter/style] (\tikzumlStateEnterName) at (\tikzumlStateEnterPos) {};% + \fi% + % + % add to fit + \ifnum\c@tikzumlStateLevel>0% + \edef\tikzumlStateFitOld{\csname tikzumlStateFit\tikzumlState@parent @@\tikzumlState@fitname\endcsname}% + \expandafter\xdef\csname tikzumlStateFit\tikzumlState@parent @@\tikzumlState@fitname\endcsname{\tikzumlStateFitOld (\tikzumlStateEnterName)}% + \stepcounter{tikzumlStateSubStateNum}% + \fi% + \stepcounter{tikzumlStateEnterNum}% +}% +% +% define a uml exit state +% args : name of the state +% optional : x,y: coordinates of the state +% width: width of the state node +% name: name of the state node +% color: color of the join symbol +% style: to manage every default TikZ option +% no coords: to tell that the state position is defined relatively +% to another node (automatically used with TikZ options above, below, left, right, below left, ...) +\newcommand{\umlstateexit}[1][]{% + \pgfkeys{/tikzuml/stateexit/.cd, x/.initial=\tikzumlDefaultX, y/.initial=\tikzumlDefaultX, width/.initial=\tikzumlStateExitDefaultWidth,% + name/.initial=stateexit-\thetikzumlStateExitNum,% + color/.initial=\tikzumlDefaultDrawColor, style/.style={},% + no coords/.is if=tikzumlstateexitWithoutCoords,% + no coords=false,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{above}\OR% + \equal{\keyname}{left}\OR% + \equal{\keyname}{below}\OR% + \equal{\keyname}{right}\OR% + \equal{\keyname}{above left}\OR% + \equal{\keyname}{above right}\OR% + \equal{\keyname}{below left}\OR% + \equal{\keyname}{below right}}{% + \IfSubStr{\keyvalue}{ of }{% + \pgfkeys{/tikzuml/stateexit/.cd, no coords}% + }{}% + }{}% + \ifx\keyvalue\pgfkeysnovalue% + \pgfkeys{/tikzuml/stateexit/.cd, style/.append style/.expand once={\keyname}}% + \else% + \pgfkeys{/tikzuml/stateexit/.cd, style/.append style/.expand twice={\expandafter\keyname\expandafter=\keyvalue}}% + \fi% + %\errmessage{TIKZUML ERROR : in umlstateexit, invalid option \keyname}% + }% + }% + \pgfkeys{/tikzuml/stateexit/.cd, #1}% + \pgfkeys{/tikzuml/stateexit/.cd, x/.get=\tikzumlStateExitX, y/.get=\tikzumlStateExitY, width/.get=\tikzumlStateExitMinimumWidth,% + name/.get=\tikzumlStateExitName, color/.get=\tikzumlStateExitColor% + }% + % + \def\tikzumlStateExitPos{\tikzumlStateExitX,\tikzumlStateExitY}% + % + \iftikzumlstateexitWithoutCoords% + \node[circle, minimum size=\tikzumlStateExitMinimumWidth, draw=\tikzumlStateExitColor, /tikzuml/stateexit/style] (\tikzumlStateExitName) {};% + \else% + \node[circle, minimum size=\tikzumlStateExitMinimumWidth, draw=\tikzumlStateExitColor, /tikzuml/stateexit/style] (\tikzumlStateExitName) at (\tikzumlStateExitPos) {};% + \fi% + \draw[draw=\tikzumlStateExitColor] (\tikzumlStateExitName.north east) -- (\tikzumlStateExitName.south west) (\tikzumlStateExitName.north west) -- (\tikzumlStateExitName.south east); + % + % add to fit + \ifnum\c@tikzumlStateLevel>0% + \edef\tikzumlStateFitOld{\csname tikzumlStateFit\tikzumlState@parent @@\tikzumlState@fitname\endcsname}% + \expandafter\xdef\csname tikzumlStateFit\tikzumlState@parent @@\tikzumlState@fitname\endcsname{\tikzumlStateFitOld (\tikzumlStateExitName)}% + \stepcounter{tikzumlStateSubStateNum}% + \fi% + \stepcounter{tikzumlStateExitNum}% +}% +% +% define a uml end state +% args : name of the state +% optional : x,y: coordinates of the state +% width: width of the state node +% name: name of the state node +% color: color of the join symbol +% style: to manage every default TikZ option +% no coords: to tell that the state position is defined relatively +% to another node (automatically used with TikZ options above, below, left, right, below left, ...) +\newcommand{\umlstateend}[1][]{% + \pgfkeys{/tikzuml/stateend/.cd, x/.initial=\tikzumlDefaultX, y/.initial=\tikzumlDefaultX, width/.initial=\tikzumlStateEndDefaultWidth,% + name/.initial=stateend-\thetikzumlStateEndNum,% + color/.initial=\tikzumlDefaultDrawColor, style/.style={},% + no coords/.is if=tikzumlstateendWithoutCoords,% + no coords=false,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{above}\OR% + \equal{\keyname}{left}\OR% + \equal{\keyname}{below}\OR% + \equal{\keyname}{right}\OR% + \equal{\keyname}{above left}\OR% + \equal{\keyname}{above right}\OR% + \equal{\keyname}{below left}\OR% + \equal{\keyname}{below right}}{% + \IfSubStr{\keyvalue}{ of }{% + \pgfkeys{/tikzuml/stateend/.cd, no coords}% + }{}% + }{}% + \ifx\keyvalue\pgfkeysnovalue% + \pgfkeys{/tikzuml/stateend/.cd, style/.append style/.expand once={\keyname}}% + \else% + \pgfkeys{/tikzuml/stateend/.cd, style/.append style/.expand twice={\expandafter\keyname\expandafter=\keyvalue}}% + \fi% + %\errmessage{TIKZUML ERROR : in umlstateend, invalid option \keyname}% + }% + }% + \pgfkeys{/tikzuml/stateend/.cd, #1}% + \pgfkeys{/tikzuml/stateend/.cd, x/.get=\tikzumlStateEndX, y/.get=\tikzumlStateEndY, width/.get=\tikzumlStateEndMinimumWidth,% + name/.get=\tikzumlStateEndName, color/.get=\tikzumlStateEndColor% + }% + % + \def\tikzumlStateEndPos{\tikzumlStateEndX,\tikzumlStateEndY}% + % + \iftikzumlstateendWithoutCoords% + \node[circle, minimum size=\tikzumlStateEndMinimumWidth, /tikzuml/stateend/style] (\tikzumlStateEndName) {};% + \else% + \node[circle, minimum size=\tikzumlStateEndMinimumWidth, /tikzuml/stateend/style] (\tikzumlStateEndName) at (\tikzumlStateEndPos) {};% + \fi% + \draw[draw=\tikzumlStateEndColor] (\tikzumlStateEndName.north east) -- (\tikzumlStateEndName.south west) (\tikzumlStateEndName.north west) -- (\tikzumlStateEndName.south east); + % + % add to fit + \ifnum\c@tikzumlStateLevel>0% + \edef\tikzumlStateFitOld{\csname tikzumlStateFit\tikzumlState@parent @@\tikzumlState@fitname\endcsname}% + \expandafter\xdef\csname tikzumlStateFit\tikzumlState@parent @@\tikzumlState@fitname\endcsname{\tikzumlStateFitOld (\tikzumlStateEndName)}% + \stepcounter{tikzumlStateSubStateNum}% + \fi% + \stepcounter{tikzumlStateEndNum}% +}% +% +\newcommand{\picturedhistory}[1]{% + \begin{tikzpicture}[#1]% + \draw[thick] (-0.1cm,-0.15cm) -- (-0.1cm,0.15cm) + (-0.1cm,0) -- (0.1cm,0) + (0.1cm,-0.15cm) -- (0.1cm,0.15cm);% + \end{tikzpicture}% +}% +% +% define a uml history state +% args : name of the state +% optional : x,y: coordinates of the state +% width: width of the state node +% name: name of the state node +% color: color of the join symbol +% style: to manage every default TikZ option +% no coords: to tell that the state position is defined relatively +% to another node (automatically used with TikZ options above, below, left, right, below left, ...) +\newcommand{\umlstatehistory}[1][]{% + \pgfkeys{/tikzuml/statehistory/.cd, x/.initial=\tikzumlDefaultX, y/.initial=\tikzumlDefaultX, width/.initial=\tikzumlStateHistoryDefaultWidth,% + name/.initial=statehistory-\thetikzumlStateHistoryNum,% + color/.initial=\tikzumlDefaultDrawColor, style/.style={},% + no coords/.is if=tikzumlstatehistoryWithoutCoords,% + no coords=false,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{above}\OR% + \equal{\keyname}{left}\OR% + \equal{\keyname}{below}\OR% + \equal{\keyname}{right}\OR% + \equal{\keyname}{above left}\OR% + \equal{\keyname}{above right}\OR% + \equal{\keyname}{below left}\OR% + \equal{\keyname}{below right}}{% + \IfSubStr{\keyvalue}{ of }{% + \pgfkeys{/tikzuml/statehistory/.cd, no coords}% + }{}% + }{}% + \ifx\keyvalue\pgfkeysnovalue% + \pgfkeys{/tikzuml/statehistory/.cd, style/.append style/.expand once={\keyname}}% + \else% + \pgfkeys{/tikzuml/statehistory/.cd, style/.append style/.expand twice={\expandafter\keyname\expandafter=\keyvalue}}% + \fi% + %\errmessage{TIKZUML ERROR : in umlstatehistory, invalid option \keyname}% + }% + }% + \pgfkeys{/tikzuml/statehistory/.cd, #1}% + \pgfkeys{/tikzuml/statehistory/.cd, x/.get=\tikzumlStateHistoryX, y/.get=\tikzumlStateHistoryY, width/.get=\tikzumlStateHistoryMinimumWidth,% + name/.get=\tikzumlStateHistoryName, color/.get=\tikzumlStateHistoryColor% + }% + % + \def\tikzumlStateHistoryPos{\tikzumlStateHistoryX,\tikzumlStateHistoryY}% + % + \iftikzumlstatehistoryWithoutCoords% + \node[circle, minimum size=\tikzumlStateHistoryMinimumWidth, draw=\tikzumlStateHistoryColor, /tikzuml/statehistory/style] (\tikzumlStateHistoryName) {\picturedhistory{draw=\tikzumlStateHistoryColor}};% + \else% + \node[circle, minimum size=\tikzumlStateHistoryMinimumWidth, draw=\tikzumlStateHistoryColor, /tikzuml/statehistory/style] (\tikzumlStateHistoryName) at (\tikzumlStateHistoryPos) {\picturedhistory{draw=\tikzumlStateHistoryColor}};% + \fi% + % + % add to fit + \ifnum\c@tikzumlStateLevel>0% + \edef\tikzumlStateFitOld{\csname tikzumlStateFit\tikzumlState@parent @@\tikzumlState@fitname\endcsname}% + \expandafter\xdef\csname tikzumlStateFit\tikzumlState@parent @@\tikzumlState@fitname\endcsname{\tikzumlStateFitOld (\tikzumlStateHistoryName)}% + \stepcounter{tikzumlStateSubStateNum}% + \fi% + \stepcounter{tikzumlStateHistoryNum}% +}% +% +\newcommand{\pictureddeephistory}[1]{% + \begin{tikzpicture}[#1]% + \draw[thick] (-0.1cm,-0.15cm) -- (-0.1cm,0.15cm) + (-0.1cm,0) -- (0.1cm,0) + (0.1cm,-0.15cm) -- (0.1cm,0.15cm) + (0.23cm,0.19cm) -- (0.23cm,0.11cm) + (0.20cm,0.17cm) -- (0.26cm,0.13cm) + (0.20cm,0.13cm) -- (0.26cm,0.17cm);% + \end{tikzpicture}% +}% +% +% define a uml deep-history state +% args : name of the state +% optional : x,y: coordinates of the state +% width: width of the state node +% name: name of the state node +% color: color of the join symbol +% style: to manage every default TikZ option +% no coords: to tell that the state position is defined relatively +% to another node (automatically used with TikZ options above, below, left, right, below left, ...) +\newcommand{\umlstatedeephistory}[1][]{% + \pgfkeys{/tikzuml/statedeephistory/.cd, x/.initial=\tikzumlDefaultX, y/.initial=\tikzumlDefaultX, width/.initial=\tikzumlStateDeepHistoryDefaultWidth,% + name/.initial=statedeephistory-\thetikzumlStateDeepHistoryNum,% + color/.initial=\tikzumlDefaultDrawColor, style/.style={},% + no coords/.is if=tikzumlstatedeephistoryWithoutCoords,% + no coords=false,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{above}\OR% + \equal{\keyname}{left}\OR% + \equal{\keyname}{below}\OR% + \equal{\keyname}{right}\OR% + \equal{\keyname}{above left}\OR% + \equal{\keyname}{above right}\OR% + \equal{\keyname}{below left}\OR% + \equal{\keyname}{below right}}{% + \IfSubStr{\keyvalue}{ of }{% + \pgfkeys{/tikzuml/statedeephistory/.cd, no coords}% + }{}% + }{}% + \ifx\keyvalue\pgfkeysnovalue% + \pgfkeys{/tikzuml/statedeephistory/.cd, style/.append style/.expand once={\keyname}}% + \else% + \pgfkeys{/tikzuml/statedeephistory/.cd, style/.append style/.expand twice={\expandafter\keyname\expandafter=\keyvalue}}% + \fi% + %\errmessage{TIKZUML ERROR : in umlstatedeephistory, invalid option \keyname}% + }% + }% + \pgfkeys{/tikzuml/statedeephistory/.cd, #1}% + \pgfkeys{/tikzuml/statedeephistory/.cd, x/.get=\tikzumlStateDeepHistoryX, y/.get=\tikzumlStateDeepHistoryY, width/.get=\tikzumlStateDeepHistoryMinimumWidth,% + name/.get=\tikzumlStateDeepHistoryName, color/.get=\tikzumlStateDeepHistoryColor% + }% + % + \def\tikzumlStateDeepHistoryPos{\tikzumlStateDeepHistoryX,\tikzumlStateDeepHistoryY}% + % + \iftikzumlstatedeephistoryWithoutCoords% + \node[circle, minimum size=\tikzumlStateDeepHistoryMinimumWidth, draw=\tikzumlStateDeepHistoryColor, /tikzuml/statedeephistory/style] (\tikzumlStateDeepHistoryName) {\pictureddeephistory{draw=\tikzumlStateDeepHistoryColor}};% + \else% + \node[circle, minimum size=\tikzumlStateDeepHistoryMinimumWidth, draw=\tikzumlStateDeepHistoryColor, /tikzuml/statedeephistory/style] (\tikzumlStateDeepHistoryName) at (\tikzumlStateDeepHistoryPos) {\pictureddeephistory{draw=\tikzumlStateDeepHistoryColor}};% + \fi% + % + % add to fit + \ifnum\c@tikzumlStateLevel>0% + \edef\tikzumlStateFitOld{\csname tikzumlStateFit\tikzumlState@parent @@\tikzumlState@fitname\endcsname}% + \expandafter\xdef\csname tikzumlStateFit\tikzumlState@parent @@\tikzumlState@fitname\endcsname{\tikzumlStateFitOld (\tikzumlStateDeepHistoryName)}% + \stepcounter{tikzumlStateSubStateNum}% + \fi% + \stepcounter{tikzumlStateDeepHistoryNum}% +}% +% +% define a uml state +% args : name of the state +% content of the state +% optional : x,y: coordinates of the state +% width: width of the state node +% name: name of the state node +% entry, do, exit: entry/do/exit action of the state +% draw, fill, text: colors +% style: to manage every default TikZ option +% no coords: to tell that the state position is defined relatively +% to another node (automatically used with TikZ options above, below, left, right, below left, ...) +\newenvironment{umlstate}[2][]{% + \ifnum\thetikzumlStateLevel>0% + \let\tikzumlState@nameold\tikzumlState@fitname% + \let\tikzumlState@parentold\tikzumlState@parent% + \edef\tikzumlState@parent{\tikzumlState@parentold @@\tikzumlState@nameold}% + \else% + \def\tikzumlState@parent{}% + \fi% + % + \stepcounter{tikzumlStateLevel}% + % + \pgfkeys{/tikzuml/state/.cd, x/.initial=\tikzumlDefaultX, y/.initial=\tikzumlDefaultX, width/.initial=\tikzumlStateDefaultWidth,% + name/.initial={},% + entry/.initial={}, do/.initial={}, exit/.initial={},% + draw/.initial=\tikzumlDefaultDrawColor, fill/.initial=\tikzumlStateDefaultFillColor,% + text/.initial=\tikzumlDefaultTextColor, style/.style={},% + no coords/.is if=tikzumlstateWithoutCoords,% + no coords=false,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{above}\OR% + \equal{\keyname}{left}\OR% + \equal{\keyname}{below}\OR% + \equal{\keyname}{right}\OR% + \equal{\keyname}{above left}\OR% + \equal{\keyname}{above right}\OR% + \equal{\keyname}{below left}\OR% + \equal{\keyname}{below right}}{% + \IfSubStr{\keyvalue}{ of }{% + \pgfkeys{/tikzuml/state/.cd, no coords}% + }{}% + }{}% + \ifx\keyvalue\pgfkeysnovalue% + \pgfkeys{/tikzuml/state/.cd, style/.append style/.expand once={\keyname}}% + \else% + \pgfkeys{/tikzuml/state/.cd, style/.append style/.expand twice={\expandafter\keyname\expandafter=\keyvalue}}% + \fi% + % \errmessage{TIKZUML ERROR : in umlstate, invalid option \keyname}% + }% + }% + \pgfkeys{/tikzuml/state/.cd, #1}% + \pgfkeys{/tikzuml/state/.cd, x/.get=\tikzumlStateXShift, y/.get=\tikzumlStateYShift, width/.get=\tikzumlStateMinimumWidth, name/.get=\tikzumlStateName,% + entry/.get=\tikzumlStateEntry, do/.get=\tikzumlStateDo, exit/.get=\tikzumlStateExit,% + draw/.get=\tikzumlStateDrawColor, fill/.get=\tikzumlStateFillColor,% + text/.get=\tikzumlStateTextColor% + }% + % + \ifthenelse{\equal{\tikzumlStateName}{}}{% + \edef\tikzumlState@name{#2}% + }{% + \edef\tikzumlState@name{\tikzumlStateName}% + }% + % + \begingroup% + \def\_{@}\edef\x{\endgroup% + \def\noexpand\tikzumlState@fitname{\tikzumlState@name}}\x% + % + \let\tikzumlState@nodeNameold\tikzumlState@nodeName% + \def\tikzumlState@caption{#2}% + % + \begingroup% + \def\_{_}\edef\x{\endgroup% + \def\noexpand\tikzumlState@nodeName{\tikzumlState@name}}\x% + % + \expandafter\gdef\csname tikzumlStateFit\tikzumlState@parent @@\tikzumlState@fitname\endcsname{}% + % + \setcounter{tikzumlStateSubStateNum}{0}% + \setcounter{tikzumlStateText}{0}% + % + \def\tikzumlStateText{tikzumlEmpty}% + \begin{scope}[xshift=\tikzumlStateXShift cm, yshift=\tikzumlStateYShift cm]% +}{% + % + \def\tikzumlstaterootlabel{\phantom{\tikzumlState@nodeName}}% + % + \def\tikzumlstaterootinnerysep{0.5ex}% + \def\tikzumlstatebodyinnerysep{2ex}% + % + \ifthenelse{\equal{\tikzumlStateEntry}{}}{}{% + \ifnum\c@tikzumlStateText=0% + \def\tikzumlStateText{entry/\tikzumlStateEntry}% + \else% + \let\tikzumlStateTextOld\tikzumlStateText% + \ifthenelse{\equal{\tikzumlStateText}{tikzumlEmpty}}{% + \def\tikzumlStateText{entry/\tikzumlStateEntry}% + }{% + \expandafter\def\expandafter\tikzumlStateText\expandafter{\tikzumlStateTextOld \\ entry/\tikzumlStateEntry}% + }% + \fi% + \setcounter{tikzumlStateText}{1}% + \ifnum\c@tikzumlStateSubStateNum=0% + \def\tikzumlstatebodyinnerysep{0}% + \def\tikzumlstaterootinnerysep{0}% + \fi% + }% + \ifthenelse{\equal{\tikzumlStateDo}{}}{}{% + \ifnum\c@tikzumlStateText=0% + \def\tikzumlStateText{do/\tikzumlStateDo}% + \else% + \let\tikzumlStateTextOld\tikzumlStateText% + \ifthenelse{\equal{\tikzumlStateText}{tikzumlEmpty}}{% + \def\tikzumlStateText{do/\tikzumlStateDo}% + }{% + \expandafter\def\expandafter\tikzumlStateText\expandafter{\tikzumlStateTextOld \\ do/\tikzumlStateDo}% + }% + \fi% + \setcounter{tikzumlStateText}{1}% + \ifnum\c@tikzumlStateSubStateNum=0% + \def\tikzumlstatebodyinnerysep{0}% + \def\tikzumlstaterootinnerysep{0}% + \fi% + }% + \ifthenelse{\equal{\tikzumlStateExit}{}}{}{% + \ifnum\c@tikzumlStateText=0% + \def\tikzumlStateText{exit/\tikzumlStateExit}% + \else% + \let\tikzumlStateTextOld\tikzumlStateText% + \ifthenelse{\equal{\tikzumlStateText}{tikzumlEmpty}}{% + \def\tikzumlStateText{exit/\tikzumlStateExit}% + }{% + \expandafter\def\expandafter\tikzumlStateText\expandafter{\tikzumlStateTextOld \\ exit/\tikzumlStateExit}% + }% + \fi% + \setcounter{tikzumlStateText}{1}% + \ifnum\c@tikzumlStateSubStateNum=0% + \def\tikzumlstatebodyinnerysep{0}% + \def\tikzumlstaterootinnerysep{0}% + \fi% + }% + % + \addtocounter{tikzumlStateLevel}{-1}% + \begin{pgfonlayer}{state\thetikzumlStateLevel}% + % + % if contains nothing, one define a fictive node to enable the fit option + \ifnum\c@tikzumlStateSubStateNum=0% + \iftikzumlstateWithoutCoords% + \node[inner ysep=\tikzumlstaterootinnerysep, minimum width=\tikzumlStateMinimumWidth, /tikzuml/state/style] (\tikzumlState@nodeName-root) {\tikzumlstaterootlabel};% + \else% + \node[inner ysep=\tikzumlstaterootinnerysep, minimum width=\tikzumlStateMinimumWidth, /tikzuml/state/style] (\tikzumlState@nodeName-root) at (0,0) {\tikzumlstaterootlabel};% + \fi% + \expandafter\xdef\csname tikzumlStateFit\tikzumlState@parent @@\tikzumlState@fitname\endcsname{(\tikzumlState@nodeName-root)}% + \fi% + % + \ifnum\c@tikzumlStateLevel>0% + \def\tikzumlStateFitTmp{\csname tikzumlStateFit\tikzumlState@parent\endcsname}% + \expandafter\xdef\csname tikzumlStateFit\tikzumlState@parent\endcsname{\tikzumlStateFitTmp (\tikzumlState@nodeName-body) (\tikzumlState@nodeName-caption)}% + \stepcounter{tikzumlStateSubStateNum}% + \fi% + % + \node[inner xsep=2ex, inner ysep=\tikzumlstatebodyinnerysep, fit = \csname tikzumlStateFit\tikzumlState@parent @@\tikzumlState@fitname\endcsname, /tikzuml/state/style ] (\tikzumlState@nodeName-body) {};% + \def\tikzumlState@orig{body}% + \ifnum\c@tikzumlStateText=1% + \node[above=0] (\tikzumlState@nodeName-texttmp) at (\tikzumlState@nodeName-\tikzumlState@orig.north) {\begin{tabular}{l}\tikzumlStateText \end{tabular}};% + \def\tikzumlState@orig{texttmp}% + \fi% + \node[above] (\tikzumlState@nodeName-captiontmp) at (\tikzumlState@nodeName-\tikzumlState@orig.north) {\tikzumlState@caption};% + \ifnum\c@tikzumlStateText=1% + \node[rounded corners, draw=\tikzumlStateDrawColor, fill=\tikzumlStateFillColor, name=\tikzumlState@nodeName, fit=(\tikzumlState@nodeName-body) (\tikzumlState@nodeName-texttmp) (\tikzumlState@nodeName-captiontmp)] {};% + \else% + \node[rounded corners, draw=\tikzumlStateDrawColor, fill=\tikzumlStateFillColor, name=\tikzumlState@nodeName, fit=(\tikzumlState@nodeName-body) (\tikzumlState@nodeName-captiontmp)] {};% + \fi% + \ifnum\c@tikzumlStateText=1% + \node (\tikzumlState@nodeName-text) at (\tikzumlState@nodeName-texttmp) {\begin{tabular}{l}\tikzumlStateText \end{tabular}};% + \fi% + \node (\tikzumlState@nodeName-caption) at (\tikzumlState@nodeName-captiontmp) {\tikzumlState@caption};% + \draw (\tikzumlState@nodeName-caption.south -| \tikzumlState@nodeName.north west) -- (\tikzumlState@nodeName-caption.south -| \tikzumlState@nodeName.north east);% + \end{pgfonlayer}% + \end{scope}% +}% +% +% shortcut for empty state +\newcommand{\umlbasicstate}[2][]{\begin{umlstate}[#1]{#2}\end{umlstate}}% +% +% command to add text in a state, to be used inside umlstate environment +\newcommand{\umlstatetext}[1]{% + \def\tikzumlStateText{#1}% + \setcounter{tikzumlStateText}{1}% +}% +% +% shortcuts for state transitions macros +\newcommand{\umltrans}[3][]{% + \ifthenelse{\equal{#2}{#3}}{% + \umlrelation[style={tikzuml transition style}, recursive mode=transition, #1]{#2}{#3}% + }{% + \umlrelation[style={tikzuml transition style}, #1]{#2}{#3}% + }% +}% +\newcommand{\umlHVtrans}[3][]{\umlHVrelation[style={tikzuml transition style}, #1]{#2}{#3}}% +\newcommand{\umlVHtrans}[3][]{\umlVHrelation[style={tikzuml transition style}, #1]{#2}{#3}}% +\newcommand{\umlVHVtrans}[3][]{\umlVHVrelation[style={tikzuml transition style}, #1]{#2}{#3}}% +\newcommand{\umlHVHtrans}[3][]{\umlHVHrelation[style={tikzuml transition style}, #1]{#2}{#3}}% +\newcommand{\umlCNtrans}[4][]{\umlCNrelation[style={tikzuml transition style}, #1]{#2}{#3}{#4}}% +% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% sequence diagrams % +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +\tikzstyle{tikzuml synchron-msg style}=[color=\tikzumlDefaultDrawColor, -triangle 45]% +\tikzstyle{tikzuml asynchron-msg style}=[color=\tikzumlDefaultDrawColor, -angle 45]% +\tikzstyle{tikzuml return-msg style}=[color=\tikzumlDefaultDrawColor, dashed, -angle 45]% +\tikzstyle{tikzuml call return style}=[color=\tikzumlDefaultDrawColor, dashed, -angle 45]% +\tikzstyle{tikzuml activity style}=[inner xsep=1ex, inner ysep=1ex]% +% +\newcounter{tikzumlObjectNum}% +\newcounter{tikzumlCallLevel}% +\newcounter{tikzumlCallNum}% +\newcounter{tikzumlFragmentLevel}% +\newcounter{tikzumlFragmentLevelNum}% +\newcounter{tikzumlFragmentNum}% +\newcounter{tikzumlFragmentPartNum}% +\newcounter{tikzumlCallStartFragmentNum}% +\newcounter{tikzumlCallEndFragmentNum}% +% +\newif\iftikzumlobjectNoDDots% +\newif\iftikzumlcreatecallNoDDots% +% +% define a sequence diagram +% +\newenvironment{umlseqdiag}{% + \gdef\tikzumlInCreateCall{0}% + \setcounter{tikzumlObjectNum}{0}% + \setcounter{tikzumlCallLevel}{0}% + \setcounter{tikzumlCallNum}{0}% + \setcounter{tikzumlFragmentLevel}{0}% + \setcounter{tikzumlFragmentLevelNum}{0}% + \setcounter{tikzumlFragmentNum}{0}% + \setcounter{tikzumlFragmentPartNum}{0}% + \setcounter{tikzumlCallStartFragmentNum}{0}% + \setcounter{tikzumlCallEndFragmentNum}{0}% + % + \ifx \@umlactor \@empty + \newcommand{\umlactor}[2][]{% + \pgfkeys{/tikzuml/actorobj/.cd, .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{stereo}}{% + \errmessage{TIKZUML ERROR : in umlactor, forbidden option stereo}% + }{}% + }% + }% + % + \pgfkeys{/tikzuml/actorobj/.cd, ##1}% + \umlobject[stereo=actor, ##1]{##2}% + }% + \else% + \renewcommand{\umlactor}[2][]{% + \pgfkeys{/tikzuml/actorobj/.cd, .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{stereo}}{% + \errmessage{TIKZUML ERROR : in umlactor, forbidden option stereo}% + }{}% + }% + }% + % + \pgfkeys{/tikzuml/actorobj/.cd, ##1}% + \umlobject[stereo=actor, ##1]{##2}% + }% + \fi% + \begin{scope}[font=\tikzumlDefaultFont]% +}{% + % draw lifelines of each object + \begin{pgfonlayer}{lifelines}% + \foreach \id in \tikzumlIdList {% + \draw (\csname tikzumlLastChild@\id \endcsname)+(0,-2.5ex) node[inner sep=0, name=end-\id] {};% + \draw[dotted] (\id) -- (end-\id);% + }% + \end{pgfonlayer}% + \end{scope}% +}% +% +% define the database symbol +% optional : global tikzpicture styles +\newcommand{\pictureddatabase}[1]{% + \pgfkeys{/tikzuml/database/.cd, scale/.initial=1, .unknown/.code={}}% + \pgfkeys{/tikzuml/database/.cd,#1}% + \pgfkeys{/tikzuml/database/.cd, scale/.get=\tikzumlDatabaseScale}% + % + \begin{tikzpicture}[#1]% + \node[fill, draw, ellipse, minimum width=\tikzumlDatabaseScale*4ex, minimum height=\tikzumlDatabaseScale*2ex, inner sep=0] (bottom) at (0,-2ex) {};% + \node[fill, draw, ellipse, minimum width=\tikzumlDatabaseScale*4ex, minimum height=\tikzumlDatabaseScale*2ex, inner sep=0] (top) at (0,4ex) {};% + \fill (bottom.west) rectangle (top.east);% + \begin{scope}% + \clip (-3.5ex,-0.5ex) rectangle (3.5ex,2.5ex);% + \node[draw, dashed, ellipse, minimum width=\tikzumlDatabaseScale*4ex, minimum height=\tikzumlDatabaseScale*2ex, inner sep=0] (bottom2) at (0,-2ex) {};% + \end{scope}% + \node[draw, ellipse, minimum width=\tikzumlDatabaseScale*4ex, minimum height=\tikzumlDatabaseScale*2ex, inner sep=0] (top2) at (0,4ex) {};% + \draw (bottom.west) -- (top.west) (bottom.east) -- (top.east);% + \end{tikzpicture}% +}% +% +% define the entity symbol +% optional : global tikzpicture styles +\newcommand{\picturedentity}[1]{% + \pgfkeys{/tikzuml/entity/.cd, scale/.initial=1, .unknown/.code={}}% + \pgfkeys{/tikzuml/entity/.cd,#1}% + \pgfkeys{/tikzuml/entity/.cd, scale/.get=\tikzumlEntityScale}% + % + \begin{tikzpicture}[#1]% + \node[fill, draw, circle, inner sep=0, minimum size=\tikzumlEntityScale*5ex] (center) at (0,0) {};% + \draw (center.south) node[coordinate, name=bottom] {};% + \draw (bottom)+(-2ex,0) node[coordinate, name=bottom-left] {};% + \draw (bottom)+(2ex,0) node[coordinate, name=bottom-right] {};% + \draw (center) -- (bottom);% + \draw (bottom-left) -- (bottom-right);% + \end{tikzpicture}% +}% +% +% define the boundary symbol +% optional : global tikzpicture styles +\newcommand{\picturedboundary}[1]{% + \pgfkeys{/tikzuml/boundary/.cd, scale/.initial=1, .unknown/.code={}}% + \pgfkeys{/tikzuml/boundary/.cd,#1}% + \pgfkeys{/tikzuml/boundary/.cd, scale/.get=\tikzumlBoundaryScale}% + % + \begin{tikzpicture}[#1] + \node[fill, draw, circle, inner sep=0, minimum size=\tikzumlBoundaryScale*5ex] (center) at (0,0) {}; + \draw (center.west)+(-0.8ex,0) node[coordinate, name=left] {}; + \draw (left)+(0,0.2ex) node[coordinate, name=left-top] {}; + \draw (left)+(0,-0.2ex) node[coordinate, name=left-bottom] {}; + \draw (center) -- (left); + \draw (left-top) -- (left-bottom); + \end{tikzpicture} +}% +% +% define the control symbol +% optional : global tikzpicture styles +\newcommand{\picturedcontrol}[1]{% + \pgfkeys{/tikzuml/control/.cd, scale/.initial=1, .unknown/.code={}}% + \pgfkeys{/tikzuml/control/.cd,#1}% + \pgfkeys{/tikzuml/control/.cd, scale/.get=\tikzumlControlScale}% + % + \begin{tikzpicture}[#1, decoration={markings, mark=at position 0.25 with {\arrow{>}}}] + \node[fill, draw, circle, inner sep=0, minimum size=\tikzumlControlScale*5ex, postaction={decorate}] (center) at (0,0) {}; + \end{tikzpicture} +}% +% +% define a uml object for a sequence diagram +% args : name of the object +% optional : x, y: coordinates of the object +% stereo: stereotype of the object (object, actor, database, boundary, control, entity, multiobject) +% class: class of the object +% scale: scale factor of the object symbol +% draw, fill, text; colors +% no ddots: when used, disable printing of double dots +\newcommand{\umlobject}[2][]{ + \stepcounter{tikzumlObjectNum}% + % + \edef\tikzumlobject@ddot{:}% + \pgfkeys{/tikzuml/obj/.cd, x/.initial=tikzumlEmpty, y/.initial=\tikzumlDefaultX, stereo/.initial=\tikzumlObjectDefaultStereo,% + class/.initial={}, scale/.initial=1,% + draw/.initial=\tikzumlDefaultDrawColor, fill/.initial=\tikzumlObjectDefaultFillColor,% + text/.initial=\tikzumlDefaultTextColor,% + no ddots/.is if=tikzumlobjectNoDDots,% + no ddots=false,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \errmessage{TIKZUML ERROR : in umlobject, invalid option \keyname}% + }% + }% + \pgfkeys{/tikzuml/obj/.cd, #1}% + \pgfkeys{/tikzuml/obj/.cd, x/.get=\tikzumlObjectX, y/.get=\tikzumlObjectY,% + stereo/.get=\tikzumlObjectStereo, class/.get=\tikzumlObjectClass,% + scale/.get=\tikzumlObjectScale,% + draw/.get=\tikzumlObjectDrawColor, fill/.get=\tikzumlObjectFillColor,% + text/.get=\tikzumlObjectTextColor% + }% + % + \iftikzumlobjectNoDDots% + \edef\tikzumlobject@ddot{}% + \fi% + % + \ifthenelse{\equal{\tikzumlObjectX}{tikzumlEmpty}}{% + \pgfmathsetmacro{\tikzumlObjectX}{4*(\thetikzumlObjectNum-1)}% + }{}% + % + \def\tikzumlObjectName{#2}% + \expandafter\xdef\csname tikzumlLastChild@\tikzumlObjectName \endcsname{\tikzumlObjectName}% + % + \ifnum\thetikzumlObjectNum=1% + \xdef\tikzumlIdList{\tikzumlObjectName}% + \else% + \let\tikzumlIdListOld\tikzumlIdList% + \expandafter\xdef\expandafter\tikzumlIdList\expandafter{\tikzumlIdListOld,\tikzumlObjectName}% + \fi% + % + \tikzstyle{tikzuml object box style}=[rectangle, text=\tikzumlObjectTextColor, font=\tikzumlDefaultFont]% + % + \ifthenelse{\equal{\tikzumlObjectStereo}{object}}{% + \tikzstyle{tikzuml object box style}+=[draw=\tikzumlObjectDrawColor, fill=\tikzumlObjectFillColor]% + }{% + \ifthenelse{\equal{\tikzumlObjectStereo}{multi}}{% + \tikzstyle{tikzuml object box style}+=[fill=\tikzumlObjectFillColor]% + }{}% + }% + % + \ifnum\tikzumlInCreateCall=1% + \draw (\tikzumlCreateCallObjectSrc -| \tikzumlObjectX,0) node[tikzuml object box style] (\tikzumlObjectName) {\tikzumlObjectName\tikzumlobject@ddot\tikzumlObjectClass};% + \else% + \node[tikzuml object box style] (\tikzumlObjectName) at (\tikzumlObjectX,\tikzumlObjectY) {\tikzumlObjectName\tikzumlobject@ddot\tikzumlObjectClass};% + \fi% + % + \ifthenelse{\equal{\tikzumlObjectStereo}{multi}}{% + \draw (\tikzumlObjectName.north east)+(0.4ex,0.4ex) node[name=\tikzumlObjectName-tr, coordinate] {}; + \draw (\tikzumlObjectName.north west)+(0.4ex,0.4ex) node[name=\tikzumlObjectName-tl, coordinate] {}; + \draw (\tikzumlObjectName.south east)+(0.4ex,0.4ex) node[name=\tikzumlObjectName-br, coordinate] {}; + \draw (\tikzumlObjectName-tr)+(0.4ex,0.4ex) node[name=\tikzumlObjectName-ttr, coordinate] {}; + \draw (\tikzumlObjectName-tl)+(0.4ex,0.4ex) node[name=\tikzumlObjectName-ttl, coordinate] {}; + \draw (\tikzumlObjectName-br)+(0.4ex,0.4ex) node[name=\tikzumlObjectName-tbr, coordinate] {}; + \fill[fill=\tikzumlObjectFillColor] (\tikzumlObjectName-ttl |- \tikzumlObjectName.north) -- (\tikzumlObjectName-ttl) -- (\tikzumlObjectName-ttr) -- (\tikzumlObjectName-tbr) -- (\tikzumlObjectName-tbr -| \tikzumlObjectName.east) -- (\tikzumlObjectName.north east) -- (\tikzumlObjectName-ttl |- \tikzumlObjectName.north); + \draw[draw=\tikzumlObjectDrawColor] (\tikzumlObjectName-ttl |- \tikzumlObjectName.north) -- (\tikzumlObjectName-ttl) -- (\tikzumlObjectName-ttr) -- (\tikzumlObjectName-tbr) -- (\tikzumlObjectName-tbr -| \tikzumlObjectName.east); + \fill[fill=\tikzumlObjectFillColor] (\tikzumlObjectName-tl |- \tikzumlObjectName.north) -- (\tikzumlObjectName-tl) -- (\tikzumlObjectName-tr) -- (\tikzumlObjectName-br) -- (\tikzumlObjectName-br -| \tikzumlObjectName.east) -- (\tikzumlObjectName.north east) -- (\tikzumlObjectName-tl |- \tikzumlObjectName.north); + \draw[draw=\tikzumlObjectDrawColor] (\tikzumlObjectName-tl |- \tikzumlObjectName.north) -- (\tikzumlObjectName-tl) -- (\tikzumlObjectName-tr) -- (\tikzumlObjectName-br) -- (\tikzumlObjectName-br -| \tikzumlObjectName.east); + \draw[draw=\tikzumlObjectDrawColor] (\tikzumlObjectName.north west) rectangle (\tikzumlObjectName.south east); + }{% + \ifthenelse{\equal{\tikzumlObjectStereo}{object}}{}{% + \node[above=1ex, name=\tikzumlObjectName-picture] at (\tikzumlObjectName) {\csname pictured\tikzumlObjectStereo \endcsname{draw=\tikzumlObjectDrawColor, fill=\tikzumlObjectFillColor, scale=\tikzumlObjectScale}}; + }% + }% +}% +% +% shortcuts for objects +\newcommand{\umlbasicobject}[2][]{% + \pgfkeys{/tikzuml/basicobj/.cd, .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{no ddots}}{% + \errmessage{TIKZUML ERROR : in umlbasicobject, forbidden option no ddots}% + }{}% + }% + }% + \pgfkeys{/tikzuml/basicobj/.cd, #1}% + \umlobject[no ddots, #1]{#2}% +}% +% +\newcommand{\umldatabase}[2][]{% + \pgfkeys{/tikzuml/databaseobj/.cd, .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{stereo}}{% + \errmessage{TIKZUML ERROR : in umldatabase, forbidden option stereo}% + }{}% + }% + }% + \pgfkeys{/tikzuml/databaseobj/.cd, #1}% + \umlobject[stereo=database, #1]{#2}% +}% +\newcommand{\umlentity}[2][]{% + \pgfkeys{/tikzuml/entityobj/.cd, .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{stereo}}{% + \errmessage{TIKZUML ERROR : in umlentity, forbidden option stereo}% + }{}% + }% + }% + \pgfkeys{/tikzuml/entityobj/.cd, #1}% + \umlobject[stereo=entity, #1]{#2}% +}% +\newcommand{\umlcontrol}[2][]{% + \pgfkeys{/tikzuml/controlobj/.cd, .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{stereo}}{% + \errmessage{TIKZUML ERROR : in umlcontrol, forbidden option stereo}% + }{}% + }% + }% + \pgfkeys{/tikzuml/controlobj/.cd, #1}% + \umlobject[stereo=control, #1]{#2}% +}% +\newcommand{\umlboundary}[2][]{% + \pgfkeys{/tikzuml/boundaryobj/.cd, .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{stereo}}{% + \errmessage{TIKZUML ERROR : in umlboundary, forbidden option stereo}% + }{}% + }% + }% + \pgfkeys{/tikzuml/boundaryobj/.cd, #1}% + \umlobject[stereo=boundary, #1]{#2}% +}% +\newcommand{\umlmulti}[2][]{% + \pgfkeys{/tikzuml/multiobj/.cd, .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{stereo}}{% + \errmessage{TIKZUML ERROR : in umlmulti, forbidden option stereo}% + }{}% + }% + }% + \pgfkeys{/tikzuml/multiobj/.cd, #1}% + \umlobject[stereo=multi, #1]{#2}% +}% +% +\newcounter{tikzumlSDNodeNum}% +% +% define a hidden node to lengthen lifeline of a object +% args : object node +% optional : dt: distance between the sdnode and the last call defined on the lifeline of the object +% name: name of the sdnode +\newcommand{\umlsdnode}[2][]{% + \pgfkeys{/tikzuml/sdnode/.cd, dt/.initial=0, name/.initial=tikzumlEmpty}% + \pgfkeys{/tikzuml/sdnode/.cd, #1}% + \pgfkeys{/tikzuml/sdnode/.cd, dt/.get=\tikzumlSDNodeDT, name/.get=\tikzumlSDNodeName}% + % + \ifthenelse{\equal{\tikzumlSDNodeName}{tikzumlEmpty}}{% + \expandafter\def\expandafter\tikzumlSDNode@nodeName{sdnode-\thetikzumlSDNodeNum}% + }{% + \expandafter\def\expandafter\tikzumlSDNode@nodeName{\tikzumlSDNodeName}% + }% + % + \stepcounter{tikzumlSDNodeNum}% + % + \begingroup% + \def\_{_}\edef\x{\endgroup% + \def\noexpand\tikzumlSDNode@objnodeName{#2}}\x% + % + \draw (\expandafter\csname tikzumlLastChild@\tikzumlSDNode@objnodeName \endcsname)+(0,-\tikzumlSDNodeDT ex) node[name=\tikzumlSDNode@nodeName,coordinate] {};% + % + % update last node drawn on sender lifeline + \expandafter\xdef\csname tikzumlLastChild@\tikzumlSDNode@objnodeName \endcsname{\tikzumlSDNode@nodeName}% +}% +% +\newlength{\tikzumlCall@xa}% +\newlength{\tikzumlCall@xb}% +% +% define a uml operation call for sequence diagrams +% args : call sender +% call receiver +% optional : dt: time delay from precedent event end +% name: name of the call +% op: operation name and input args +% return: return value +% type: type of the call (synchron, asynchron) +% draw, fill, text: colors +% padding: time padding from call start and to call end +\newenvironment{umlcall}[3][]{% + \stepcounter{tikzumlCallNum}% + \def\tikzumlCallWithReturn{tikzumlFalse}% + \edef\tikzumlCall@lastchildNum{\thetikzumlCallNum}% for testing presence of sub-calls + \gdef\tikzumlCallBottom{0}% + % + \pgfkeys{/tikzuml/call/.cd, dt/.initial=\tikzumlCallDefaultDT, name/.initial={call-\thetikzumlCallNum},% + op/.initial={}, return/.initial={}, type/.initial=\tikzumlCallDefaultType,% + draw/.initial=\tikzumlDefaultDrawColor, fill/.initial=\tikzumlCallDefaultFillColor,% + text/.initial=\tikzumlDefaultTextColor,% + padding/.initial=\tikzumlCallDefaultPadding,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{with return}}{% + \def\tikzumlCallWithReturn{tikzumlTrue}% + }{% + \errmessage{TIKZUML ERROR : in umlcall, invalid option \keyname}% + }% + }% + }% + \pgfkeys{/tikzuml/call/.cd, #1}% + \pgfkeys{/tikzuml/call/.cd, dt/.get=\tikzumlCallDT, name/.get=\tikzumlCallName, op/.get=\tikzumlCallOp,% + return/.get=\tikzumlCallReturn, type/.get=\tikzumlCallType,% + padding/.get=\tikzumlCallPadding,% + draw/.get=\tikzumlCallDrawColor, fill/.get=\tikzumlCallFillColor,% + text/.get=\tikzumlCallTextColor% + }% + % + \edef\tikzumlfillcall{\tikzumlCallFillColor}% + \edef\tikzumldrawcall{\tikzumlCallDrawColor}% + \edef\tikzumltextcall{\tikzumlCallTextColor}% + \edef\tikzumltypecall{\tikzumlCallType}% + % + \ifthenelse{\equal{\tikzumlCallDT}{tikzumlEmpty}}{% + \ifnum\thetikzumlCallNum=1% + \def\tikzumlCallDT{2}% + \def\tikzumlcallSrc{2}% + \else% + \def\tikzumlCallDT{2}% + \def\tikzumlcallSrc{1}% + \fi% + }{% + \def\tikzumlcallSrc{0}% + }% + % + \let\tikzumlCallStartNodeNameold\tikzumlCallStartNodeName% + \def\tikzumlCallStartNodeName{#2}% + \let\tikzumlCallEndNodeNameold\tikzumlCallEndNodeName% + \def\tikzumlCallEndNodeName{#3}% + \def\tikzumlcallheight{\tikzumlCallPadding}% + % + % managing time delays from previous/parent fragments + \ifnum\thetikzumlCallStartFragmentNum>0% + \let\tikzumlCallDTold\tikzumlCallDT% + \pgfmathparse{0.5*\tikzumlFragment@paddingy+\tikzumlCallDTold}% + \edef\tikzumlCallDT{\pgfmathresult}% + \addtocounter{tikzumlCallStartFragmentNum}{-1} + \fi% + \ifnum\thetikzumlCallEndFragmentNum>0% + \let\tikzumlCallDTold\tikzumlCallDT% + \pgfmathparse{0.5*\tikzumlFragment@paddingy+\tikzumlCallDTold}% + \edef\tikzumlCallDT{\pgfmathresult}% + \addtocounter{tikzumlCallEndFragmentNum}{-1} + \fi% + \ifnum\thetikzumlFragmentPartNum>0% + \let\tikzumlCallDTold\tikzumlCallDT% + \pgfmathparse{0.5*\tikzumlFragment@paddingy+\tikzumlCallDTold}% + \edef\tikzumlCallDT{\pgfmathresult}% + \fi% + % + % managing parent-child structure + \ifnum\thetikzumlCallLevel>0% + \let\tikzumlCall@nameold\tikzumlCall@name% + \edef\tikzumlCall@name{\tikzumlCallName}% + \let\tikzumlCall@parentold\tikzumlCall@parent% + \edef\tikzumlCall@parent{\tikzumlCall@parentold @@\tikzumlCall@nameold}% + \else% + \edef\tikzumlCall@parent{}% + \edef\tikzumlCall@parentold{}% + \edef\tikzumlCall@nameold{} + \edef\tikzumlCall@name{\tikzumlCallName}% + \fi% + % + \begingroup% + \def\_{_}\edef\x{\endgroup% + \def\noexpand\tikzumlCall@nodeName{\tikzumlCall@name}}\x% + % + \let\tikzumlCall@nodeNameold\tikzumlCall@nodeName% + % + \def\tikzumlcallstyle{tikzuml \tikzumlCallType-msg style}% + % + % top node of activity period of call sender + \begin{pgfonlayer}{connections}% + \pgfmathparse{\tikzumlCallDT+\tikzumlcallSrc}% + \draw (\csname tikzumlLastChild@\tikzumlCallStartNodeName \endcsname)+(0,-\pgfmathresult ex) node[coordinate, name=tikzumlTmpNode] {};% + \node[tikzuml activity style] (st-\tikzumlCall@nodeName) at (\tikzumlCallStartNodeName |- tikzumlTmpNode) {};% + % + % update last node drawn on sender lifeline + \expandafter\xdef\csname tikzumlLastChild@\tikzumlCallStartNodeName \endcsname{st-\tikzumlCall@nodeName}% + % + % top node of activity period of call receiver + \ifstrequal{\tikzumlCallStartNodeName}{\tikzumlCallEndNodeName}{% + \draw (st-\tikzumlCall@nodeName)+(0,-0.75*\tikzumlCallPadding ex) node[coordinate, name=tikzumlTmpNode] {};% + \node[tikzuml activity style] (et-\tikzumlCall@nodeName) at (\tikzumlCallStartNodeName |- tikzumlTmpNode) {};% + }{% + \node[tikzuml activity style] (et-\tikzumlCall@nodeName) at (\tikzumlCallEndNodeName |- st-\tikzumlCall@nodeName) {};% + }% + % + % update last node drawn on receiver lifeline + \expandafter\xdef\csname tikzumlLastChild@\tikzumlCallEndNodeName \endcsname{et-\tikzumlCall@nodeName}% + \xdef\tikzumlCallBottomSrc{et-\tikzumlCall@nodeName}% + \end{pgfonlayer}% + % + \stepcounter{tikzumlCallLevel}% +}{% + \addtocounter{tikzumlCallLevel}{-1}% + % + % bottom nodes of activity periods of call sender and receiver + \begin{pgfonlayer}{connections}% + \ifnum\tikzumlCall@lastchildNum=\thetikzumlCallNum% + % + % this test occurs a bug with latex package preview + \ifstrequal{\tikzumlCallStartNodeName}{\tikzumlCallEndNodeName}{% + \draw (\tikzumlCallBottomSrc)+(0,-\tikzumlCallPadding ex) node[coordinate, name=tikzumlTmpNode] {};% + \node[tikzuml activity style] (eb-\tikzumlCall@nodeName) at (\tikzumlCallEndNodeName |- tikzumlTmpNode) {};% + \draw (eb-\tikzumlCall@nodeName)+(0,-0.75*\tikzumlCallPadding ex) node[coordinate, name=tikzumlTmpNode] {};% + \node[tikzuml activity style] (sb-\tikzumlCall@nodeName) at (\tikzumlCallStartNodeName |- tikzumlTmpNode) {};% + }{% + \ifthenelse{\equal{\tikzumlCallReturn}{tikzumlEmpty}}{% + \pgfmathsetmacro{\tikzumlCallPaddingd}{0.5*\tikzumlCallPadding}% + }{% + \pgfmathsetmacro{\tikzumlCallPaddingd}{1.2*\tikzumlCallPadding}% + }% + \draw (\tikzumlCallBottomSrc)+(0,-\tikzumlCallPaddingd ex) node[coordinate, name=tikzumlTmpNode] {};% + \node[tikzuml activity style] (eb-\tikzumlCall@nodeName) at (\tikzumlCallEndNodeName |- tikzumlTmpNode) {};% + \node[tikzuml activity style] (sb-\tikzumlCall@nodeName) at (\tikzumlCallStartNodeName |- eb-\tikzumlCall@nodeName) {};% + }% + \xdef\tikzumlCallBottomSrc{sb-\tikzumlCall@nodeName}% + \else% + % + % managing time delays from previous/parent fragments + \ifnum\thetikzumlCallStartFragmentNum>0% + \let\tikzumlcallheightold\tikzumlCallPadding% + \pgfmathparse{\tikzumlcallheightold+0.5*\tikzumlFragment@paddingy}% + \edef\tikzumlcallheight{\pgfmathresult}% + \addtocounter{tikzumlCallStartFragmentNum}{-1}% + \fi% + \ifnum\thetikzumlCallEndFragmentNum>0% + \let\tikzumlcallheightold\tikzumlCallPadding% + \pgfmathparse{\tikzumlcallheightold+0.5*\tikzumlFragment@paddingy}% + \edef\tikzumlcallheight{\pgfmathresult}% + \addtocounter{tikzumlCallEndFragmentNum}{-1}% + \fi% + % + \ifstrequal{\tikzumlCallStartNodeName}{\tikzumlCallEndNodeName}{% + \draw (\tikzumlCallBottomSrc)+(0,-\tikzumlcallheight ex) node[coordinate, name=tikzumlTmpNode] {};% + \node[tikzuml activity style] (eb-\tikzumlCall@nodeName) at (\tikzumlCallEndNodeName |- tikzumlTmpNode) {};% + \draw (eb-\tikzumlCall@nodeName)+(0,-0.75*\tikzumlCallPadding ex) node[coordinate, name=tikzumlTmpNode] {};% + \node[tikzuml activity style] (sb-\tikzumlCall@nodeName) at (\tikzumlCallStartNodeName |- tikzumlTmpNode) {};% + }{% + \draw (\tikzumlCallBottomSrc)+(0,-\tikzumlcallheight ex) node[coordinate, name=tikzumlTmpNode] {};% + \node[tikzuml activity style] (eb-\tikzumlCall@nodeName) at (\tikzumlCallEndNodeName |- tikzumlTmpNode) {};% + \node[tikzuml activity style] (sb-\tikzumlCall@nodeName) at (\tikzumlCallStartNodeName |- eb-\tikzumlCall@nodeName) {};% + }% + % + \xdef\tikzumlCallBottomSrc{sb-\tikzumlCall@nodeName}% + \fi% + \end{pgfonlayer}% + % + % draw activity periods + \begin{pgfonlayer}{activity}% + \ifstrequal{\tikzumlCallStartNodeName}{\tikzumlCallEndNodeName}{% + % draw root activity period only + \ifnum\thetikzumlCallLevel=0% + \draw[draw=\tikzumldrawcall, fill=\tikzumlfillcall] (st-\tikzumlCall@nodeName.north west) rectangle (sb-\tikzumlCall@nodeName.south east);% + \else% + % draw root activity from inner call + \ifthenelse{\equal{\tikzumlCallStartNodeName}{\tikzumlCallEndNodeNameold}}{}{% + \draw[draw=\tikzumldrawcall, fill=\tikzumlfillcall] (st-\tikzumlCall@nodeName.north west) rectangle (sb-\tikzumlCall@nodeName.south east);% + }% + \fi% + }{% + % draw root activity period + \ifnum\thetikzumlCallLevel=0% + \draw[draw=\tikzumldrawcall, fill=\tikzumlfillcall] (st-\tikzumlCall@nodeName.north west) rectangle (sb-\tikzumlCall@nodeName.south east);% + \else% + % draw root activity from inner call + \ifthenelse{\equal{\tikzumlCallStartNodeName}{\tikzumlCallEndNodeNameold}}{}{% + \draw[draw=\tikzumldrawcall, fill=\tikzumlfillcall] (st-\tikzumlCall@nodeName.north west) rectangle (sb-\tikzumlCall@nodeName.south east);% + }% + \fi% + % draw receiver activity period + \draw[draw=\tikzumldrawcall, fill=\tikzumlfillcall] (et-\tikzumlCall@nodeName.north west) rectangle (eb-\tikzumlCall@nodeName.south east);% + }% + \end{pgfonlayer}% + \ifthenelse{\equal{\tikzumlCallDefaultFillColor}{\tikzumlCallFillColor}}{}{% + \fill[\tikzumlfillcall] (st-\tikzumlCall@nodeName.north west) rectangle (sb-\tikzumlCall@nodeName.south east);% + \draw[\tikzumldrawcall] (st-\tikzumlCall@nodeName.north west) rectangle (sb-\tikzumlCall@nodeName.south west) (st-\tikzumlCall@nodeName.north east) rectangle (sb-\tikzumlCall@nodeName.south east);% + }% + % + % update last nodes drawn on sender and receiver lifelines + \expandafter\xdef\csname tikzumlLastChild@\tikzumlCallEndNodeName \endcsname{eb-\tikzumlCall@nodeName}% + \expandafter\xdef\csname tikzumlLastChild@\tikzumlCallStartNodeName \endcsname{sb-\tikzumlCall@nodeName}% + % + % draw call arrows + \begin{pgfonlayer}{connections}% + \ifstrequal{\tikzumlCallStartNodeName}{\tikzumlCallEndNodeName}{% + \draw[\tikzumlcallstyle, \tikzumldrawcall] (st-\tikzumlCall@nodeName.east) -- ++(2.5*\tikzumlCallPadding ex,0) % + -- ++(0,-0.75*\tikzumlCallPadding ex) % + node[font=\tikzumlDefaultFont, text=\tikzumltextcall, midway, right, name=\tikzumlCall@nodeName-op] {\tikzumlCallOp} % + -- (et-\tikzumlCall@nodeName.east);% + % + % draw return arrow and update fit for parent fragment + \ifthenelse{\equal{\tikzumltypecall}{synchron}}{% + \ifthenelse{\NOT\equal{\tikzumlCallReturn}{}\OR\equal{\tikzumlCallWithReturn}{tikzumlTrue}}{% + \ifnum\c@tikzumlFragmentLevel>0% + \edef\tikzumlFragmentFitOld{\csname tikzumlFragmentFit\tikzumlFragment@name \endcsname}% + \expandafter\xdef\csname tikzumlFragmentFit\tikzumlFragment@name \endcsname{\tikzumlFragmentFitOld (st-\tikzumlCall@nodeName) (et-\tikzumlCall@nodeName) (eb-\tikzumlCall@nodeName) (sb-\tikzumlCall@nodeName) (\tikzumlCall@nodeName-op) (\tikzumlCall@nodeName-return)}% + \fi% + % + \draw[tikzuml call return style, \tikzumldrawcall] (eb-\tikzumlCall@nodeName.east) -- ++(2.5*\tikzumlCallPadding ex,0) + -- ++(0,-0.75*\tikzumlCallPadding ex) % + node[font=\tikzumlDefaultFont, text=\tikzumltextcall, midway, right, name=\tikzumlCall@nodeName-return] {\tikzumlCallReturn} % + -- (sb-\tikzumlCall@nodeName.east);% + }{% + \ifnum\c@tikzumlFragmentLevel>0% + \edef\tikzumlFragmentFitOld{\csname tikzumlFragmentFit\tikzumlFragment@name \endcsname}% + \expandafter\xdef\csname tikzumlFragmentFit\tikzumlFragment@name \endcsname{\tikzumlFragmentFitOld (st-\tikzumlCall@nodeName) (et-\tikzumlCall@nodeName) (eb-\tikzumlCall@nodeName) (sb-\tikzumlCall@nodeName) (\tikzumlCall@nodeName-op)}% + \fi% + }% + }{% + \ifnum\c@tikzumlFragmentLevel>0% + \edef\tikzumlFragmentFitOld{\csname tikzumlFragmentFit\tikzumlFragment@name \endcsname}% + \expandafter\xdef\csname tikzumlFragmentFit\tikzumlFragment@name \endcsname{\tikzumlFragmentFitOld (st-\tikzumlCall@nodeName) (et-\tikzumlCall@nodeName) (eb-\tikzumlCall@nodeName) (sb-\tikzumlCall@nodeName) (\tikzumlCall@nodeName-op)}% + \fi% + }% + }{% + % draw call arrows + \pgfextractx{\tikzumlCall@xa}{\pgfpointanchor{\tikzumlCallStartNodeName}{center}}% + \pgfextractx{\tikzumlCall@xb}{\pgfpointanchor{\tikzumlCallEndNodeName}{center}}% + % + \ifthenelse{\tikzumlCall@xb>\tikzumlCall@xa}{% + \draw[\tikzumlcallstyle, \tikzumldrawcall] (st-\tikzumlCall@nodeName.east) -- (et-\tikzumlCall@nodeName.west) % + node[font=\tikzumlDefaultFont, text=\tikzumltextcall, midway, above=-0.4ex, name=\tikzumlCall@nodeName-op] {\tikzumlCallOp};% + }{% + \draw[\tikzumlcallstyle, \tikzumldrawcall] (st-\tikzumlCall@nodeName.west) -- (et-\tikzumlCall@nodeName.east) % + node[font=\tikzumlDefaultFont, text=\tikzumltextcall, midway, above=-0.4ex, name=\tikzumlCall@nodeName-op] {\tikzumlCallOp};% + }% + % + % draw return arrow and update fit for parent fragment + \ifthenelse{\equal{\tikzumltypecall}{synchron}}{% + \ifthenelse{\NOT\equal{\tikzumlCallReturn}{}\OR\equal{\tikzumlCallWithReturn}{tikzumlTrue}}{% + \ifnum\c@tikzumlFragmentLevel>0% + \edef\tikzumlFragmentFitOld{\csname tikzumlFragmentFit\tikzumlFragment@name \endcsname}% + \expandafter\xdef\csname tikzumlFragmentFit\tikzumlFragment@name \endcsname{\tikzumlFragmentFitOld (st-\tikzumlCall@nodeName) (et-\tikzumlCall@nodeName) (eb-\tikzumlCall@nodeName) (sb-\tikzumlCall@nodeName) (\tikzumlCall@nodeName-op) (\tikzumlCall@nodeName-return)}% + \fi% + % + \ifthenelse{\tikzumlCall@xb>\tikzumlCall@xa}{% + \draw[tikzuml call return style, \tikzumldrawcall] (eb-\tikzumlCall@nodeName.west) -- (sb-\tikzumlCall@nodeName.east) % + node[font=\tikzumlDefaultFont, text=\tikzumltextcall, midway, above=-0.4ex, name=\tikzumlCall@nodeName-return] {\tikzumlCallReturn};% + }{% + \draw[tikzuml call return style, \tikzumldrawcall] (eb-\tikzumlCall@nodeName.east) -- (sb-\tikzumlCall@nodeName.west) % + node[font=\tikzumlDefaultFont, text=\tikzumltextcall, midway, above=-0.4ex, name=\tikzumlCall@nodeName-return] {\tikzumlCallReturn};% + }% + }{% + \ifnum\c@tikzumlFragmentLevel>0% + \edef\tikzumlFragmentFitOld{\csname tikzumlFragmentFit\tikzumlFragment@name \endcsname}% + \expandafter\xdef\csname tikzumlFragmentFit\tikzumlFragment@name \endcsname{\tikzumlFragmentFitOld (st-\tikzumlCall@nodeName) (et-\tikzumlCall@nodeName) (eb-\tikzumlCall@nodeName) (sb-\tikzumlCall@nodeName) (\tikzumlCall@nodeName-op)}% + \fi% + }% + }{% + \ifnum\c@tikzumlFragmentLevel>0% + \edef\tikzumlFragmentFitOld{\csname tikzumlFragmentFit\tikzumlFragment@name \endcsname}% + \expandafter\xdef\csname tikzumlFragmentFit\tikzumlFragment@name \endcsname{\tikzumlFragmentFitOld (st-\tikzumlCall@nodeName) (et-\tikzumlCall@nodeName) (eb-\tikzumlCall@nodeName) (sb-\tikzumlCall@nodeName) (\tikzumlCall@nodeName-op)}% + \fi% + }% + }% + \end{pgfonlayer}% +}% +% +% alias for function self call +\newenvironment{umlcallself}[2][]{\begin{umlcall}[#1]{#2}{#2} }{\end{umlcall}}% +% +% define a combined fragment +% optional : name: name of fragment +% type: type of fragment (opt, alt, break, loop, par, critical, ignore, consider, assert, neg, weak, strict, ref) +% label: label of fragment (ex : condition for opt, iterator for loop, ...) +% inner xsep, inner ysep: padding of the fragment box +% draw, fill, text: colors +\newenvironment{umlfragment}[1][]{% + % define a fragment separator + % optional : label of the fragment part (ex : else for alt) + \providecommand{\umlfpart}[1][]{% + \stepcounter{tikzumlFragmentPartNum}% + % + \node[outer sep=0, inner xsep=\tikzumlFragmentXSep ex, inner ysep=\tikzumlFragmentYSep ex, fit=\csname tikzumlFragmentFit\tikzumlFragment@name \endcsname, name=\tikzumlFragment@name-Part-tmp] {};% + \node[anchor=east, name=\tikzumlFragment@name-PartType-\thetikzumlFragmentPartNum] at (\tikzumlFragment@name-Part-tmp.north west |- \tikzumlCallBottomSrc) {\phantom{\tikzumlFragmentType}};% + \draw (\tikzumlFragment@name-PartType-\thetikzumlFragmentPartNum.north west |- \tikzumlCallBottomSrc)+(0,-0.4*\tikzumlFragment@paddingy ex) node[name=\tikzumlFragment@name-PartWest-\thetikzumlFragmentPartNum] {};% + \draw (\tikzumlFragment@name-Part-tmp.north east |- \tikzumlCallBottomSrc)+(0,-0.4*\tikzumlFragment@paddingy ex) node[name=\tikzumlFragment@name-PartEast-\thetikzumlFragmentPartNum] {};% + \draw[dashed] (\tikzumlFragment@name-PartWest-\thetikzumlFragmentPartNum) -- (\tikzumlFragment@name-PartEast-\thetikzumlFragmentPartNum); + \draw (\tikzumlFragment@name-PartType-\thetikzumlFragmentPartNum)+(0,-0.4*\tikzumlFragment@paddingy ex) node[name=tikzumlTmpNode] {\phantom{\tikzumlFragmentType}};% + \node[anchor=north west] at (tikzumlTmpNode.south west) {[##1]};% + }% + % + \stepcounter{tikzumlFragmentNum}% + % + \pgfkeys{/tikzuml/fragment/.cd, name/.initial=fragment@\alph{tikzumlFragmentNum}, type/.initial=\tikzumlFragmentDefaultType,% + label/.initial=tikzumlEmpty,% + inner xsep/.initial=\tikzumlFragmentDefaultXSep, inner ysep/.initial=\tikzumlFragmentDefaultYSep,% + draw/.initial=\tikzumlDefaultDrawColor, fill/.initial=\tikzumlFragmentDefaultFillColor,% + text/.initial=\tikzumlDefaultTextColor,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \errmessage{TIKZUML ERROR : in umlfragment, invalid option \keyname}% + }% + }% + \pgfkeys{/tikzuml/fragment/.cd, #1}% + \pgfkeys{/tikzuml/fragment/.cd, name/.get=\tikzumlFragmentName, type/.get=\tikzumlFragmentType,% + label/.get=\tikzumlFragmentLabel,% + inner xsep/.get=\tikzumlFragmentXSep, inner ysep/.get=\tikzumlFragmentYSep,% + draw/.get=\tikzumlFragmentDrawColor, fill/.get=\tikzumlFragmentFillColor,% + text/.get=\tikzumlFragmentTextColor% + }% + % + \ifthenelse{\equal{\tikzumlFragmentLabel}{tikzumlEmpty}}{% + \def\tikzumlFragmentLabel{}% + }{% + \let\tikzumlFragmentLabelold\tikzumlFragmentLabel% + \def\tikzumlFragmentLabel{[\tikzumlFragmentLabelold]}% + }% + % + \ifnum\thetikzumlFragmentLevel>0% + \let\tikzumlFragment@parentold\tikzumlFragment@parent% + \let\tikzumlFragment@nameold\tikzumlFragment@name% + \edef\tikzumlFragment@parent{\tikzumlFragment@nameold}% + \else% + \setcounter{tikzumlFragmentPartNum}{0}% + \edef\tikzumlFragment@parent{}% + \edef\tikzumlFragment@parentold{}% + \edef\tikzumlFragment@nameold{}% + \fi% + % + \edef\tikzumlFragment@name{\tikzumlFragmentName}% + \expandafter\gdef\csname tikzumlFragmentFit\tikzumlFragment@name \endcsname{}% + % + \stepcounter{tikzumlFragmentLevel}% + % + \ifnum\thetikzumlCallLevel>0% + \stepcounter{tikzumlCallStartFragmentNum}% + \fi% + % + \pgfmathparse{6*\tikzumlFragmentYSep}% + \xdef\tikzumlFragment@paddingy{\pgfmathresult}% + \if\c@tikzumlFragmentLevelNum=0% + \setcounter{tikzumlFragmentLevelNum}{\thetikzumlFragmentLevel}% + \fi% + % + % time delay adjustment for two consecutive fragments + \ifnum\thetikzumlCallEndFragmentNum>0% + \addtocounter{tikzumlCallEndFragmentNum}{-1} + \fi% +}{% + % + \addtocounter{tikzumlFragmentLevel}{-1}% + % + \ifnum\thetikzumlFragmentLevel>0% + \edef\tikzumlFragmentFitOld{\csname tikzumlFragmentFit\tikzumlFragment@parent \endcsname}% + \expandafter\xdef\csname tikzumlFragmentFit\tikzumlFragment@parent \endcsname{\tikzumlFragmentFitOld (\tikzumlFragment@name)}% + \fi% + % + % draw working fragment box + \begin{pgfonlayer}{fragment\thetikzumlFragmentLevel}% + \node[outer sep=0, inner xsep=\tikzumlFragmentXSep ex, inner ysep=\tikzumlFragmentYSep ex, fit=\csname tikzumlFragmentFit\tikzumlFragment@name \endcsname, name=\tikzumlFragment@name-back] {};% + \end{pgfonlayer}% + % + % draw type and label + \node[text=\tikzumlFragmentTextColor, font=\tikzumlDefaultFont, anchor=north east, name=\tikzumlFragment@name-type] % + at (\tikzumlFragment@name-back.north west) {\tikzumlFragmentType};% + \node[text=\tikzumlFragmentTextColor, font=\tikzumlDefaultFont, anchor=north west, name=\tikzumlFragment@name-label] % + at (\tikzumlFragment@name-type.south west) {\tikzumlFragmentLabel};% + % + % draw final fragment box + \begin{pgfonlayer}{fragment\thetikzumlFragmentLevel}% + \node[draw=\tikzumlFragmentDrawColor, fill=\tikzumlFragmentFillColor, outer sep=0, inner sep=0, font=\tikzumlDefaultFont, fit=(\tikzumlFragment@name-back) (\tikzumlFragment@name-type) (\tikzumlFragment@name-label), name=\tikzumlFragment@name] {};% + \end{pgfonlayer}% + % + \draw[draw=\tikzumlFragmentDrawColor] (\tikzumlFragment@name.north west) rectangle (\tikzumlFragment@name.south east);% + \draw (\tikzumlFragment@name-type.south east)+(0,1ex) node[name=\tikzumlFragment@name-typetop, inner sep=0] {};% + \draw (\tikzumlFragment@name-type.south east)+(-1ex,0) node[name=\tikzumlFragment@name-typeleft, inner sep=0] {};% + \draw (\tikzumlFragment@name.north west) -| (\tikzumlFragment@name-typetop.center) -- (\tikzumlFragment@name-typeleft.center) -| (\tikzumlFragment@name.north west);% + % + \ifnum\thetikzumlCallLevel>0% + \stepcounter{tikzumlCallEndFragmentNum}% + \fi% +}% +% +% define a constructor call +% arg : call sender +% name of constructed object +% optional : x: coordinate of the new object +% stereo: stereotype of the new object +% class: class type of the new object +% dt: time delay from last event +% name: name of the call +% draw, fill, text: colors +% no ddots: when used, disable printing of double dots +\newcommand{\umlcreatecall}[3][]{% + \stepcounter{tikzumlCallNum}% + \edef\tikzumlCall@lastchildNum{\thetikzumlCallNum}% for testing presence of sub-calls + \gdef\tikzumlInCreateCall{1}% + \pgfkeys{/tikzuml/createcall/.cd, x/.initial=tikzumlEmpty, stereo/.initial=\tikzumlObjectDefaultStereo, class/.initial={},% + dt/.initial=\tikzumlCreateCallDefaultDT, name/.initial=call-\thetikzumlCallNum,% + draw/.initial=\tikzumlDefaultDrawColor, fill/.initial=\tikzumlCallDefaultFillColor,% + text/.initial=\tikzumlDefaultTextColor,% + draw obj/.initial=\tikzumlDefaultDrawColor, fill obj/.initial=\tikzumlObjectDefaultFillColor,% + text obj/.initial=\tikzumlDefaultTextColor,% + no ddots/.is if=tikzumlcreatecallNoDDots,% + no ddots=false,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \errmessage{TIKZUML ERROR : in umlcreatecall, invalid option \keyname}% + }% + }% + \pgfkeys{/tikzuml/createcall/.cd, #1}% + \pgfkeys{/tikzuml/createcall/.cd, x/.get=\tikzumlCreateCallX, stereo/.get=\tikzumlCreateCallStereo,% + class/.get=\tikzumlCreateCallClass,% + dt/.get=\tikzumlCreateCallDT, name/.get=\tikzumlCreateCallName,% + draw/.get=\tikzumlCreateCallDrawColor, fill/.get=\tikzumlCreateCallFillColor,% + text/.get=\tikzumlCreateCallTextColor,% + draw obj/.get=\tikzumlCreateCallObjectDrawColor, fill obj/.get=\tikzumlCreateCallObjectFillColor,% + text obj/.get=\tikzumlCreateCallObjectTextColor% + }% + % + \def\tikzumlCreateCallSrc@name{#2}% + % + \begingroup% + \def\_{_}\edef\x{\endgroup% + \def\noexpand\tikzumlCreateCallSrc@nodeName{\tikzumlCreateCallSrc@name}}\x% + % + % managing time delays from previous/parent fragments + \ifnum\thetikzumlCallStartFragmentNum>0% + \let\tikzumlCreateCallDTold\tikzumlCreateCallDT% + \pgfmathparse{0.5*\tikzumlFragment@paddingy+\tikzumlCreateCallDTold}% + \edef\tikzumlCreateCallDT{\pgfmathresult}% + \addtocounter{tikzumlCallStartFragmentNum}{-1} + \fi% + \ifnum\thetikzumlCallEndFragmentNum>0% + \let\tikzumlCreateCallDTold\tikzumlCreateCallDT% + \pgfmathparse{0.5*\tikzumlFragment@paddingy+\tikzumlCreateCallDTold}% + \edef\tikzumlCreateCallDT{\pgfmathresult}% + \addtocounter{tikzumlCallEndFragmentNum}{-1} + \fi% + \ifnum\thetikzumlFragmentPartNum>0% + \let\tikzumlCreateCallDTold\tikzumlCreateCallDT% + \pgfmathparse{0.5*\tikzumlFragment@paddingy+\tikzumlCreateCallDTold}% + \edef\tikzumlCreateCallDT{\pgfmathresult}% + \fi% + % + % managing parent-child structure + \ifnum\thetikzumlCallLevel>0% + \let\tikzumlCall@nameold\tikzumlCall@name% + \def\tikzumlCall@name{\tikzumlCreateCallName}% + \let\tikzumlCall@parentold\tikzumlCall@parent% + \edef\tikzumlCall@parent{\tikzumlCall@parentold @@\tikzumlCall@nameold}% + \else% + \edef\tikzumlCall@parent{}% + \edef\tikzumlCall@parentold{}% + \edef\tikzumlCall@nameold{} + \edef\tikzumlCall@name{\tikzumlCreateCallName}% + \fi% + % + \begingroup% + \def\_{_}\edef\x{\endgroup% + \def\noexpand\tikzumlCreateCall@nodeName{\tikzumlCall@name}}\x% + % + \draw (\csname tikzumlLastChild@\tikzumlCreateCallSrc@nodeName \endcsname)+(0,-\tikzumlCreateCallDT ex) node[name=st-\tikzumlCreateCall@nodeName, tikzuml activity style] {};% + % + \xdef\tikzumlCreateCallObjectSrc{st-\tikzumlCreateCall@nodeName}% + % + \iftikzumlcreatecallNoDDots% + \umlobject[x=\tikzumlCreateCallX, stereo=\tikzumlCreateCallStereo, class=\tikzumlCreateCallClass, draw=\tikzumlCreateCallObjectDrawColor, fill=\tikzumlCreateCallObjectFillColor, text=\tikzumlCreateCallObjectTextColor, no ddots]{#3}% + \else + \umlobject[x=\tikzumlCreateCallX, stereo=\tikzumlCreateCallStereo, class=\tikzumlCreateCallClass, draw=\tikzumlCreateCallObjectDrawColor, fill=\tikzumlCreateCallObjectFillColor, text=\tikzumlCreateCallObjectTextColor]{#3}% + \fi + % + \draw (\csname tikzumlLastChild@\tikzumlCreateCallSrc@nodeName \endcsname |- #3)+(0,-0.5*\tikzumlCreateCallDT ex) node[name=sb-\tikzumlCreateCall@nodeName, tikzuml activity style] {};% + % + \expandafter\xdef\csname tikzumlLastChild@\tikzumlCreateCallSrc@nodeName \endcsname{sb-\tikzumlCreateCall@nodeName}% + \xdef\tikzumlCallBottomSrc{sb-\tikzumlCreateCall@nodeName}% + % + \begin{pgfonlayer}{connections}% + \draw[tikzuml synchron-msg style, \tikzumlCreateCallDrawColor] (st-\tikzumlCreateCall@nodeName) -- (#3) node[midway, above, font=\tikzumlDefaultFont, text=\tikzumlCreateCallTextColor, name=\tikzumlCreateCall@nodeName-op] {create};% + \end{pgfonlayer}% + % + \ifnum\thetikzumlCallLevel=0% + \begin{pgfonlayer}{activity}% + \draw[draw=\tikzumlCreateCallDrawColor, fill=\tikzumlCreateCallFillColor] (st-\tikzumlCreateCall@nodeName.north west) rectangle (sb-\tikzumlCreateCall@nodeName.south east);% + \end{pgfonlayer}% + \fi% + % add to fit fragment + \ifnum\c@tikzumlFragmentLevel>0% + \edef\tikzumlFragmentFitOld{\csname tikzumlFragmentFit\tikzumlFragment@name \endcsname}% + \expandafter\xdef\csname tikzumlFragmentFit\tikzumlFragment@name \endcsname{\tikzumlFragmentFitOld (st-\tikzumlCreateCall@nodeName) (sb-\tikzumlCreateCall@nodeName) (\tikzumlCreateCall@nodeName-op) (#3) }% + \fi% +} +% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% component diagrams % +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +\tikzstyle{tikzuml connector style}=[color=\tikzumlDefaultDrawColor, -]% +% +\newcounter{tikzumlComponentLevel}% +\newcounter{tikzumlComponentSubComponentNum}% +\newcounter{tikzumlConnectorNum}% +\setcounter{tikzumlConnectorNum}{1}% +% +\newif\iftikzumlcomponentWithoutCoords% +% +\newcommand{\picturedcomponent}[1]{% + \pgfkeys{/tikzuml/component/picture/.cd, scale/.initial=1, .unknown/.code={}}% + \pgfkeys{/tikzuml/component/picture/.cd,#1}% + \pgfkeys{/tikzuml/component/picture/.cd, scale/.get=\tikzumlComponentScale}% + \begin{tikzpicture}[#1]% + \filldraw (0,0) rectangle (1ex,1.5ex);% + \filldraw (-0.2ex,0.4ex) rectangle (0.2ex,0.6ex);% + \filldraw (-0.2ex,0.9ex) rectangle (0.2ex,1.1ex);% + \end{tikzpicture}% +}% +% +% define a uml component +% args : name of the component +% content of the component +% optional args : x,y coordinates of the component +% width of the component node +\newenvironment{umlcomponent}[2][]{% + \ifnum\thetikzumlComponentLevel>0% + \let\tikzumlComponent@nameold\tikzumlComponent@fitname% + \let\tikzumlComponent@parentold\tikzumlComponent@parent% + \edef\tikzumlComponent@parent{\tikzumlComponent@parentold @@\tikzumlComponent@nameold}% + \else% + \def\tikzumlComponent@parent{}% + \fi% + % + \stepcounter{tikzumlComponentLevel}% + % + \pgfkeys{/tikzuml/component/.cd, x/.initial=\tikzumlDefaultX, y/.initial=\tikzumlDefaultX, width/.initial=\tikzumlComponentDefaultWidth, name/.initial={},% + draw/.initial=\tikzumlDefaultDrawColor, fill/.initial=\tikzumlComponentDefaultFillColor,% + text/.initial=\tikzumlDefaultTextColor, style/.style={},% + no coords/.is if=tikzumlcomponentWithoutCoords,% + no coords=false,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{above}\OR% + \equal{\keyname}{left}\OR% + \equal{\keyname}{below}\OR% + \equal{\keyname}{right}\OR% + \equal{\keyname}{above left}\OR% + \equal{\keyname}{above right}\OR% + \equal{\keyname}{below left}\OR% + \equal{\keyname}{below right}}{% + \IfSubStr{\keyvalue}{ of }{% + \pgfkeys{/tikzuml/component/.cd, no coords}% + }{}% + }{}% + \ifx\keyvalue\pgfkeysnovalue% + \pgfkeys{/tikzuml/component/.cd, style/.append style/.expand once={\keyname}}% + \else% + \pgfkeys{/tikzuml/component/.cd, style/.append style/.expand twice={\expandafter\keyname\expandafter=\keyvalue}}% + \fi% + %\errmessage{TIKZUML ERROR : in umlcomponent, invalid option \keyname}% + }% + }% + \pgfkeys{/tikzuml/component/.cd, #1}% + \pgfkeys{/tikzuml/component/.cd, x/.get=\tikzumlComponentXShift, y/.get=\tikzumlComponentYShift,% + width/.get=\tikzumlComponentMinimumWidth, name/.get=\tikzumlComponentName,% + draw/.get=\tikzumlComponentDrawColor, fill/.get=\tikzumlComponentFillColor,% + text/.get=\tikzumlComponentTextColor% + }% + % + \ifthenelse{\equal{\tikzumlComponentName}{}}{% + \edef\tikzumlComponent@name{#2}% + }{% + \edef\tikzumlComponent@name{\tikzumlComponentName}% + }% + % + \begingroup% + \def\_{@}\edef\x{\endgroup% + \def\noexpand\tikzumlComponent@fitname{\tikzumlComponent@name}}\x% + % + \let\tikzumlComponent@nodeNameold\tikzumlComponent@nodeName% + \def\tikzumlComponent@caption{#2}% + % + \begingroup% + \def\_{_}\edef\x{\endgroup% + \def\noexpand\tikzumlComponent@nodeName{\tikzumlComponent@name}}\x% + % + \expandafter\gdef\csname tikzumlComponentFit\tikzumlComponent@parent @@\tikzumlComponent@fitname\endcsname{}% + % + \setcounter{tikzumlComponentSubComponentNum}{0}% + % + \begin{scope}[xshift=\tikzumlComponentXShift cm, yshift=\tikzumlComponentYShift cm]% +}{% + \addtocounter{tikzumlComponentLevel}{-1}% + \begin{pgfonlayer}{component\thetikzumlComponentLevel}% + % + % if contains nothing, one define a fictive node to enable the fit option + \ifnum\c@tikzumlComponentSubComponentNum=0% + \iftikzumlcomponentWithoutCoords% + \node[inner ysep=0.5ex, minimum width=\tikzumlComponentMinimumWidth, font=\tikzumlDefaultFont, /tikzuml/component/style] (\tikzumlComponent@nodeName-root) {\phantom{\tikzumlComponent@nodeName}};% + \else% + \node[inner ysep=0.5ex, minimum width=\tikzumlComponentMinimumWidth, font=\tikzumlDefaultFont, /tikzuml/component/style] (\tikzumlComponent@nodeName-root) at (0,0) {\phantom{\tikzumlComponent@nodeName}};% + \fi% + \expandafter\xdef\csname tikzumlComponentFit\tikzumlComponent@parent @@\tikzumlComponent@fitname\endcsname{(\tikzumlComponent@nodeName-root)}% + \fi% + % + \ifnum\c@tikzumlComponentLevel>0% + \def\tikzumlComponentFitTmp{\csname tikzumlComponentFit\tikzumlComponent@parent\endcsname}% + \expandafter\xdef\csname tikzumlComponentFit\tikzumlComponent@parent\endcsname{\tikzumlComponentFitTmp (\tikzumlComponent@nodeName-body) (\tikzumlComponent@nodeName-caption)}% + \stepcounter{tikzumlComponentSubComponentNum}% + \fi% + % + \node[inner sep=2ex, font=\tikzumlDefaultFont, fit = \csname tikzumlComponentFit\tikzumlComponent@parent @@\tikzumlComponent@fitname\endcsname] (\tikzumlComponent@nodeName-body) {};% + \node[above, font=\tikzumlDefaultFont] (\tikzumlComponent@nodeName-captiontmp) at (\tikzumlComponent@nodeName-body.north) {\tikzumlComponent@caption};% + \node (\tikzumlComponent@nodeName-logotmp) at (\tikzumlComponent@nodeName-captiontmp.north -| \tikzumlComponent@nodeName-body.east) {\picturedcomponent{draw=\tikzumlComponentDrawColor, fill=\tikzumlComponentFillColor, font=\tikzumlDefaultFont} };% + \node[draw=\tikzumlComponentDrawColor, fill=\tikzumlComponentFillColor, name=\tikzumlComponent@nodeName, /tikzuml/component/style, fit=(\tikzumlComponent@nodeName-body) (\tikzumlComponent@nodeName-captiontmp)] {};% + \node[font=\tikzumlDefaultFont] (\tikzumlComponent@nodeName-caption) at (\tikzumlComponent@nodeName-captiontmp) {\tikzumlComponent@caption};% + \draw (\tikzumlComponent@nodeName-caption.north -| \tikzumlComponent@nodeName.east) node[font=\tikzumlDefaultFont, xshift=-1ex, below=-1ex, name=\tikzumlComponent@nodeName-logo] {\picturedcomponent{draw=\tikzumlComponentDrawColor, fill=\tikzumlComponentFillColor, font=\tikzumlDefaultFont} };% + \draw (\tikzumlComponent@nodeName-caption.south -| \tikzumlComponent@nodeName.north west) -- (\tikzumlComponent@nodeName-caption.south -| \tikzumlComponent@nodeName.north east);% + \coordinate (\tikzumlComponent@nodeName-west-port) at (\tikzumlComponent@nodeName.west); + \coordinate (\tikzumlComponent@nodeName-east-port) at (\tikzumlComponent@nodeName.east); + \coordinate (\tikzumlComponent@nodeName-south-port) at (\tikzumlComponent@nodeName.south); + \coordinate (\tikzumlComponent@nodeName-north-port) at (\tikzumlComponent@nodeName.north); + \end{pgfonlayer}% + \end{scope}% + % + % add to fit + \ifnum\c@tikzumlPackageLevel>0% + \edef\tikzumlPackageFitOld{\csname tikzumlPackageFit\tikzumlPackage@parent @@\tikzumlPackage@fitname\endcsname}% + \expandafter\xdef\csname tikzumlPackageFit\tikzumlPackage@parent @@\tikzumlPackage@fitname\endcsname{\tikzumlPackageFitOld (\tikzumlComponent@nodeName)}% + \stepcounter{tikzumlPackageClassNum}% + \fi% +}% +% +% shortcut for empty component +\newcommand{\umlbasiccomponent}[2][]{\begin{umlcomponent}[#1]{#2} \end{umlcomponent}}% +% +\newcommand{\umlrequiredinterface}[2][]{% + \def\tikzumlInterfaceWithPort{tikzumlFalse}% + \pgfkeys{/tikzuml/requiredinterfacerelation/.cd, interface/.initial={}, distance/.initial=\tikzumlRequiredInterfaceDefaultDistance,% + name/.initial=tikzumlEmpty, width/.initial=\tikzumlRequiredInterfaceDefaultWidth,% + padding/.initial=\tikzumlRequiredInterfaceDefaultPadding,% + draw/.initial=\tikzumlDefaultDrawColor, fill/.initial=\tikzumlComponentDefaultFillColor,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{with port}}{% + \def\tikzumlInterfaceWithPort{tikzumlTrue}% + }{}% + }% + }% + \pgfkeys{/tikzuml/requiredinterfacerelation/.cd, #1}% + \pgfkeys{/tikzuml/requiredinterfacerelation/.cd, interface/.get=\tikzumlRequiredInterfaceLabel,% + distance/.get=\tikzumlRequiredInterfaceDistance,% + name/.get=\tikzumlRequiredInterfaceName,% + width/.get=\tikzumlRequiredInterfaceWidth,% + padding/.get=\tikzumlRequiredInterfacePadding,% + draw/.get=\tikzumlRequiredInterfaceDrawColor,% + fill/.get=\tikzumlRequiredInterfaceFillColor% + }% + % + \ifthenelse{\equal{\tikzumlRequiredInterfaceName}{tikzumlEmpty}}{% + \edef\tikzumlRequiredInterface@interfacename{#2-east-interface}% + \edef\tikzumlRequiredInterface@portname{#2-east-port}% + \edef\tikzumlRequiredInterface@paddingname{#2-east-padding}% + }{% + \edef\tikzumlRequiredInterface@interfacename{\tikzumlRequiredInterfaceName}% + \edef\tikzumlRequiredInterface@portname{\tikzumlRequiredInterfaceName-port}% + \edef\tikzumlRequiredInterface@paddingname{\tikzumlRequiredInterfaceName-padding}% + }% + % + \edef\tikzumlRequiredInterface@name{#2}% + % + \begingroup% + \def\_{_}\edef\x{\endgroup% + \def\noexpand\tikzumlRequiredInterface@nodeName{\tikzumlRequiredInterface@name}}\x% + % + \begingroup% + \def\_{_}\edef\x{\endgroup% + \def\noexpand\tikzumlRequiredInterface@interfacenodeName{\tikzumlRequiredInterface@interfacename}}\x% + % + \begingroup% + \def\_{_}\edef\x{\endgroup% + \def\noexpand\tikzumlRequiredInterface@portnodeName{\tikzumlRequiredInterface@portname}}\x% + % + \begingroup% + \def\_{_}\edef\x{\endgroup% + \def\noexpand\tikzumlRequiredInterface@paddingnodeName{\tikzumlRequiredInterface@paddingname}}\x% + % + \ifthenelse{\equal{\tikzumlInterfaceWithPort}{tikzumlTrue}}{% + \node[inner sep=0.5*\tikzumlRequiredInterfaceWidth, rectangle, draw=\tikzumlRequiredInterfaceDrawColor, fill=\tikzumlRequiredInterfaceFillColor] (\tikzumlRequiredInterface@portnodeName) at (\tikzumlRequiredInterface@nodeName.east) {};% + }{% + \node[inner sep=0] (\tikzumlRequiredInterface@nodeName-east-port) at (\tikzumlRequiredInterface@nodeName.east) {};% + }% + \begin{scope}% + \draw (\tikzumlRequiredInterface@nodeName)+(\tikzumlRequiredInterfaceDistance,0) node[inner sep=0, text width=\tikzumlRequiredInterfaceWidth, circle, name=\tikzumlRequiredInterface@interfacenodeName-tmp] {};% + \clip (\tikzumlRequiredInterface@interfacenodeName-tmp.north) rectangle (\tikzumlRequiredInterface@interfacenodeName-tmp.south -| \tikzumlRequiredInterface@interfacenodeName-tmp.west);% + \node[inner sep=0, text width=\tikzumlRequiredInterfaceWidth, circle, draw=\tikzumlRequiredInterfaceDrawColor] (\tikzumlRequiredInterface@interfacenodeName) at (\tikzumlRequiredInterface@interfacenodeName-tmp) {};% + \end{scope}% + \node[above] at (\tikzumlRequiredInterface@interfacenodeName.north) {\tikzumlRequiredInterfaceLabel};% + % + \umlrelation[style={tikzuml connector style}, #1]{\tikzumlRequiredInterface@portnodeName}{\tikzumlRequiredInterface@interfacenodeName}% + % + \draw (\tikzumlRequiredInterface@interfacenodeName)+(\tikzumlRequiredInterfacePadding,0) node[name=\tikzumlRequiredInterface@paddingnodeName] {};% + % + % add to fit + \ifnum\c@tikzumlComponentLevel>0% + \def\tikzumlComponentFitTmp{\csname tikzumlComponentFit\tikzumlComponent@parent @@\tikzumlComponent@fitname\endcsname}% + \expandafter\xdef\csname tikzumlComponentFit\tikzumlComponent@parent @@\tikzumlComponent@fitname\endcsname{\tikzumlComponentFitTmp (\tikzumlRequiredInterface@paddingnodeName) (\tikzumlRequiredInterface@portnodeName) }% + \fi% +}% +% +\newcommand{\umlprovidedinterface}[2][]{% + \def\tikzumlInterfaceWithPort{tikzumlFalse}% + \pgfkeys{/tikzuml/providedinterfacerelation/.cd, interface/.initial={}, distance/.initial=\tikzumlProvidedInterfaceDefaultDistance,% + name/.initial=tikzumlEmpty, width/.initial=\tikzumlProvidedInterfaceDefaultWidth,% + padding/.initial=\tikzumlProvidedInterfaceDefaultPadding,% + draw/.initial=\tikzumlDefaultDrawColor, fill/.initial=\tikzumlComponentDefaultFillColor,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{with port}}{% + \def\tikzumlInterfaceWithPort{tikzumlTrue}% + }{}% + }% + }% + \pgfkeys{/tikzuml/providedinterfacerelation/.cd, #1}% + \pgfkeys{/tikzuml/providedinterfacerelation/.cd, interface/.get=\tikzumlProvidedInterfaceLabel,% + distance/.get=\tikzumlProvidedInterfaceDistance,% + name/.get=\tikzumlProvidedInterfaceName,% + width/.get=\tikzumlProvidedInterfaceWidth,% + padding/.get=\tikzumlProvidedInterfacePadding,% + draw/.get=\tikzumlProvidedInterfaceDrawColor,% + fill/.get=\tikzumlProvidedInterfaceFillColor% + }% + % + \ifthenelse{\equal{\tikzumlProvidedInterfaceName}{tikzumlEmpty}}{% + \edef\tikzumlProvidedInterface@interfacename{#2-west-interface}% + \edef\tikzumlProvidedInterface@portname{#2-west-port}% + \edef\tikzumlProvidedInterface@paddingname{#2-west-padding}% + }{% + \edef\tikzumlProvidedInterface@interfacename{\tikzumlProvidedInterfaceName}% + \edef\tikzumlProvidedInterface@portname{\tikzumlProvidedInterfaceName-port}% + \edef\tikzumlProvidedInterface@paddingname{\tikzumlProvidedInterfaceName-padding}% + }% + % + \edef\tikzumlProvidedInterface@name{#2}% + % + \begingroup% + \def\_{_}\edef\x{\endgroup% + \def\noexpand\tikzumlProvidedInterface@nodeName{\tikzumlProvidedInterface@name}}\x% + % + \begingroup% + \def\_{_}\edef\x{\endgroup% + \def\noexpand\tikzumlProvidedInterface@interfacenodeName{\tikzumlProvidedInterface@interfacename}}\x% + % + \begingroup% + \def\_{_}\edef\x{\endgroup% + \def\noexpand\tikzumlProvidedInterface@portnodeName{\tikzumlProvidedInterface@portname}}\x% + % + \begingroup% + \def\_{_}\edef\x{\endgroup% + \def\noexpand\tikzumlProvidedInterface@paddingnodeName{\tikzumlProvidedInterface@paddingname}}\x% + % + \ifthenelse{\equal{\tikzumlInterfaceWithPort}{tikzumlTrue}}{% + \node[inner sep=0.5*\tikzumlProvidedInterfaceWidth, rectangle, draw=\tikzumlProvidedInterfaceDrawColor, fill=\tikzumlProvidedInterfaceFillColor] (\tikzumlProvidedInterface@portnodeName) at (\tikzumlProvidedInterface@nodeName.west) {};% + }{% + \node[inner sep=0] (\tikzumlProvidedInterface@portnodeName) at (\tikzumlProvidedInterface@nodeName.west) {};% + }% + \draw (\tikzumlProvidedInterface@nodeName)+(-\tikzumlProvidedInterfaceDistance,0) node[inner sep=0, text width=\tikzumlProvidedInterfaceWidth, circle, draw=\tikzumlProvidedInterfaceDrawColor, fill=\tikzumlProvidedInterfaceFillColor, name=\tikzumlProvidedInterface@interfacenodeName] {};% + \node[above] at (\tikzumlProvidedInterface@interfacenodeName.north) + {\tikzumlProvidedInterfaceLabel};% + % + \umlrelation[style={tikzuml connector style}, #1]{\tikzumlProvidedInterface@portnodeName}{\tikzumlProvidedInterface@interfacenodeName}% + % + \draw (\tikzumlProvidedInterface@interfacenodeName)+(-\tikzumlProvidedInterfacePadding,0) node[name=\tikzumlProvidedInterface@paddingnodeName] {};% + % + % add to fit + \ifnum\thetikzumlComponentLevel>0% + \def\tikzumlComponentFitTmp{\csname tikzumlComponentFit\tikzumlComponent@parent @@\tikzumlComponent@fitname\endcsname}% + \expandafter\xdef\csname tikzumlComponentFit\tikzumlComponent@parent @@\tikzumlComponent@fitname\endcsname{\tikzumlComponentFitTmp (\tikzumlProvidedInterface@paddingnodeName) (\tikzumlProvidedInterface@portnodeName) }% + \fi% +}% +% +\newlength{\tikzuml@AC@xa}% +\newlength{\tikzuml@AC@ya}% +\newlength{\tikzuml@AC@xb}% +\newlength{\tikzuml@AC@yb}% +\newlength{\tikzuml@AC@xi}% +\newlength{\tikzuml@AC@yi}% +\newlength{\tikzuml@AC@xic}% +\newlength{\tikzuml@AC@yic}% +\newlength{\tikzuml@AC@xio}% +\newlength{\tikzuml@AC@yio}% +\newlength{\tikzuml@AC@AB}% +\newlength{\tikzuml@AC@lambda}% +\newlength{\tikzuml@AC@xtrc}% +\newlength{\tikzuml@AC@ytrc}% +\newlength{\tikzuml@AC@xtlc}% +\newlength{\tikzuml@AC@ytlc}% +\newlength{\tikzuml@AC@xblc}% +\newlength{\tikzuml@AC@yblc}% +\newlength{\tikzuml@AC@xbrc}% +\newlength{\tikzuml@AC@ybrc}% +\newlength{\tikzuml@AC@middleArm}% +% +\newcommand{\umlassemblyconnectorsymbol}[2]{% + \ifthenelse{\NOT\equal{\tikzumlAssemblyConnectorLabel}{}}{% + \edef\tikzuml@ACStart@name{#1}% + \edef\tikzuml@ACEnd@name{#2}% + \edef\tikzuml@AC@width{\tikzumlAssemblyConnectorWidth}% + % + \begingroup% + \def\_{_}\edef\x{\endgroup% + \def\noexpand\tikzuml@ACStart@nodeName{\tikzuml@ACStart@name}}\x% + % + \begingroup% + \def\_{_}\edef\x{\endgroup% + \def\noexpand\tikzuml@ACEnd@nodeName{\tikzuml@ACEnd@name}}\x% + % + \begingroup% + \def\_{_}\edef\x{\endgroup% + \def\noexpand\tikzuml@ACInterface@nodeName{\tikzumlAssemblyConnectorSymbolName}}\x% + % + \pgfextractx{\tikzuml@AC@xa}{\pgfpointanchor{\tikzuml@ACStart@nodeName}{\tikzumlAssemblyConnectorStartAnchor}}% + \pgfextracty{\tikzuml@AC@ya}{\pgfpointanchor{\tikzuml@ACStart@nodeName}{\tikzumlAssemblyConnectorStartAnchor}}% + \pgfextractx{\tikzuml@AC@xb}{\pgfpointanchor{\tikzuml@ACEnd@nodeName}{\tikzumlAssemblyConnectorEndAnchor}}% + \pgfextracty{\tikzuml@AC@yb}{\pgfpointanchor{\tikzuml@ACEnd@nodeName}{\tikzumlAssemblyConnectorEndAnchor}}% + \pgfmathsetlength{\tikzuml@AC@xi}{0.5*\tikzuml@AC@xa+0.5*\tikzuml@AC@xb}% + \pgfmathsetlength{\tikzuml@AC@yi}{0.5*\tikzuml@AC@ya+0.5*\tikzuml@AC@yb}% + \pgfmathsetlength{\tikzuml@AC@AB}{veclen(\tikzuml@AC@xa-\tikzuml@AC@xb,\tikzuml@AC@ya-\tikzuml@AC@yb)}% + \pgfmathsetlength{\tikzuml@AC@lambda}{0.25*\tikzuml@AC@width/\tikzuml@AC@AB}% + \pgfmathsetlength{\tikzuml@AC@xic}{\tikzuml@AC@xi-\tikzuml@AC@lambda*(\tikzuml@AC@xb-\tikzuml@AC@xa)}% + \pgfmathsetlength{\tikzuml@AC@yic}{\tikzuml@AC@yi-\tikzuml@AC@lambda*(\tikzuml@AC@yb-\tikzuml@AC@ya)}% + \pgfmathsetlength{\tikzuml@AC@xio}{\tikzuml@AC@xi+\tikzuml@AC@lambda*(\tikzuml@AC@xb-\tikzuml@AC@xa)}% + \pgfmathsetlength{\tikzuml@AC@yio}{\tikzuml@AC@yi+\tikzuml@AC@lambda*(\tikzuml@AC@yb-\tikzuml@AC@ya)}% + \node[inner sep=0.5*\tikzuml@AC@width] (\tikzuml@ACInterface@nodeName-interface) at (\tikzuml@AC@xi,\tikzuml@AC@yi) {};% + \node[inner sep=0, text width=\tikzuml@AC@width, circle, draw=\tikzumlAssemblyConnectorDrawColor, fill=\tikzumlAssemblyConnectorFillColor] (\tikzuml@ACInterface@nodeName-io) at (\tikzuml@AC@xio,\tikzuml@AC@yio) {};% + \begin{scope}% + \pgfmathsetlength{\tikzuml@AC@xtrc}{\tikzuml@AC@xic-2*\tikzuml@AC@lambda*(\tikzuml@AC@yb-\tikzuml@AC@ya)}% + \pgfmathsetlength{\tikzuml@AC@ytrc}{\tikzuml@AC@yic+2*\tikzuml@AC@lambda*(\tikzuml@AC@xb-\tikzuml@AC@xa)}% + \pgfmathsetlength{\tikzuml@AC@xbrc}{\tikzuml@AC@xic+2*\tikzuml@AC@lambda*(\tikzuml@AC@yb-\tikzuml@AC@ya)}% + \pgfmathsetlength{\tikzuml@AC@ybrc}{\tikzuml@AC@yic-2*\tikzuml@AC@lambda*(\tikzuml@AC@xb-\tikzuml@AC@xa)}% + \pgfmathsetlength{\tikzuml@AC@xtlc}{\tikzuml@AC@xic-3*\tikzuml@AC@lambda*(\tikzuml@AC@yb-\tikzuml@AC@ya+\tikzuml@AC@xb-\tikzuml@AC@xa)}% + \pgfmathsetlength{\tikzuml@AC@ytlc}{\tikzuml@AC@yic+3*\tikzuml@AC@lambda*(\tikzuml@AC@xb-\tikzuml@AC@xa+\tikzuml@AC@ya-\tikzuml@AC@yb)}% + \pgfmathsetlength{\tikzuml@AC@xblc}{\tikzuml@AC@xic+3*\tikzuml@AC@lambda*(\tikzuml@AC@yb-\tikzuml@AC@ya+\tikzuml@AC@xa-\tikzuml@AC@xb)}% + \pgfmathsetlength{\tikzuml@AC@yblc}{\tikzuml@AC@yic-3*\tikzuml@AC@lambda*(\tikzuml@AC@xb-\tikzuml@AC@xa+\tikzuml@AC@yb-\tikzuml@AC@ya)}% + \coordinate (\tikzuml@ACInterface@nodeName-trc) at (\tikzuml@AC@xtrc,\tikzuml@AC@ytrc);% + \coordinate (\tikzuml@ACInterface@nodeName-brc) at (\tikzuml@AC@xbrc,\tikzuml@AC@ybrc);% + \coordinate (\tikzuml@ACInterface@nodeName-tlc) at (\tikzuml@AC@xtlc,\tikzuml@AC@ytlc);% + \coordinate (\tikzuml@ACInterface@nodeName-blc) at (\tikzuml@AC@xblc,\tikzuml@AC@yblc);% + \clip (\tikzuml@ACInterface@nodeName-trc) -- (\tikzuml@ACInterface@nodeName-tlc) -- (\tikzuml@ACInterface@nodeName-blc) -- (\tikzuml@ACInterface@nodeName-brc) -- cycle;% + \node[inner sep=0, text width=\tikzuml@AC@width, circle, draw=\tikzumlAssemblyConnectorDrawColor, fill=\tikzumlAssemblyConnectorFillColor] (\tikzuml@ACInterface@nodeName-ic) at (\tikzuml@AC@xic,\tikzuml@AC@yic) {};% + \end{scope}% + \node[above, font=\tikzumlDefaultFont] at (\tikzuml@ACInterface@nodeName-interface.north) + {\tikzumlAssemblyConnectorLabel};% + }{}% +}% +% +\newcommand{\umlassemblyconnector}[3][]{% + \def\tikzumlAssemblyConnectorWithPort{tikzumlFalse}% + \def\tikzumlAssemblyConnectorFirstArm{tikzumlFalse}% + \def\tikzumlAssemblyConnectorSecondArm{tikzumlFalse}% + \def\tikzumlAssemblyConnectorMiddleArm{tikzumlFalse}% + \def\tikzumlAssemblyConnectorLastArm{tikzumlFalse}% + \pgfkeys{/tikzuml/assemblyconnectorrelation/.cd, geometry/.initial=--, interface/.initial={},% + arm1/.initial={auto}, arm2/.initial={auto},% + name/.initial=connector-\thetikzumlConnectorNum, width/.initial=1em,% + anchor1/.initial={}, anchor2/.initial={},% + draw/.initial=\tikzumlDefaultDrawColor,% + fill assembly connector/.initial=\tikzumlAssemblyConnectorDefaultFillColor,% + fill port/.initial=\tikzumlPortDefaultFillColor,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{with port}}{% + \def\tikzumlAssemblyConnectorWithPort{tikzumlTrue}% + }{% + \ifthenelse{\equal{\keyname}{first arm}}{% + \def\tikzumlAssemblyConnectorFirstArm{tikzumlTrue}% + }{% + \ifthenelse{\equal{\keyname}{second arm}}{% + \def\tikzumlAssemblyConnectorSecondArm{tikzumlTrue}% + }{% + \ifthenelse{\equal{\keyname}{middle arm}}{% + \def\tikzumlAssemblyConnectorMiddleArm{tikzumlTrue}% + }{% + \ifthenelse{\equal{\keyname}{last arm}}{% + \def\tikzumlAssemblyConnectorLastArm{tikzumlTrue}% + }{% + }% + }% + }% + }% + }% + }% + }% + \pgfkeys{/tikzuml/assemblyconnectorrelation/.cd, #1}% + \pgfkeys{/tikzuml/assemblyconnectorrelation/.cd, geometry/.get=\tikzumlAssemblyConnectorGeometry,% + name/.get=\tikzumlAssemblyConnectorName,% + interface/.get=\tikzumlAssemblyConnectorLabel,% + width/.get=\tikzumlAssemblyConnectorWidth,% + arm1/.get=\tikzumlAssemblyConnectorStartArm,% + arm2/.get=\tikzumlAssemblyConnectorEndArm,% + anchor1/.get=\tikzumlAssemblyConnectorStartAnchorTmp,% + anchor2/.get=\tikzumlAssemblyConnectorEndAnchorTmp,% + draw/.get=\tikzumlAssemblyConnectorDrawColor,% + fill assembly connector/.get=\tikzumlAssemblyConnectorFillColor,% + fill port/.get=\tikzumlAssemblyConnectorPortFillColor% + }% + % + \edef\tikzumlAssemblyConnectorStart@name{#2}% + \edef\tikzumlAssemblyConnectorEnd@name{#3}% + % + \begingroup% + \def\_{_}\edef\x{\endgroup% + \def\noexpand\tikzumlAssemblyConnectorStart@nodeName{\tikzumlAssemblyConnectorStart@name}}\x% + % + \begingroup% + \def\_{_}\edef\x{\endgroup% + \def\noexpand\tikzumlAssemblyConnectorEnd@nodeName{\tikzumlAssemblyConnectorEnd@name}}\x% + % + \begingroup% + \def\_{_}\edef\x{\endgroup% + \def\noexpand\tikzumlAssemblyConnectorLabel@nodeName{\tikzumlAssemblyConnectorLabel}}\x% + % + \pgfextractx{\tikzuml@AC@xa}{\pgfpointanchor{\tikzumlAssemblyConnectorStart@nodeName}{center}}% + \pgfextracty{\tikzuml@AC@ya}{\pgfpointanchor{\tikzumlAssemblyConnectorStart@nodeName}{center}}% + \pgfextractx{\tikzuml@AC@xb}{\pgfpointanchor{\tikzumlAssemblyConnectorEnd@nodeName}{center}}% + \pgfextracty{\tikzuml@AC@yb}{\pgfpointanchor{\tikzumlAssemblyConnectorEnd@nodeName}{center}}% + % + \ifthenelse{\equal{\tikzumlAssemblyConnectorGeometry}{--}}{% + \ifthenelse{\tikzuml@AC@xb>\tikzuml@AC@xa}{% + \def\tikzumlAssemblyConnectorStartAnchor{east}% + \def\tikzumlAssemblyConnectorEndAnchor{west}% + }{% + \def\tikzumlAssemblyConnectorStartAnchor{west}% + \def\tikzumlAssemblyConnectorEndAnchor{east}% + } + }{}% + % + \ifthenelse{\equal{\tikzumlAssemblyConnectorGeometry}{-|}}{% + \ifthenelse{\tikzuml@AC@xb>\tikzuml@AC@xa}{% + \def\tikzumlAssemblyConnectorStartAnchor{east}% + }{% + \def\tikzumlAssemblyConnectorStartAnchor{west}% + } + \ifthenelse{\tikzuml@AC@yb>\tikzuml@AC@ya}{% + \def\tikzumlAssemblyConnectorEndAnchor{south}% + }{% + \def\tikzumlAssemblyConnectorEndAnchor{north}% + } + }{}% + % + \ifthenelse{\equal{\tikzumlAssemblyConnectorGeometry}{|-}}{% + \ifthenelse{\tikzuml@AC@xb>\tikzuml@AC@xa}{% + \def\tikzumlAssemblyConnectorEndAnchor{west}% + }{% + \def\tikzumlAssemblyConnectorEndAnchor{east}% + } + \ifthenelse{\tikzuml@AC@yb>\tikzuml@AC@ya}{% + \def\tikzumlAssemblyConnectorStartAnchor{north}% + }{% + \def\tikzumlAssemblyConnectorStartAnchor{south}% + } + }{}% + % + \ifthenelse{\equal{\tikzumlAssemblyConnectorGeometry}{-|-}}{% + \ifthenelse{\equal{\tikzumlAssemblyConnectorStartArm}{auto}}{% + \ifthenelse{\equal{\tikzumlAssemblyConnectorEndArm}{auto}}{% + \pgfmathsetlength{\tikzuml@AC@middleArm}{0.5 * \tikzuml@AC@xa + 0.5 * \tikzuml@AC@xb}% + }{% + \pgfmathsetlength{\tikzuml@AC@middleArm}{\tikzuml@AC@xb+\tikzumlAssemblyConnectorEndArm}% + }% + }{% + \pgfmathsetlength{\tikzuml@AC@middleArm}{\tikzuml@AC@xa+\tikzumlAssemblyConnectorStartArm}% + }% + \pgfmathparse{\tikzuml@AC@middleArm>\tikzuml@AC@xa} + \pgfmathparse{\tikzuml@AC@middleArm>\tikzuml@AC@xb} + \ifthenelse{\lengthtest{\tikzuml@AC@middleArm>\tikzuml@AC@xa}}{% + \def\tikzumlAssemblyConnectorStartAnchor{east}% + }{% + \def\tikzumlAssemblyConnectorStartAnchor{west}% + } + \ifthenelse{\lengthtest{\tikzuml@AC@middleArm>\tikzuml@AC@xb}}{% + \def\tikzumlAssemblyConnectorEndAnchor{east}% + }{% + \def\tikzumlAssemblyConnectorEndAnchor{west}% + } + }{}% + % + \ifthenelse{\equal{\tikzumlAssemblyConnectorGeometry}{|-|}}{% + \ifthenelse{\equal{\tikzumlAssemblyConnectorStartArm}{auto}}{% + \ifthenelse{\equal{\tikzumlAssemblyConnectorEndArm}{auto}}{% + \pgfmathsetlength{\tikzuml@AC@middleArm}{0.5 * \tikzuml@AC@ya + 0.5 * \tikzuml@AC@yb}% + }{% + \pgfmathsetlength{\tikzuml@AC@middleArm}{\tikzuml@AC@yb+\tikzumlAssemblyConnectorEndArm}% + }% + }{% + \pgfmathsetlength{\tikzuml@AC@middleArm}{\tikzuml@AC@ya+\tikzumlAssemblyConnectorStartArm}% + }% + \ifthenelse{\tikzuml@AC@middleArm>\tikzuml@AC@ya}{% + \def\tikzumlAssemblyConnectorStartAnchor{north}% + }{% + \def\tikzumlAssemblyConnectorStartAnchor{south}% + } + \ifthenelse{\tikzuml@AC@middleArm>\tikzuml@AC@yb}{% + \def\tikzumlAssemblyConnectorEndAnchor{north}% + }{% + \def\tikzumlAssemblyConnectorEndAnchor{south}% + } + }{}% + % + \ifthenelse{\equal{\tikzumlAssemblyConnectorStartAnchorTmp}{}}{% + }{% + \def\tikzumlAssemblyConnectorStartAnchor{\tikzumlAssemblyConnectorStartAnchorTmp}% + }% + \ifthenelse{\equal{\tikzumlAssemblyConnectorEndAnchorTmp}{}}{% + }{% + \def\tikzumlAssemblyConnectorEndAnchor{\tikzumlAssemblyConnectorEndAnchorTmp}% + }% + % + \node[inner sep=0] (\tikzumlAssemblyConnectorStart@nodeName-\tikzumlAssemblyConnectorLabel@nodeName-port-tmp) at (\tikzumlAssemblyConnectorStart@nodeName.\tikzumlAssemblyConnectorStartAnchor) {};% + \node[inner sep=0] (\tikzumlAssemblyConnectorEnd@nodeName-\tikzumlAssemblyConnectorLabel@nodeName-port-tmp) at (\tikzumlAssemblyConnectorEnd@nodeName.\tikzumlAssemblyConnectorEndAnchor) {};% + % + \umlrelation[style={tikzuml connector style}, #1]{\tikzumlAssemblyConnectorStart@nodeName-\tikzumlAssemblyConnectorLabel@nodeName-port-tmp}{\tikzumlAssemblyConnectorEnd@nodeName-\tikzumlAssemblyConnectorLabel@nodeName-port-tmp}% + % + \ifthenelse{\equal{\tikzumlAssemblyConnectorWithPort}{tikzumlTrue}}{% + \node[inner sep=0.5*\tikzumlAssemblyConnectorWidth, rectangle, draw=\tikzumlAssemblyConnectorDrawColor, fill=\tikzumlAssemblyConnectorPortFillColor] (\tikzumlAssemblyConnectorStart@nodeName-\tikzumlAssemblyConnectorLabel@nodeName-port) at (\tikzumlAssemblyConnectorStart@nodeName-\tikzumlAssemblyConnectorLabel@nodeName-port-tmp) {};% + \node[inner sep=0.5*\tikzumlAssemblyConnectorWidth, rectangle, draw=\tikzumlAssemblyConnectorDrawColor, fill=\tikzumlAssemblyConnectorPortFillColor] (\tikzumlAssemblyConnectorEnd@nodeName-\tikzumlAssemblyConnectorLabel@nodeName-port) at (\tikzumlAssemblyConnectorEnd@nodeName-\tikzumlAssemblyConnectorLabel@nodeName-port-tmp) {};% + }{% + \node[inner sep=0] (\tikzumlAssemblyConnectorStart@nodeName-\tikzumlAssemblyConnectorLabel@nodeName-port) at (\tikzumlAssemblyConnectorStart@nodeName.\tikzumlAssemblyConnectorStartAnchor) {};% + \node[inner sep=0] (\tikzumlAssemblyConnectorEnd@nodeName-\tikzumlAssemblyConnectorLabel@nodeName-port) at (\tikzumlAssemblyConnectorEnd@nodeName.\tikzumlAssemblyConnectorEndAnchor) {};% + }% + % + \addtocounter{tikzumlRelationNum}{-1}% + \ifthenelse{\equal{\tikzumlAssemblyConnectorName}{connector-\thetikzumlConnectorNum}}{% + \edef\tikzumlAssemblyConnectorName{relation-\thetikzumlRelationNum}% + \edef\tikzumlAssemblyConnectorSymbolName{\tikzumlAssemblyConnectorLabel@nodeName}% + }{% + \edef\tikzumlAssemblyConnectorSymbolName{\tikzumlAssemblyConnectorName}% + }% + % + \stepcounter{tikzumlRelationNum}% + % + \ifthenelse{\equal{\tikzumlAssemblyConnectorGeometry}{--}}{% + \umlassemblyconnectorsymbol{\tikzumlAssemblyConnectorStart@nodeName}{\tikzumlAssemblyConnectorEnd@nodeName}% + }{% + \ifthenelse{\equal{\tikzumlAssemblyConnectorGeometry}{-|}}{% + \ifthenelse{\equal{\tikzumlAssemblyConnectorFirstArm}{tikzumlTrue}}{% + \umlassemblyconnectorsymbol{\tikzumlAssemblyConnectorStart@nodeName}{\tikzumlAssemblyConnectorName-2}% + }{% + \umlassemblyconnectorsymbol{\tikzumlAssemblyConnectorName-2}{\tikzumlAssemblyConnectorEnd@nodeName}% + }% + }{% + \ifthenelse{\equal{\tikzumlAssemblyConnectorGeometry}{|-}}{% + \ifthenelse{\equal{\tikzumlAssemblyConnectorFirstArm}{tikzumlTrue}}{% + \umlassemblyconnectorsymbol{\tikzumlAssemblyConnectorStart@nodeName}{\tikzumlAssemblyConnectorName-2}% + }{% + \umlassemblyconnectorsymbol{\tikzumlAssemblyConnectorName-2}{\tikzumlAssemblyConnectorEnd@nodeName}% + }% + }{% + \ifthenelse{\equal{\tikzumlAssemblyConnectorGeometry}{-|-}}{% + \ifthenelse{\equal{\tikzumlAssemblyConnectorFirstArm}{tikzumlTrue}}{% + \umlassemblyconnectorsymbol{\tikzumlAssemblyConnectorStart@nodeName}{\tikzumlAssemblyConnectorName-2}% + }{% + \ifthenelse{\equal{\tikzumlAssemblyConnectorLastArm}{tikzumlTrue}}{% + \umlassemblyconnectorsymbol{\tikzumlAssemblyConnectorName-4}{\tikzumlAssemblyConnectorEnd@nodeName}% + }{% + \umlassemblyconnectorsymbol{\tikzumlAssemblyConnectorName-2}{\tikzumlAssemblyConnectorName-4}% + }% + }% + }{% + \ifthenelse{\equal{\tikzumlAssemblyConnectorGeometry}{|-|}}{% + \ifthenelse{\equal{\tikzumlAssemblyConnectorFirstArm}{tikzumlTrue}}{% + \umlassemblyconnectorsymbol{\tikzumlAssemblyConnectorStart@nodeName}{\tikzumlAssemblyConnectorName-2}% + }{% + \ifthenelse{\equal{\tikzumlAssemblyConnectorLastArm}{tikzumlTrue}}{% + \umlassemblyconnectorsymbol{\tikzumlAssemblyConnectorName-4}{\tikzumlAssemblyConnectorEnd@nodeName}% + }{% + \umlassemblyconnectorsymbol{\tikzumlAssemblyConnectorName-2}{\tikzumlAssemblyConnectorName-4}% + }% + }% + }{}% + }% + }% + }% + }% + \stepcounter{tikzumlConnectorNum}% +}% +% +% shortcuts of \umlassemblyconnector +\newcommand{\umlHVassemblyconnector}[3][]{% + \pgfkeys{/tikzuml/HVassemblyconnector/.cd,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{geometry}}{% + \errmessage{TIKZUML ERROR : in umlHVassemblyconnector, forbidden option geometry}% + }{}% + }}% + \pgfkeys{/tikzuml/HVassemblyconnector/.cd, #1}% + \umlassemblyconnector[geometry=-|, #1]{#2}{#3}% +}% +% +\newcommand{\umlVHassemblyconnector}[3][]{% + \pgfkeys{/tikzuml/VHassemblyconnector/.cd,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{geometry}}{% + \errmessage{TIKZUML ERROR : in umlVHassemblyconnector, forbidden option geometry}% + }{}% + }% + }% + \pgfkeys{/tikzuml/VHassemblyconnector/.cd, #1}% + \umlassemblyconnector[geometry=|-, #1]{#2}{#3}% +}% +% +\newcommand{\umlHVHassemblyconnector}[3][]{% + \pgfkeys{/tikzuml/HVHassemblyconnector/.cd,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{geometry}}{% + \errmessage{TIKZUML ERROR : in umlHVHassemblyconnector, forbidden option geometry}% + }{}% + }}% + \pgfkeys{/tikzuml/HVHassemblyconnector/.cd, #1}% + \umlassemblyconnector[geometry=-|-, #1]{#2}{#3}% +}% +% +\newcommand{\umlVHVassemblyconnector}[3][]{% + \pgfkeys{/tikzuml/VHVassemblyconnector/.cd,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{geometry}}{% + \errmessage{TIKZUML ERROR : in umlVHVassemblyconnector, forbidden option geometry}% + }{}% + }}% + \pgfkeys{/tikzuml/VHVassemblyconnector/.cd, #1}% + \umlassemblyconnector[geometry=|-|, #1]{#2}{#3}% +}% +% +\newcommand{\umlport}[3][]{% + \pgfkeys{/tikzuml/port/.cd, draw/.initial=\tikzumlDefaultDrawColor, fill/.initial=\tikzumlPortDefaultFillColor,% + width/.initial=\tikzumlPortDefaultWidth,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \errmessage{TIKZUML ERROR : in umlport forbidden option \keyname}% + }% + }% + \pgfkeys{/tikzuml/port/.cd, #1}% + \pgfkeys{/tikzuml/port/.cd, width/.get=\tikzumlPortWidth,% + draw/.get=\tikzumlPortDrawColor, fill/.get=\tikzumlPortFillColor}% + \edef\tikzumlPort@name{#2}% + \edef\tikzumlPort@anchor{#3}% + \begingroup% + \def\_{_}\edef\x{\endgroup% + \def\noexpand\tikzumlPort@nodeName{\tikzumlPort@name}}\x% + % + \node[inner sep=0.5*\tikzumlPortWidth, rectangle, draw=\tikzumlPortDrawColor, fill=\tikzumlPortFillColor] (\tikzumlPort@nodeName-\tikzumlPort@anchor-port) at (\tikzumlPort@nodeName.\tikzumlPort@anchor) {};% +}% +% +\newcommand{\umldelegateconnector}[3][]{% + \def\tikzumlDelegateConnectorWithStartPort{tikzumlFalse}% + \def\tikzumlDelegateConnectorWithEndPort{tikzumlFalse}% + \pgfkeys{/tikzuml/delegateconnector/.cd, + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{stereo}}{% + \errmessage{TIKZUML ERROR : in umldelegateconnector, forbidden option stereo}% + }{}% + }}% + \pgfkeys{/tikzuml/delegateconnector/.cd, #1}% + \umlrelation[style={tikzuml connector style}, stereo=delegate, #1]{#2}{#3}% +}% +% +% shortcuts of \umldelegateconnector +\newcommand{\umlHVdelegateconnector}[3][]{% + \pgfkeys{/tikzuml/HVdelegateconnector/.cd,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{geometry}\OR\equal{\keyname}{stereo}}{% + \errmessage{TIKZUML ERROR : in umlHVdelegateconnector, forbidden option \keyname}% + }{}% + }}% + \pgfkeys{/tikzuml/HVdelegateconnector/.cd, #1}% + \umldelegateconnector[geometry=-|, #1]{#2}{#3}% +}% +% +\newcommand{\umlVHdelegateconnector}[3][]{% + \pgfkeys{/tikzuml/VHdelegateconnector/.cd,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{geometry}\OR\equal{\keyname}{stereo}}{% + \errmessage{TIKZUML ERROR : in umlVHdelegateconnector, forbidden option \keyname}% + }{}% + }}% + \pgfkeys{/tikzuml/VHdelegateconnector/.cd, #1}% + \umldelegateconnector[geometry=|-, #1]{#2}{#3}% +}% +% +\newcommand{\umlHVHdelegateconnector}[3][]{% + \pgfkeys{/tikzuml/HVHdelegateconnector/.cd,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{geometry}\OR\equal{\keyname}{stereo}}{% + \errmessage{TIKZUML ERROR : in umlHVHdelegateconnector, forbidden option \keyname}% + }{}% + }}% + \pgfkeys{/tikzuml/HVHdelegateconnector/.cd, #1}% + \umldelegateconnector[geometry=-|-, #1]{#2}{#3}% +}% +% +\newcommand{\umlVHVdelegateconnector}[3][]{% + \pgfkeys{/tikzuml/VHVdelegateconnector/.cd,% + .unknown/.code={% + \let\keyname=\pgfkeyscurrentname% + \let\keyvalue=\pgfkeyscurrentvalue% + \ifthenelse{\equal{\keyname}{geometry}\OR\equal{\keyname}{stereo}}{% + \errmessage{TIKZUML ERROR : in umlVHVdelegateconnector, forbidden option \keyname}% + }{}% + }}% + \pgfkeys{/tikzuml/VHVdelegateconnector/.cd, #1}% + \umldelegateconnector[geometry=|-|, #1]{#2}{#3}% +}% +%%% End of tikz-uml.sty +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \ No newline at end of file diff --git a/30-qualitaetsheft/.gitignore b/30-qualitaetsheft/.gitignore new file mode 100644 index 0000000..b1b3dda --- /dev/null +++ b/30-qualitaetsheft/.gitignore @@ -0,0 +1,305 @@ +## Core latex/pdflatex auxiliary files: +*.aux +*.lof +*.log +*.lot +*.fls +*.out +*.toc +*.fmt +*.fot +*.cb +*.cb2 +.*.lb + +## Intermediate documents: +*.dvi +*.xdv +*-converted-to.* +# these rules might exclude image files for figures etc. +# *.ps +# *.eps +*.pdf +!assets/*.pdf + +## Generated if empty string is given at "Please type another file name for output:" +.pdf + +## Bibliography auxiliary files (bibtex/biblatex/biber): +*.bbl +*.bcf +*.blg +*-blx.aux +*-blx.bib +*.run.xml + +## Build tool auxiliary files: +*.fdb_latexmk +*.synctex +*.synctex(busy) +*.synctex.gz +*.synctex.gz(busy) +*.pdfsync + +## Build tool directories for auxiliary files +# latexrun +latex.out/ + +## Auxiliary and intermediate files from other packages: +# algorithms +*.alg +*.loa + +# achemso +acs-*.bib + +# amsthm +*.thm + +# beamer +*.nav +*.pre +*.snm +*.vrb + +# changes +*.soc + +# comment +*.cut + +# cprotect +*.cpt + +# elsarticle (documentclass of Elsevier journals) +*.spl + +# endnotes +*.ent + +# fixme +*.lox + +# feynmf/feynmp +*.mf +*.mp +*.t[1-9] +*.t[1-9][0-9] +*.tfm + +#(r)(e)ledmac/(r)(e)ledpar +*.end +*.?end +*.[1-9] +*.[1-9][0-9] +*.[1-9][0-9][0-9] +*.[1-9]R +*.[1-9][0-9]R +*.[1-9][0-9][0-9]R +*.eledsec[1-9] +*.eledsec[1-9]R +*.eledsec[1-9][0-9] +*.eledsec[1-9][0-9]R +*.eledsec[1-9][0-9][0-9] +*.eledsec[1-9][0-9][0-9]R + +# glossaries +*.acn +*.acr +*.glg +*.glo +*.gls +*.glsdefs +*.lzo +*.lzs +*.slg +*.slo +*.sls + +# uncomment this for glossaries-extra (will ignore makeindex's style files!) +*.ist + +# gnuplot +*.gnuplot +*.table + +# gnuplottex +*-gnuplottex-* + +# gregoriotex +*.gaux +*.glog +*.gtex + +# htlatex +*.4ct +*.4tc +*.idv +*.lg +*.trc +*.xref + +# hyperref +*.brf + +# knitr +*-concordance.tex +# TODO Uncomment the next line if you use knitr and want to ignore its generated tikz files +# *.tikz +*-tikzDictionary + +# listings +*.lol + +# luatexja-ruby +*.ltjruby + +# makeidx +*.idx +*.ilg +*.ind + +# minitoc +*.maf +*.mlf +*.mlt +*.mtc[0-9]* +*.slf[0-9]* +*.slt[0-9]* +*.stc[0-9]* + +# minted +_minted* +*.pyg + +# morewrites +*.mw + +# newpax +*.newpax + +# nomencl +*.nlg +*.nlo +*.nls + +# pax +*.pax + +# pdfpcnotes +*.pdfpc + +# sagetex +*.sagetex.sage +*.sagetex.py +*.sagetex.scmd + +# scrwfile +*.wrt + +# svg +svg-inkscape/ + +# sympy +*.sout +*.sympy +sympy-plots-for-*.tex/ + +# pdfcomment +*.upa +*.upb + +# pythontex +*.pytxcode +pythontex-files-*/ + +# tcolorbox +*.listing + +# thmtools +*.loe + +# TikZ & PGF +*.dpth +*.md5 +*.auxlock + +# titletoc +*.ptc + +# todonotes +*.tdo + +# vhistory +*.hst +*.ver + +# easy-todo +*.lod + +# xcolor +*.xcp + +# xmpincl +*.xmpi + +# xindy +*.xdy + +# xypic precompiled matrices and outlines +*.xyc +*.xyd + +# endfloat +*.ttt +*.fff + +# Latexian +TSWLatexianTemp* + +## Editors: +# WinEdt +*.bak +*.sav + +# Texpad +.texpadtmp + +# LyX +*.lyx~ + +# Kile +*.backup + +# gummi +.*.swp + +# KBibTeX +*~[0-9]* + +# TeXnicCenter +*.tps + +# auto folder when using emacs and auctex +./auto/* +*.el + +# expex forward references with \gathertags +*-tags.tex + +# standalone packages +*.sta + +# Makeindex log files +*.lpz + +# xwatermark package +*.xwm + +# REVTeX puts footnotes in the bibliography by default, unless the nofootinbib +# option is specified. Footnotes are the stored in a file with suffix Notes.bib. +# Uncomment the next line to have this generated file ignored. +#*Notes.bib + +# Gannt Diagramm als EPS ignorieren +assets/gantt.eps \ No newline at end of file diff --git a/30-qualitaetsheft/.gitlab-ci.yml b/30-qualitaetsheft/.gitlab-ci.yml new file mode 100644 index 0000000..27d0617 --- /dev/null +++ b/30-qualitaetsheft/.gitlab-ci.yml @@ -0,0 +1,36 @@ +plantuml: + stage: .pre + image: + name: plantuml/plantuml + entrypoint: [""] + script: + - java -jar plantuml.jar -tpdf assets/diagrams/*.puml + artifacts: + paths: + - assets + +tex: + stage: build + image: texlive/texlive + script: + - mkdir public + - make tex + - mv *.pdf public + artifacts: + paths: + - public + dependencies: + - plantuml + +pages: + stage: deploy + script: + - echo Hello, World! + artifacts: + paths: + - public + rules: + - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH + dependencies: + - tex + diff --git a/30-qualitaetsheft/.latexmkrc b/30-qualitaetsheft/.latexmkrc new file mode 100644 index 0000000..bec2b38 --- /dev/null +++ b/30-qualitaetsheft/.latexmkrc @@ -0,0 +1,7 @@ +# https://tex.stackexchange.com/questions/1226/how-to-make-latexmk-use-makeglossaries +add_cus_dep('glo', 'gls', 0, 'makeglo2gls'); +add_cus_dep('acn', 'acr', 0, 'makeglo2gls'); +sub makeglo2gls { + system("makeglossaries $_[0]"); +} + diff --git a/30-qualitaetsheft/Makefile b/30-qualitaetsheft/Makefile new file mode 100644 index 0000000..292ac93 --- /dev/null +++ b/30-qualitaetsheft/Makefile @@ -0,0 +1,18 @@ +MAIN = qualitaetsheft +FLAGS = -pdf + +all: clean compile +compile: diagram tex +clean: clean-diagram clean-tex + +dev: + latexmk $(FLAGS) -pvc $(MAIN) +tex: + latexmk $(FLAGS) $(MAIN) +diagram: + java -jar plantuml.jar -tpdf assets/diagrams/*.puml +clean-tex: + latexmk -C +clean-diagram: + find assets/diagrams -type f -not -name '*.puml' -delete + diff --git a/30-qualitaetsheft/README.md b/30-qualitaetsheft/README.md new file mode 100644 index 0000000..2c08f29 --- /dev/null +++ b/30-qualitaetsheft/README.md @@ -0,0 +1,31 @@ +# Entwurfsheft + +> Systemdesign und -spezifikation + +## Diagramme + +Installiere [PlantUML](https://plantuml.com/starting) (oder über einen Paketmanager). + +Arbeiten an Diagrammen mit Echtzeit-Vorschau (Anzeige wird beim Speichern der +puml-Datei aktualisiert): +```sh +java -jar plantuml.jar -gui assets/classdiagram.puml + +# bzw (wenn zu PATH hinzugefügt oder Linux) +plantuml -gui assets/classdiagram.puml +``` + +Bauen der Diagramme: +```sh +java -jar plantuml.jar -teps assets/*.puml + +# bzw (wenn zu PATH hinzugefügt oder Linux) +plantuml -teps assets/*.puml + +# bzw über Makefile (Linux) +make diagram + +# oder zum Erstellen von Diagrammen und LaTeX: +make +``` + diff --git a/30-qualitaetsheft/assets/.gitignore b/30-qualitaetsheft/assets/.gitignore new file mode 100644 index 0000000..16252a4 --- /dev/null +++ b/30-qualitaetsheft/assets/.gitignore @@ -0,0 +1,3 @@ +diagrams/* +!diagrams/*.puml + diff --git a/30-qualitaetsheft/assets/ChangedPassword.PNG b/30-qualitaetsheft/assets/ChangedPassword.PNG new file mode 100644 index 0000000..254bf66 Binary files /dev/null and b/30-qualitaetsheft/assets/ChangedPassword.PNG differ diff --git a/30-qualitaetsheft/assets/KIT_Deckblatt.pdf b/30-qualitaetsheft/assets/KIT_Deckblatt.pdf new file mode 100644 index 0000000..7de8ed4 Binary files /dev/null and b/30-qualitaetsheft/assets/KIT_Deckblatt.pdf differ diff --git a/30-qualitaetsheft/assets/WrongUser.PNG b/30-qualitaetsheft/assets/WrongUser.PNG new file mode 100644 index 0000000..16614d4 Binary files /dev/null and b/30-qualitaetsheft/assets/WrongUser.PNG differ diff --git a/30-qualitaetsheft/assets/coverage/authenticationService.png b/30-qualitaetsheft/assets/coverage/authenticationService.png new file mode 100644 index 0000000..4ad91e1 Binary files /dev/null and b/30-qualitaetsheft/assets/coverage/authenticationService.png differ diff --git a/30-qualitaetsheft/assets/coverage/episodeActionService.png b/30-qualitaetsheft/assets/coverage/episodeActionService.png new file mode 100644 index 0000000..c81f26f Binary files /dev/null and b/30-qualitaetsheft/assets/coverage/episodeActionService.png differ diff --git a/30-qualitaetsheft/assets/coverage/server.png b/30-qualitaetsheft/assets/coverage/server.png new file mode 100644 index 0000000..d28917a Binary files /dev/null and b/30-qualitaetsheft/assets/coverage/server.png differ diff --git a/30-qualitaetsheft/assets/coverage/subscriptionsService.png b/30-qualitaetsheft/assets/coverage/subscriptionsService.png new file mode 100644 index 0000000..fdb7c9b Binary files /dev/null and b/30-qualitaetsheft/assets/coverage/subscriptionsService.png differ diff --git a/30-qualitaetsheft/assets/coverage/util.png b/30-qualitaetsheft/assets/coverage/util.png new file mode 100644 index 0000000..823cd9e Binary files /dev/null and b/30-qualitaetsheft/assets/coverage/util.png differ diff --git a/30-qualitaetsheft/assets/lasttest/goodSpecsApdex.png b/30-qualitaetsheft/assets/lasttest/goodSpecsApdex.png new file mode 100644 index 0000000..17a7031 Binary files /dev/null and b/30-qualitaetsheft/assets/lasttest/goodSpecsApdex.png differ diff --git a/30-qualitaetsheft/assets/lasttest/goodSpecsResponse.png b/30-qualitaetsheft/assets/lasttest/goodSpecsResponse.png new file mode 100644 index 0000000..ca737f4 Binary files /dev/null and b/30-qualitaetsheft/assets/lasttest/goodSpecsResponse.png differ diff --git a/30-qualitaetsheft/assets/lasttest/goodSpecsTable.png b/30-qualitaetsheft/assets/lasttest/goodSpecsTable.png new file mode 100644 index 0000000..c247933 Binary files /dev/null and b/30-qualitaetsheft/assets/lasttest/goodSpecsTable.png differ diff --git a/30-qualitaetsheft/assets/lasttest/minimumSpecsApdex.png b/30-qualitaetsheft/assets/lasttest/minimumSpecsApdex.png new file mode 100644 index 0000000..f326e01 Binary files /dev/null and b/30-qualitaetsheft/assets/lasttest/minimumSpecsApdex.png differ diff --git a/30-qualitaetsheft/assets/lasttest/minimumSpecsResponse.png b/30-qualitaetsheft/assets/lasttest/minimumSpecsResponse.png new file mode 100644 index 0000000..c6d27ff Binary files /dev/null and b/30-qualitaetsheft/assets/lasttest/minimumSpecsResponse.png differ diff --git a/30-qualitaetsheft/assets/lasttest/minimumSpecsTable.png b/30-qualitaetsheft/assets/lasttest/minimumSpecsTable.png new file mode 100644 index 0000000..db077cd Binary files /dev/null and b/30-qualitaetsheft/assets/lasttest/minimumSpecsTable.png differ diff --git a/30-qualitaetsheft/assets/logo.pdf b/30-qualitaetsheft/assets/logo.pdf new file mode 100644 index 0000000..91fd334 Binary files /dev/null and b/30-qualitaetsheft/assets/logo.pdf differ diff --git a/30-qualitaetsheft/assets/logo.svg b/30-qualitaetsheft/assets/logo.svg new file mode 100644 index 0000000..1609066 --- /dev/null +++ b/30-qualitaetsheft/assets/logo.svg @@ -0,0 +1,211 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Podcast Synchronisation made Efficient + + + + diff --git a/30-qualitaetsheft/assets/notifications.png b/30-qualitaetsheft/assets/notifications.png new file mode 100644 index 0000000..1a5b81d Binary files /dev/null and b/30-qualitaetsheft/assets/notifications.png differ diff --git a/30-qualitaetsheft/assets/select-subscriptions.png b/30-qualitaetsheft/assets/select-subscriptions.png new file mode 100644 index 0000000..0595397 Binary files /dev/null and b/30-qualitaetsheft/assets/select-subscriptions.png differ diff --git a/30-qualitaetsheft/assets/sources/checkstyle-after-processed.txt b/30-qualitaetsheft/assets/sources/checkstyle-after-processed.txt new file mode 100644 index 0000000..8bb59fa --- /dev/null +++ b/30-qualitaetsheft/assets/sources/checkstyle-after-processed.txt @@ -0,0 +1 @@ +1 (design) HideUtilityClassConstructor diff --git a/30-qualitaetsheft/assets/sources/checkstyle-after.txt b/30-qualitaetsheft/assets/sources/checkstyle-after.txt new file mode 100644 index 0000000..cec00d5 --- /dev/null +++ b/30-qualitaetsheft/assets/sources/checkstyle-after.txt @@ -0,0 +1,22 @@ +[INFO] Scanning for projects... +[INFO] +[INFO] -----------------------< org.pse-squared:server >----------------------- +[INFO] Building server 0.0.1-SNAPSHOT +[INFO] --------------------------------[ jar ]--------------------------------- +[INFO] +[INFO] --- maven-checkstyle-plugin:3.2.1:check (default-cli) @ server --- +[INFO] There is 1 error reported by Checkstyle 9.3 with sun_checks.xml ruleset. +[ERROR] src/main/java/org/psesquared/server/ServerApplication.java:[12,1] (design) HideUtilityClassConstructor: Hilfsklassen sollten keinen Standard-Konstruktur und keinen als public deklarierten Konstruktor haben. +[INFO] ------------------------------------------------------------------------ +[INFO] BUILD FAILURE +[INFO] ------------------------------------------------------------------------ +[INFO] Total time: 14.432 s +[INFO] Finished at: 2023-03-18T08:30:11+01:00 +[INFO] ------------------------------------------------------------------------ +[ERROR] Failed to execute goal org.apache.maven.plugins:maven-checkstyle-plugin:3.2.1:check (default-cli) on project server: You have 1 Checkstyle violation. -> [Help 1] +[ERROR] +[ERROR] To see the full stack trace of the errors, re-run Maven with the -e switch. +[ERROR] Re-run Maven using the -X switch to enable full debug logging. +[ERROR] +[ERROR] For more information about the errors and possible solutions, please read the following articles: +[ERROR] [Help 1] http://cwiki.apache.org/confluence/display/MAVEN/MojoFailureException diff --git a/30-qualitaetsheft/assets/sources/checkstyle-before-processed.txt b/30-qualitaetsheft/assets/sources/checkstyle-before-processed.txt new file mode 100644 index 0000000..3a5af1a --- /dev/null +++ b/30-qualitaetsheft/assets/sources/checkstyle-before-processed.txt @@ -0,0 +1,23 @@ +285 (sizes) LineLength +143 (misc) FinalParameters +101 (javadoc) JavadocVariable +83 (javadoc) JavadocStyle +43 (design) DesignForExtension +39 (javadoc) MissingJavadocMethod +26 (regexp) RegexpSingleline +15 (whitespace) WhitespaceAround +13 (javadoc) JavadocPackage +10 (whitespace) OperatorWrap +10 (javadoc) JavadocType +7 (imports) AvoidStarImport +6 (coding) MagicNumber +4 (javadoc) JavadocMethod +3 (coding) HiddenField +2 (javadoc) InvalidJavadocPosition +2 (design) HideUtilityClassConstructor +2 (blocks) RightCurly +1 (whitespace) WhitespaceAfter +1 (whitespace) FileTabCharacter +1 (misc) NewlineAtEndOfFile +1 (misc) ArrayTypeStyle +1 (imports) UnusedImports diff --git a/30-qualitaetsheft/assets/sources/checkstyle-before.txt b/30-qualitaetsheft/assets/sources/checkstyle-before.txt new file mode 100644 index 0000000..abc9f4f --- /dev/null +++ b/30-qualitaetsheft/assets/sources/checkstyle-before.txt @@ -0,0 +1,820 @@ +[INFO] Scanning for projects... +[INFO] +[INFO] -----------------------< org.pse-squared:server >----------------------- +[INFO] Building server 0.0.1-SNAPSHOT +[INFO] --------------------------------[ jar ]--------------------------------- +[INFO] +[INFO] --- maven-checkstyle-plugin:3.2.1:check (default-cli) @ server --- +[INFO] There are 799 errors reported by Checkstyle 9.3 with sun_checks.xml ruleset. +[ERROR] src/main/java/org/psesquared/server/config/AuthenticationValidatorInterceptor.java:[1] (javadoc) JavadocPackage: Es fehlt eine package-info.java. +[ERROR] src/main/java/org/psesquared/server/config/AuthenticationValidatorInterceptor.java:[15,5] (javadoc) JavadocVariable: Es fehlt ein Javadoc-Kommentar. +[ERROR] src/main/java/org/psesquared/server/config/AuthenticationValidatorInterceptor.java:[16,5] (javadoc) JavadocVariable: Es fehlt ein Javadoc-Kommentar. +[ERROR] src/main/java/org/psesquared/server/config/AuthenticationValidatorInterceptor.java:[17,5] (javadoc) JavadocVariable: Es fehlt ein Javadoc-Kommentar. +[ERROR] src/main/java/org/psesquared/server/config/AuthenticationValidatorInterceptor.java:[18,5] (javadoc) JavadocVariable: Es fehlt ein Javadoc-Kommentar. +[ERROR] src/main/java/org/psesquared/server/config/AuthenticationValidatorInterceptor.java:[20,5] (design) DesignForExtension: Klasse 'AuthenticationValidatorInterceptor' sieht wie zur Erweiterung entworfen aus (kann subclassed werden), aber die Methode 'preHandle' besitzt keinen Javadoc-Kommentar, der erklärt, wie Sie das sicher tun. Wenn die Klasse nicht für Erweiterungen ausgelegt ist, sollten sie die Klasse 'AuthenticationValidatorInterceptor' final oder die Methode 'preHandle' static/final/abstract/empty machen oder die zugelassene Annotation für die Methode hinzufügen. +[ERROR] src/main/java/org/psesquared/server/config/AuthenticationValidatorInterceptor.java:[21,30] (misc) FinalParameters: Der Parameter request sollte als 'final' deklariert sein. +[ERROR] src/main/java/org/psesquared/server/config/AuthenticationValidatorInterceptor.java:[22,13] (misc) FinalParameters: Der Parameter response sollte als 'final' deklariert sein. +[ERROR] src/main/java/org/psesquared/server/config/AuthenticationValidatorInterceptor.java:[23,13] (misc) FinalParameters: Der Parameter handler sollte als 'final' deklariert sein. +[ERROR] src/main/java/org/psesquared/server/config/AuthenticationValidatorInterceptor.java:[25] (sizes) LineLength: Zeile ist 81 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/config/AuthenticationValidatorInterceptor.java:[26] (sizes) LineLength: Zeile ist 113 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/config/AuthenticationValidatorInterceptor.java:[44,48] (misc) FinalParameters: Der Parameter request sollte als 'final' deklariert sein. +[ERROR] src/main/java/org/psesquared/server/config/AuthenticationValidatorInterceptor.java:[45] (sizes) LineLength: Zeile ist 117 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/config/AuthenticationValidatorInterceptor.java:[46] (sizes) LineLength: Zeile ist 101 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/config/AuthenticationValidatorInterceptor.java:[49] (sizes) LineLength: Zeile ist 90 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/config/SecurityConfigProperties.java:[6] (sizes) LineLength: Zeile ist 87 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/config/SecurityConfigProperties.java:[6,86] (whitespace) WhitespaceAround: Nach '{' fehlt ein Leerzeichen. +[ERROR] src/main/java/org/psesquared/server/config/SecurityConfigProperties.java:[6,87] (whitespace) WhitespaceAround: Vor '}' fehlt ein Leerzeichen. +[ERROR] src/main/java/org/psesquared/server/config/EMailConfigProperties.java:[6] (sizes) LineLength: Zeile ist 108 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/config/EMailConfigProperties.java:[6,107] (whitespace) WhitespaceAround: Nach '{' fehlt ein Leerzeichen. +[ERROR] src/main/java/org/psesquared/server/config/EMailConfigProperties.java:[6,108] (whitespace) WhitespaceAround: Vor '}' fehlt ein Leerzeichen. +[ERROR] src/main/java/org/psesquared/server/config/SecurityConfig.java:[22,5] (javadoc) JavadocVariable: Es fehlt ein Javadoc-Kommentar. +[ERROR] src/main/java/org/psesquared/server/config/SecurityConfig.java:[23,5] (javadoc) JavadocVariable: Es fehlt ein Javadoc-Kommentar. +[ERROR] src/main/java/org/psesquared/server/config/SecurityConfig.java:[24] (sizes) LineLength: Zeile ist 82 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/config/SecurityConfig.java:[24,5] (javadoc) JavadocVariable: Es fehlt ein Javadoc-Kommentar. +[ERROR] src/main/java/org/psesquared/server/config/SecurityConfig.java:[25] (sizes) LineLength: Zeile ist 97 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/config/SecurityConfig.java:[25,5] (javadoc) JavadocVariable: Es fehlt ein Javadoc-Kommentar. +[ERROR] src/main/java/org/psesquared/server/config/SecurityConfig.java:[27,5] (javadoc) JavadocVariable: Es fehlt ein Javadoc-Kommentar. +[ERROR] src/main/java/org/psesquared/server/config/SecurityConfig.java:[28,5] (javadoc) JavadocVariable: Es fehlt ein Javadoc-Kommentar. +[ERROR] src/main/java/org/psesquared/server/config/SecurityConfig.java:[30,5] (javadoc) MissingJavadocMethod: Es fehlt ein Javadoc-Kommentar. +[ERROR] src/main/java/org/psesquared/server/config/SecurityConfig.java:[30,5] (design) DesignForExtension: Klasse 'SecurityConfig' sieht wie zur Erweiterung entworfen aus (kann subclassed werden), aber die Methode 'securityFilterChain' besitzt keinen Javadoc-Kommentar, der erklärt, wie Sie das sicher tun. Wenn die Klasse nicht für Erweiterungen ausgelegt ist, sollten sie die Klasse 'SecurityConfig' final oder die Methode 'securityFilterChain' static/final/abstract/empty machen oder die zugelassene Annotation für die Methode hinzufügen. +[ERROR] src/main/java/org/psesquared/server/config/SecurityConfig.java:[31] (sizes) LineLength: Zeile ist 88 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/config/SecurityConfig.java:[31,52] (misc) FinalParameters: Der Parameter http sollte als 'final' deklariert sein. +[ERROR] src/main/java/org/psesquared/server/config/SecurityConfig.java:[48] (sizes) LineLength: Zeile ist 91 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/config/SecurityConfig.java:[56,5] (design) DesignForExtension: Klasse 'SecurityConfig' sieht wie zur Erweiterung entworfen aus (kann subclassed werden), aber die Methode 'corsConfigurationSource' besitzt keinen Javadoc-Kommentar, der erklärt, wie Sie das sicher tun. Wenn die Klasse nicht für Erweiterungen ausgelegt ist, sollten sie die Klasse 'SecurityConfig' final oder die Methode 'corsConfigurationSource' static/final/abstract/empty machen oder die zugelassene Annotation für die Methode hinzufügen. +[ERROR] src/main/java/org/psesquared/server/config/SecurityConfig.java:[63] (sizes) LineLength: Zeile ist 87 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/config/JwtService.java:[3,23] (imports) AvoidStarImport: Stern-Importe der Form '.*' sollten vermieden werden - io.jsonwebtoken.*. +[ERROR] src/main/java/org/psesquared/server/config/JwtService.java:[21,5] (javadoc) JavadocVariable: Es fehlt ein Javadoc-Kommentar. +[ERROR] src/main/java/org/psesquared/server/config/JwtService.java:[22] (sizes) LineLength: Zeile ist 92 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/config/JwtService.java:[22,5] (javadoc) JavadocVariable: Es fehlt ein Javadoc-Kommentar. +[ERROR] src/main/java/org/psesquared/server/config/JwtService.java:[24,5] (javadoc) JavadocVariable: Es fehlt ein Javadoc-Kommentar. +[ERROR] src/main/java/org/psesquared/server/config/JwtService.java:[26,5] (javadoc) MissingJavadocMethod: Es fehlt ein Javadoc-Kommentar. +[ERROR] src/main/java/org/psesquared/server/config/JwtService.java:[26,5] (design) DesignForExtension: Klasse 'JwtService' sieht wie zur Erweiterung entworfen aus (kann subclassed werden), aber die Methode 'extractUsername' besitzt keinen Javadoc-Kommentar, der erklärt, wie Sie das sicher tun. Wenn die Klasse nicht für Erweiterungen ausgelegt ist, sollten sie die Klasse 'JwtService' final oder die Methode 'extractUsername' static/final/abstract/empty machen oder die zugelassene Annotation für die Methode hinzufügen. +[ERROR] src/main/java/org/psesquared/server/config/JwtService.java:[26,35] (misc) FinalParameters: Der Parameter token sollte als 'final' deklariert sein. +[ERROR] src/main/java/org/psesquared/server/config/JwtService.java:[30,5] (javadoc) MissingJavadocMethod: Es fehlt ein Javadoc-Kommentar. +[ERROR] src/main/java/org/psesquared/server/config/JwtService.java:[30,5] (design) DesignForExtension: Klasse 'JwtService' sieht wie zur Erweiterung entworfen aus (kann subclassed werden), aber die Methode 'extractClaim' besitzt keinen Javadoc-Kommentar, der erklärt, wie Sie das sicher tun. Wenn die Klasse nicht für Erweiterungen ausgelegt ist, sollten sie die Klasse 'JwtService' final oder die Methode 'extractClaim' static/final/abstract/empty machen oder die zugelassene Annotation für die Methode hinzufügen. +[ERROR] src/main/java/org/psesquared/server/config/JwtService.java:[30,31] (misc) FinalParameters: Der Parameter token sollte als 'final' deklariert sein. +[ERROR] src/main/java/org/psesquared/server/config/JwtService.java:[30,45] (misc) FinalParameters: Der Parameter claimsResolver sollte als 'final' deklariert sein. +[ERROR] src/main/java/org/psesquared/server/config/JwtService.java:[36] (regexp) RegexpSingleline: Line has trailing spaces. +[ERROR] src/main/java/org/psesquared/server/config/JwtService.java:[41] (sizes) LineLength: Zeile ist 122 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/config/JwtService.java:[41,5] (javadoc) MissingJavadocMethod: Es fehlt ein Javadoc-Kommentar. +[ERROR] src/main/java/org/psesquared/server/config/JwtService.java:[41,5] (design) DesignForExtension: Klasse 'JwtService' sieht wie zur Erweiterung entworfen aus (kann subclassed werden), aber die Methode 'generateTokenString' besitzt keinen Javadoc-Kommentar, der erklärt, wie Sie das sicher tun. Wenn die Klasse nicht für Erweiterungen ausgelegt ist, sollten sie die Klasse 'JwtService' final oder die Methode 'generateTokenString' static/final/abstract/empty machen oder die zugelassene Annotation für die Methode hinzufügen. +[ERROR] src/main/java/org/psesquared/server/config/JwtService.java:[41,39] (misc) FinalParameters: Der Parameter additionalClaims sollte als 'final' deklariert sein. +[ERROR] src/main/java/org/psesquared/server/config/JwtService.java:[41,77] (misc) FinalParameters: Der Parameter userDetails sollte als 'final' deklariert sein. +[ERROR] src/main/java/org/psesquared/server/config/JwtService.java:[41,102] (misc) FinalParameters: Der Parameter tokenLifespan sollte als 'final' deklariert sein. +[ERROR] src/main/java/org/psesquared/server/config/JwtService.java:[46] (sizes) LineLength: Zeile ist 84 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/config/JwtService.java:[51,5] (javadoc) MissingJavadocMethod: Es fehlt ein Javadoc-Kommentar. +[ERROR] src/main/java/org/psesquared/server/config/JwtService.java:[51,5] (design) DesignForExtension: Klasse 'JwtService' sieht wie zur Erweiterung entworfen aus (kann subclassed werden), aber die Methode 'generateAccessTokenString' besitzt keinen Javadoc-Kommentar, der erklärt, wie Sie das sicher tun. Wenn die Klasse nicht für Erweiterungen ausgelegt ist, sollten sie die Klasse 'JwtService' final oder die Methode 'generateAccessTokenString' static/final/abstract/empty machen oder die zugelassene Annotation für die Methode hinzufügen. +[ERROR] src/main/java/org/psesquared/server/config/JwtService.java:[53] (sizes) LineLength: Zeile ist 95 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/config/JwtService.java:[56,5] (javadoc) MissingJavadocMethod: Es fehlt ein Javadoc-Kommentar. +[ERROR] src/main/java/org/psesquared/server/config/JwtService.java:[56,5] (design) DesignForExtension: Klasse 'JwtService' sieht wie zur Erweiterung entworfen aus (kann subclassed werden), aber die Methode 'generateUrlTokenString' besitzt keinen Javadoc-Kommentar, der erklärt, wie Sie das sicher tun. Wenn die Klasse nicht für Erweiterungen ausgelegt ist, sollten sie die Klasse 'JwtService' final oder die Methode 'generateUrlTokenString' static/final/abstract/empty machen oder die zugelassene Annotation für die Methode hinzufügen. +[ERROR] src/main/java/org/psesquared/server/config/JwtService.java:[57] (sizes) LineLength: Zeile ist 92 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/config/JwtService.java:[60,5] (javadoc) MissingJavadocMethod: Es fehlt ein Javadoc-Kommentar. +[ERROR] src/main/java/org/psesquared/server/config/JwtService.java:[60,5] (design) DesignForExtension: Klasse 'JwtService' sieht wie zur Erweiterung entworfen aus (kann subclassed werden), aber die Methode 'isTokenValid' besitzt keinen Javadoc-Kommentar, der erklärt, wie Sie das sicher tun. Wenn die Klasse nicht für Erweiterungen ausgelegt ist, sollten sie die Klasse 'JwtService' final oder die Methode 'isTokenValid' static/final/abstract/empty machen oder die zugelassene Annotation für die Methode hinzufügen. +[ERROR] src/main/java/org/psesquared/server/config/JwtService.java:[60,33] (misc) FinalParameters: Der Parameter token sollte als 'final' deklariert sein. +[ERROR] src/main/java/org/psesquared/server/config/JwtService.java:[60,47] (misc) FinalParameters: Der Parameter userDetails sollte als 'final' deklariert sein. +[ERROR] src/main/java/org/psesquared/server/config/JwtService.java:[62] (sizes) LineLength: Zeile ist 84 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/config/JwtService.java:[65,36] (misc) FinalParameters: Der Parameter token sollte als 'final' deklariert sein. +[ERROR] src/main/java/org/psesquared/server/config/JwtService.java:[69,36] (misc) FinalParameters: Der Parameter token sollte als 'final' deklariert sein. +[ERROR] src/main/java/org/psesquared/server/config/JwtService.java:[73,37] (misc) FinalParameters: Der Parameter token sollte als 'final' deklariert sein. +[ERROR] src/main/java/org/psesquared/server/config/JwtService.java:[88] (sizes) LineLength: Zeile ist 91 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/config/JwtAuthenticationFilter.java:[26,5] (javadoc) JavadocVariable: Es fehlt ein Javadoc-Kommentar. +[ERROR] src/main/java/org/psesquared/server/config/JwtAuthenticationFilter.java:[27,5] (javadoc) JavadocVariable: Es fehlt ein Javadoc-Kommentar. +[ERROR] src/main/java/org/psesquared/server/config/JwtAuthenticationFilter.java:[28] (sizes) LineLength: Zeile ist 82 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/config/JwtAuthenticationFilter.java:[28,5] (javadoc) JavadocVariable: Es fehlt ein Javadoc-Kommentar. +[ERROR] src/main/java/org/psesquared/server/config/JwtAuthenticationFilter.java:[29] (sizes) LineLength: Zeile ist 97 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/config/JwtAuthenticationFilter.java:[29,5] (javadoc) JavadocVariable: Es fehlt ein Javadoc-Kommentar. +[ERROR] src/main/java/org/psesquared/server/config/JwtAuthenticationFilter.java:[30,5] (javadoc) JavadocVariable: Es fehlt ein Javadoc-Kommentar. +[ERROR] src/main/java/org/psesquared/server/config/JwtAuthenticationFilter.java:[32,5] (javadoc) JavadocVariable: Es fehlt ein Javadoc-Kommentar. +[ERROR] src/main/java/org/psesquared/server/config/JwtAuthenticationFilter.java:[33,5] (javadoc) JavadocVariable: Es fehlt ein Javadoc-Kommentar. +[ERROR] src/main/java/org/psesquared/server/config/JwtAuthenticationFilter.java:[35,5] (design) DesignForExtension: Klasse 'JwtAuthenticationFilter' sieht wie zur Erweiterung entworfen aus (kann subclassed werden), aber die Methode 'doFilterInternal' besitzt keinen Javadoc-Kommentar, der erklärt, wie Sie das sicher tun. Wenn die Klasse nicht für Erweiterungen ausgelegt ist, sollten sie die Klasse 'JwtAuthenticationFilter' final oder die Methode 'doFilterInternal' static/final/abstract/empty machen oder die zugelassene Annotation für die Methode hinzufügen. +[ERROR] src/main/java/org/psesquared/server/config/JwtAuthenticationFilter.java:[36,37] (misc) FinalParameters: Der Parameter request sollte als 'final' deklariert sein. +[ERROR] src/main/java/org/psesquared/server/config/JwtAuthenticationFilter.java:[37,37] (misc) FinalParameters: Der Parameter response sollte als 'final' deklariert sein. +[ERROR] src/main/java/org/psesquared/server/config/JwtAuthenticationFilter.java:[38,37] (misc) FinalParameters: Der Parameter filterChain sollte als 'final' deklariert sein. +[ERROR] src/main/java/org/psesquared/server/config/JwtAuthenticationFilter.java:[45] (sizes) LineLength: Zeile ist 96 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/config/JwtAuthenticationFilter.java:[54] (sizes) LineLength: Zeile ist 81 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/config/JwtAuthenticationFilter.java:[54,38] (misc) FinalParameters: Der Parameter cookie sollte als 'final' deklariert sein. +[ERROR] src/main/java/org/psesquared/server/config/JwtAuthenticationFilter.java:[54,53] (misc) FinalParameters: Der Parameter request sollte als 'final' deklariert sein. +[ERROR] src/main/java/org/psesquared/server/config/JwtAuthenticationFilter.java:[68] (sizes) LineLength: Zeile ist 95 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/config/JwtAuthenticationFilter.java:[69,13] (whitespace) WhitespaceAfter: Nach 'if' fehlt ein Leerzeichen. +[ERROR] src/main/java/org/psesquared/server/config/JwtAuthenticationFilter.java:[70] (sizes) LineLength: Zeile ist 104 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/config/JwtAuthenticationFilter.java:[76] (sizes) LineLength: Zeile ist 82 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/config/ApplicationConfig.java:[32] (sizes) LineLength: Zeile ist 98 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/config/ApplicationConfig.java:[32,5] (javadoc) JavadocVariable: Es fehlt ein Javadoc-Kommentar. +[ERROR] src/main/java/org/psesquared/server/config/ApplicationConfig.java:[34,5] (javadoc) JavadocVariable: Es fehlt ein Javadoc-Kommentar. +[ERROR] src/main/java/org/psesquared/server/config/ApplicationConfig.java:[36,5] (javadoc) MissingJavadocMethod: Es fehlt ein Javadoc-Kommentar. +[ERROR] src/main/java/org/psesquared/server/config/ApplicationConfig.java:[36,5] (design) DesignForExtension: Klasse 'ApplicationConfig' sieht wie zur Erweiterung entworfen aus (kann subclassed werden), aber die Methode 'userDetailsService' besitzt keinen Javadoc-Kommentar, der erklärt, wie Sie das sicher tun. Wenn die Klasse nicht für Erweiterungen ausgelegt ist, sollten sie die Klasse 'ApplicationConfig' final oder die Methode 'userDetailsService' static/final/abstract/empty machen oder die zugelassene Annotation für die Methode hinzufügen. +[ERROR] src/main/java/org/psesquared/server/config/ApplicationConfig.java:[39] (sizes) LineLength: Zeile ist 94 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/config/ApplicationConfig.java:[42,5] (javadoc) MissingJavadocMethod: Es fehlt ein Javadoc-Kommentar. +[ERROR] src/main/java/org/psesquared/server/config/ApplicationConfig.java:[42,5] (design) DesignForExtension: Klasse 'ApplicationConfig' sieht wie zur Erweiterung entworfen aus (kann subclassed werden), aber die Methode 'authenticationProvider' besitzt keinen Javadoc-Kommentar, der erklärt, wie Sie das sicher tun. Wenn die Klasse nicht für Erweiterungen ausgelegt ist, sollten sie die Klasse 'ApplicationConfig' final oder die Methode 'authenticationProvider' static/final/abstract/empty machen oder die zugelassene Annotation für die Methode hinzufügen. +[ERROR] src/main/java/org/psesquared/server/config/ApplicationConfig.java:[44] (sizes) LineLength: Zeile ist 81 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/config/ApplicationConfig.java:[50,5] (javadoc) MissingJavadocMethod: Es fehlt ein Javadoc-Kommentar. +[ERROR] src/main/java/org/psesquared/server/config/ApplicationConfig.java:[50,5] (design) DesignForExtension: Klasse 'ApplicationConfig' sieht wie zur Erweiterung entworfen aus (kann subclassed werden), aber die Methode 'passwordEncoder' besitzt keinen Javadoc-Kommentar, der erklärt, wie Sie das sicher tun. Wenn die Klasse nicht für Erweiterungen ausgelegt ist, sollten sie die Klasse 'ApplicationConfig' final oder die Methode 'passwordEncoder' static/final/abstract/empty machen oder die zugelassene Annotation für die Methode hinzufügen. +[ERROR] src/main/java/org/psesquared/server/config/ApplicationConfig.java:[55,5] (javadoc) MissingJavadocMethod: Es fehlt ein Javadoc-Kommentar. +[ERROR] src/main/java/org/psesquared/server/config/ApplicationConfig.java:[55,5] (design) DesignForExtension: Klasse 'ApplicationConfig' sieht wie zur Erweiterung entworfen aus (kann subclassed werden), aber die Methode 'authenticationManager' besitzt keinen Javadoc-Kommentar, der erklärt, wie Sie das sicher tun. Wenn die Klasse nicht für Erweiterungen ausgelegt ist, sollten sie die Klasse 'ApplicationConfig' final oder die Methode 'authenticationManager' static/final/abstract/empty machen oder die zugelassene Annotation für die Methode hinzufügen. +[ERROR] src/main/java/org/psesquared/server/config/ApplicationConfig.java:[56] (sizes) LineLength: Zeile ist 109 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/config/ApplicationConfig.java:[56,56] (misc) FinalParameters: Der Parameter config sollte als 'final' deklariert sein. +[ERROR] src/main/java/org/psesquared/server/config/ApplicationConfig.java:[60,5] (javadoc) MissingJavadocMethod: Es fehlt ein Javadoc-Kommentar. +[ERROR] src/main/java/org/psesquared/server/config/ApplicationConfig.java:[60,5] (design) DesignForExtension: Klasse 'ApplicationConfig' sieht wie zur Erweiterung entworfen aus (kann subclassed werden), aber die Methode 'corsConfigurer' besitzt keinen Javadoc-Kommentar, der erklärt, wie Sie das sicher tun. Wenn die Klasse nicht für Erweiterungen ausgelegt ist, sollten sie die Klasse 'ApplicationConfig' final oder die Methode 'corsConfigurer' static/final/abstract/empty machen oder die zugelassene Annotation für die Methode hinzufügen. +[ERROR] src/main/java/org/psesquared/server/config/ApplicationConfig.java:[64,41] (misc) FinalParameters: Der Parameter registry sollte als 'final' deklariert sein. +[ERROR] src/main/java/org/psesquared/server/config/ApplicationConfig.java:[73,5] (design) DesignForExtension: Klasse 'ApplicationConfig' sieht wie zur Erweiterung entworfen aus (kann subclassed werden), aber die Methode 'addInterceptors' besitzt keinen Javadoc-Kommentar, der erklärt, wie Sie das sicher tun. Wenn die Klasse nicht für Erweiterungen ausgelegt ist, sollten sie die Klasse 'ApplicationConfig' final oder die Methode 'addInterceptors' static/final/abstract/empty machen oder die zugelassene Annotation für die Methode hinzufügen. +[ERROR] src/main/java/org/psesquared/server/config/ApplicationConfig.java:[74,33] (misc) FinalParameters: Der Parameter registry sollte als 'final' deklariert sein. +[ERROR] src/main/java/org/psesquared/server/model/Subscription.java:[1] (javadoc) JavadocPackage: Es fehlt eine package-info.java. +[ERROR] src/main/java/org/psesquared/server/model/Subscription.java:[24] (javadoc) JavadocStyle: Der erste Satz sollte mit einem Punkt enden. +[ERROR] src/main/java/org/psesquared/server/model/Subscription.java:[33] (sizes) LineLength: Zeile ist 105 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/model/Subscription.java:[36] (javadoc) JavadocStyle: Der erste Satz sollte mit einem Punkt enden. +[ERROR] src/main/java/org/psesquared/server/model/Subscription.java:[45] (javadoc) JavadocStyle: Der erste Satz sollte mit einem Punkt enden. +[ERROR] src/main/java/org/psesquared/server/model/Subscription.java:[51] (javadoc) JavadocStyle: Der erste Satz sollte mit einem Punkt enden. +[ERROR] src/main/java/org/psesquared/server/model/Subscription.java:[57] (javadoc) JavadocStyle: Der erste Satz sollte mit einem Punkt enden. +[ERROR] src/main/java/org/psesquared/server/model/Subscription.java:[63,5] (javadoc) JavadocVariable: Es fehlt ein Javadoc-Kommentar. +[ERROR] src/main/java/org/psesquared/server/model/Subscription.java:[68] (javadoc) JavadocStyle: Der erste Satz sollte mit einem Punkt enden. +[ERROR] src/main/java/org/psesquared/server/model/Subscription.java:[75,9] (javadoc) MissingJavadocMethod: Es fehlt ein Javadoc-Kommentar. +[ERROR] src/main/java/org/psesquared/server/model/Subscription.java:[75,9] (design) DesignForExtension: Klasse 'Subscription' sieht wie zur Erweiterung entworfen aus (kann subclassed werden), aber die Methode 'addEpisode' besitzt keinen Javadoc-Kommentar, der erklärt, wie Sie das sicher tun. Wenn die Klasse nicht für Erweiterungen ausgelegt ist, sollten sie die Klasse 'Subscription' final oder die Methode 'addEpisode' static/final/abstract/empty machen oder die zugelassene Annotation für die Methode hinzufügen. +[ERROR] src/main/java/org/psesquared/server/model/Subscription.java:[75,32] (misc) FinalParameters: Der Parameter episode sollte als 'final' deklariert sein. +[ERROR] src/main/java/org/psesquared/server/model/Role.java:[3] (javadoc) JavadocStyle: Der erste Satz sollte mit einem Punkt enden. +[ERROR] src/main/java/org/psesquared/server/model/Role.java:[8] (javadoc) JavadocStyle: Der erste Satz sollte mit einem Punkt enden. +[ERROR] src/main/java/org/psesquared/server/model/Role.java:[12] (javadoc) JavadocStyle: Der erste Satz sollte mit einem Punkt enden. +[ERROR] src/main/java/org/psesquared/server/model/Role.java:[17,5] (javadoc) JavadocVariable: Es fehlt ein Javadoc-Kommentar. +[ERROR] src/main/java/org/psesquared/server/model/EpisodeAction.java:[22] (javadoc) JavadocStyle: Der erste Satz sollte mit einem Punkt enden. +[ERROR] src/main/java/org/psesquared/server/model/EpisodeAction.java:[33] (javadoc) JavadocStyle: Der erste Satz sollte mit einem Punkt enden. +[ERROR] src/main/java/org/psesquared/server/model/EpisodeAction.java:[42] (javadoc) JavadocStyle: Der erste Satz sollte mit einem Punkt enden. +[ERROR] src/main/java/org/psesquared/server/model/EpisodeAction.java:[49] (javadoc) JavadocStyle: Der erste Satz sollte mit einem Punkt enden. +[ERROR] src/main/java/org/psesquared/server/model/EpisodeAction.java:[56] (javadoc) JavadocStyle: Der erste Satz sollte mit einem Punkt enden. +[ERROR] src/main/java/org/psesquared/server/model/EpisodeAction.java:[64] (javadoc) JavadocStyle: Der erste Satz sollte mit einem Punkt enden. +[ERROR] src/main/java/org/psesquared/server/model/EpisodeAction.java:[73] (javadoc) JavadocStyle: Der erste Satz sollte mit einem Punkt enden. +[ERROR] src/main/java/org/psesquared/server/model/EpisodeAction.java:[80] (javadoc) JavadocStyle: Der erste Satz sollte mit einem Punkt enden. +[ERROR] src/main/java/org/psesquared/server/model/EpisodeAction.java:[88] (javadoc) JavadocStyle: Der erste Satz sollte mit einem Punkt enden. +[ERROR] src/main/java/org/psesquared/server/model/EpisodeAction.java:[92] (javadoc) JavadocMethod: @return-Tag sollte vorhanden sein und eine Beschreibung haben. +[ERROR] src/main/java/org/psesquared/server/model/EpisodeAction.java:[93] (sizes) LineLength: Zeile ist 81 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/model/EpisodeAction.java:[98] (sizes) LineLength: Zeile ist 95 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/model/SubscriptionAction.java:[16] (javadoc) JavadocStyle: Der erste Satz sollte mit einem Punkt enden. +[ERROR] src/main/java/org/psesquared/server/model/SubscriptionAction.java:[27] (javadoc) JavadocStyle: Der erste Satz sollte mit einem Punkt enden. +[ERROR] src/main/java/org/psesquared/server/model/SubscriptionAction.java:[36] (javadoc) JavadocStyle: Der erste Satz sollte mit einem Punkt enden. +[ERROR] src/main/java/org/psesquared/server/model/SubscriptionAction.java:[42] (javadoc) JavadocStyle: Der erste Satz sollte mit einem Punkt enden. +[ERROR] src/main/java/org/psesquared/server/model/SubscriptionAction.java:[50] (javadoc) JavadocStyle: Der erste Satz sollte mit einem Punkt enden. +[ERROR] src/main/java/org/psesquared/server/model/SubscriptionAction.java:[56] (javadoc) JavadocStyle: Der erste Satz sollte mit einem Punkt enden. +[ERROR] src/main/java/org/psesquared/server/model/User.java:[6,27] (imports) AvoidStarImport: Stern-Importe der Form '.*' sollten vermieden werden - jakarta.persistence.*. +[ERROR] src/main/java/org/psesquared/server/model/User.java:[15] (javadoc) JavadocStyle: Der erste Satz sollte mit einem Punkt enden. +[ERROR] src/main/java/org/psesquared/server/model/User.java:[26] (javadoc) JavadocStyle: Der erste Satz sollte mit einem Punkt enden. +[ERROR] src/main/java/org/psesquared/server/model/User.java:[35] (javadoc) JavadocStyle: Der erste Satz sollte mit einem Punkt enden. +[ERROR] src/main/java/org/psesquared/server/model/User.java:[44] (javadoc) JavadocStyle: Der erste Satz sollte mit einem Punkt enden. +[ERROR] src/main/java/org/psesquared/server/model/User.java:[52] (javadoc) JavadocStyle: Der erste Satz sollte mit einem Punkt enden. +[ERROR] src/main/java/org/psesquared/server/model/User.java:[59] (javadoc) JavadocStyle: Der erste Satz sollte mit einem Punkt enden. +[ERROR] src/main/java/org/psesquared/server/model/User.java:[66] (javadoc) JavadocStyle: Der erste Satz sollte mit einem Punkt enden. +[ERROR] src/main/java/org/psesquared/server/model/User.java:[74] (javadoc) JavadocStyle: Der erste Satz sollte mit einem Punkt enden. +[ERROR] src/main/java/org/psesquared/server/model/User.java:[81] (javadoc) JavadocStyle: Der erste Satz sollte mit einem Punkt enden. +[ERROR] src/main/java/org/psesquared/server/model/User.java:[88] (javadoc) JavadocStyle: Der erste Satz sollte mit einem Punkt enden. +[ERROR] src/main/java/org/psesquared/server/model/User.java:[95,5] (design) DesignForExtension: Klasse 'User' sieht wie zur Erweiterung entworfen aus (kann subclassed werden), aber die Methode 'getAuthorities' besitzt keinen Javadoc-Kommentar, der erklärt, wie Sie das sicher tun. Wenn die Klasse nicht für Erweiterungen ausgelegt ist, sollten sie die Klasse 'User' final oder die Methode 'getAuthorities' static/final/abstract/empty machen oder die zugelassene Annotation für die Methode hinzufügen. +[ERROR] src/main/java/org/psesquared/server/model/User.java:[100,5] (design) DesignForExtension: Klasse 'User' sieht wie zur Erweiterung entworfen aus (kann subclassed werden), aber die Methode 'isAccountNonExpired' besitzt keinen Javadoc-Kommentar, der erklärt, wie Sie das sicher tun. Wenn die Klasse nicht für Erweiterungen ausgelegt ist, sollten sie die Klasse 'User' final oder die Methode 'isAccountNonExpired' static/final/abstract/empty machen oder die zugelassene Annotation für die Methode hinzufügen. +[ERROR] src/main/java/org/psesquared/server/model/User.java:[105,5] (design) DesignForExtension: Klasse 'User' sieht wie zur Erweiterung entworfen aus (kann subclassed werden), aber die Methode 'isAccountNonLocked' besitzt keinen Javadoc-Kommentar, der erklärt, wie Sie das sicher tun. Wenn die Klasse nicht für Erweiterungen ausgelegt ist, sollten sie die Klasse 'User' final oder die Methode 'isAccountNonLocked' static/final/abstract/empty machen oder die zugelassene Annotation für die Methode hinzufügen. +[ERROR] src/main/java/org/psesquared/server/model/User.java:[110,5] (design) DesignForExtension: Klasse 'User' sieht wie zur Erweiterung entworfen aus (kann subclassed werden), aber die Methode 'isCredentialsNonExpired' besitzt keinen Javadoc-Kommentar, der erklärt, wie Sie das sicher tun. Wenn die Klasse nicht für Erweiterungen ausgelegt ist, sollten sie die Klasse 'User' final oder die Methode 'isCredentialsNonExpired' static/final/abstract/empty machen oder die zugelassene Annotation für die Methode hinzufügen. +[ERROR] src/main/java/org/psesquared/server/model/Action.java:[5] (javadoc) JavadocStyle: Der erste Satz sollte mit einem Punkt enden. +[ERROR] src/main/java/org/psesquared/server/model/Action.java:[7] (regexp) RegexpSingleline: Line has trailing spaces. +[ERROR] src/main/java/org/psesquared/server/model/Action.java:[10] (regexp) RegexpSingleline: Line has trailing spaces. +[ERROR] src/main/java/org/psesquared/server/model/Action.java:[11,5] (javadoc) JavadocVariable: Es fehlt ein Javadoc-Kommentar. +[ERROR] src/main/java/org/psesquared/server/model/Action.java:[12,5] (javadoc) JavadocVariable: Es fehlt ein Javadoc-Kommentar. +[ERROR] src/main/java/org/psesquared/server/model/Action.java:[13,5] (javadoc) JavadocVariable: Es fehlt ein Javadoc-Kommentar. +[ERROR] src/main/java/org/psesquared/server/model/Action.java:[14,5] (javadoc) JavadocVariable: Es fehlt ein Javadoc-Kommentar. +[ERROR] src/main/java/org/psesquared/server/model/Action.java:[15,5] (javadoc) JavadocVariable: Es fehlt ein Javadoc-Kommentar. +[ERROR] src/main/java/org/psesquared/server/model/Action.java:[17,5] (javadoc) MissingJavadocMethod: Es fehlt ein Javadoc-Kommentar. +[ERROR] src/main/java/org/psesquared/server/model/Episode.java:[19] (javadoc) JavadocStyle: Der erste Satz sollte mit einem Punkt enden. +[ERROR] src/main/java/org/psesquared/server/model/Episode.java:[30] (javadoc) JavadocStyle: Der erste Satz sollte mit einem Punkt enden. +[ERROR] src/main/java/org/psesquared/server/model/Episode.java:[38] (javadoc) JavadocStyle: Der erste Satz sollte mit einem Punkt enden. +[ERROR] src/main/java/org/psesquared/server/model/Episode.java:[44] (javadoc) JavadocStyle: Der erste Satz sollte mit einem Punkt enden. +[ERROR] src/main/java/org/psesquared/server/model/Episode.java:[50] (javadoc) JavadocStyle: Der erste Satz sollte mit einem Punkt enden. +[ERROR] src/main/java/org/psesquared/server/model/Episode.java:[56] (javadoc) JavadocStyle: Der erste Satz sollte mit einem Punkt enden. +[ERROR] src/main/java/org/psesquared/server/model/Episode.java:[62] (javadoc) JavadocStyle: Der erste Satz sollte mit einem Punkt enden. +[ERROR] src/main/java/org/psesquared/server/model/Episode.java:[68] (javadoc) JavadocStyle: Der erste Satz sollte mit einem Punkt enden. +[ERROR] src/main/java/org/psesquared/server/util/UpdateURLsWrapper.java:[1] (javadoc) JavadocPackage: Es fehlt eine package-info.java. +[ERROR] src/main/java/org/psesquared/server/util/UpdateURLsWrapper.java:[10] (javadoc) JavadocStyle: Der erste Satz sollte mit einem Punkt enden. +[ERROR] src/main/java/org/psesquared/server/util/UpdateURLsWrapper.java:[15,5] (javadoc) JavadocVariable: Es fehlt ein Javadoc-Kommentar. +[ERROR] src/main/java/org/psesquared/server/util/UpdateURLsWrapper.java:[16,5] (javadoc) JavadocVariable: Es fehlt ein Javadoc-Kommentar. +[ERROR] src/main/java/org/psesquared/server/util/UpdateURLsWrapper.java:[18] (javadoc) JavadocStyle: Der erste Satz sollte mit einem Punkt enden. +[ERROR] src/main/java/org/psesquared/server/util/UpdateURLsWrapper.java:[26,5] (javadoc) MissingJavadocMethod: Es fehlt ein Javadoc-Kommentar. +[ERROR] src/main/java/org/psesquared/server/util/UpdateURLsWrapper.java:[26,5] (design) DesignForExtension: Klasse 'UpdateURLsWrapper' sieht wie zur Erweiterung entworfen aus (kann subclassed werden), aber die Methode 'getTimestamp' besitzt keinen Javadoc-Kommentar, der erklärt, wie Sie das sicher tun. Wenn die Klasse nicht für Erweiterungen ausgelegt ist, sollten sie die Klasse 'UpdateURLsWrapper' final oder die Methode 'getTimestamp' static/final/abstract/empty machen oder die zugelassene Annotation für die Methode hinzufügen. +[ERROR] src/main/java/org/psesquared/server/util/UpdateURLsWrapper.java:[30,5] (javadoc) MissingJavadocMethod: Es fehlt ein Javadoc-Kommentar. +[ERROR] src/main/java/org/psesquared/server/util/UpdateURLsWrapper.java:[30,5] (design) DesignForExtension: Klasse 'UpdateURLsWrapper' sieht wie zur Erweiterung entworfen aus (kann subclassed werden), aber die Methode 'getUpdateURLs' besitzt keinen Javadoc-Kommentar, der erklärt, wie Sie das sicher tun. Wenn die Klasse nicht für Erweiterungen ausgelegt ist, sollten sie die Klasse 'UpdateURLsWrapper' final oder die Methode 'getUpdateURLs' static/final/abstract/empty machen oder die zugelassene Annotation für die Methode hinzufügen. +[ERROR] src/main/java/org/psesquared/server/util/Scheduler.java:[10] (javadoc) JavadocStyle: Der erste Satz sollte mit einem Punkt enden. +[ERROR] src/main/java/org/psesquared/server/util/Scheduler.java:[16,5] (javadoc) JavadocVariable: Es fehlt ein Javadoc-Kommentar. +[ERROR] src/main/java/org/psesquared/server/util/Scheduler.java:[19] (sizes) LineLength: Zeile ist 137 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/util/Scheduler.java:[24] (sizes) LineLength: Zeile ist 126 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/util/Scheduler.java:[24,112] (coding) MagicNumber: Die magische Zahl '24' sollte als Konstante definiert werden. +[ERROR] src/main/java/org/psesquared/server/util/Scheduler.java:[24,117] (coding) MagicNumber: Die magische Zahl '60' sollte als Konstante definiert werden. +[ERROR] src/main/java/org/psesquared/server/util/Scheduler.java:[24,122] (coding) MagicNumber: Die magische Zahl '60' sollte als Konstante definiert werden. +[ERROR] src/main/java/org/psesquared/server/util/RSSParser.java:[38,5] (javadoc) JavadocVariable: Es fehlt ein Javadoc-Kommentar. +[ERROR] src/main/java/org/psesquared/server/util/RSSParser.java:[40,5] (javadoc) JavadocVariable: Es fehlt ein Javadoc-Kommentar. +[ERROR] src/main/java/org/psesquared/server/util/RSSParser.java:[41,5] (javadoc) JavadocVariable: Es fehlt ein Javadoc-Kommentar. +[ERROR] src/main/java/org/psesquared/server/util/RSSParser.java:[43,5] (javadoc) MissingJavadocMethod: Es fehlt ein Javadoc-Kommentar. +[ERROR] src/main/java/org/psesquared/server/util/RSSParser.java:[43,5] (design) DesignForExtension: Klasse 'RSSParser' sieht wie zur Erweiterung entworfen aus (kann subclassed werden), aber die Methode 'validate' besitzt keinen Javadoc-Kommentar, der erklärt, wie Sie das sicher tun. Wenn die Klasse nicht für Erweiterungen ausgelegt ist, sollten sie die Klasse 'RSSParser' final oder die Methode 'validate' static/final/abstract/empty machen oder die zugelassene Annotation für die Methode hinzufügen. +[ERROR] src/main/java/org/psesquared/server/util/RSSParser.java:[44,5] (javadoc) InvalidJavadocPosition: Javadoc-Kommentar ist an der falschen Stelle platziert. +[ERROR] src/main/java/org/psesquared/server/util/RSSParser.java:[48] (sizes) LineLength: Zeile ist 84 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/util/RSSParser.java:[51] (regexp) RegexpSingleline: Line has trailing spaces. +[ERROR] src/main/java/org/psesquared/server/util/RSSParser.java:[54,26] (misc) FinalParameters: Der Parameter subscription sollte als 'final' deklariert sein. +[ERROR] src/main/java/org/psesquared/server/util/RSSParser.java:[66] (sizes) LineLength: Zeile ist 101 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/util/RSSParser.java:[73] (sizes) LineLength: Zeile ist 89 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/util/RSSParser.java:[87] (sizes) LineLength: Zeile ist 83 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/util/RSSParser.java:[87,56] (misc) FinalParameters: Der Parameter subscription sollte als 'final' deklariert sein. +[ERROR] src/main/java/org/psesquared/server/util/RSSParser.java:[126] (sizes) LineLength: Zeile ist 82 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/util/RSSParser.java:[126,34] (misc) FinalParameters: Der Parameter syndEntry sollte als 'final' deklariert sein. +[ERROR] src/main/java/org/psesquared/server/util/RSSParser.java:[126,55] (misc) FinalParameters: Der Parameter subscription sollte als 'final' deklariert sein. +[ERROR] src/main/java/org/psesquared/server/util/RSSParser.java:[158] (sizes) LineLength: Zeile ist 114 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/util/RSSParser.java:[161] (sizes) LineLength: Zeile ist 95 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/util/RSSParser.java:[161,45] (misc) FinalParameters: Der Parameter episodeURL sollte als 'final' deklariert sein. +[ERROR] src/main/java/org/psesquared/server/util/RSSParser.java:[161,64] (misc) FinalParameters: Der Parameter episodes sollte als 'final' deklariert sein. +[ERROR] src/main/java/org/psesquared/server/util/RSSParser.java:[168,5] (design) DesignForExtension: Klasse 'RSSParser' sieht wie zur Erweiterung entworfen aus (kann subclassed werden), aber die Methode 'saveEpisodes' besitzt keinen Javadoc-Kommentar, der erklärt, wie Sie das sicher tun. Wenn die Klasse nicht für Erweiterungen ausgelegt ist, sollten sie die Klasse 'RSSParser' final oder die Methode 'saveEpisodes' static/final/abstract/empty machen oder die zugelassene Annotation für die Methode hinzufügen. +[ERROR] src/main/java/org/psesquared/server/util/RSSParser.java:[169,33] (misc) FinalParameters: Der Parameter episodes sollte als 'final' deklariert sein. +[ERROR] src/main/java/org/psesquared/server/util/RSSParser.java:[173,5] (design) DesignForExtension: Klasse 'RSSParser' sieht wie zur Erweiterung entworfen aus (kann subclassed werden), aber die Methode 'deleteEpisodes' besitzt keinen Javadoc-Kommentar, der erklärt, wie Sie das sicher tun. Wenn die Klasse nicht für Erweiterungen ausgelegt ist, sollten sie die Klasse 'RSSParser' final oder die Methode 'deleteEpisodes' static/final/abstract/empty machen oder die zugelassene Annotation für die Methode hinzufügen. +[ERROR] src/main/java/org/psesquared/server/util/RSSParser.java:[174,35] (misc) FinalParameters: Der Parameter episodes sollte als 'final' deklariert sein. +[ERROR] src/main/java/org/psesquared/server/util/RSSParser.java:[179,5] (design) DesignForExtension: Klasse 'RSSParser' sieht wie zur Erweiterung entworfen aus (kann subclassed werden), aber die Methode 'saveSubscription' besitzt keinen Javadoc-Kommentar, der erklärt, wie Sie das sicher tun. Wenn die Klasse nicht für Erweiterungen ausgelegt ist, sollten sie die Klasse 'RSSParser' final oder die Methode 'saveSubscription' static/final/abstract/empty machen oder die zugelassene Annotation für die Methode hinzufügen. +[ERROR] src/main/java/org/psesquared/server/util/RSSParser.java:[180,37] (misc) FinalParameters: Der Parameter subscription sollte als 'final' deklariert sein. +[ERROR] src/main/java/org/psesquared/server/util/RSSParser.java:[184,5] (design) DesignForExtension: Klasse 'RSSParser' sieht wie zur Erweiterung entworfen aus (kann subclassed werden), aber die Methode 'deleteSubscription' besitzt keinen Javadoc-Kommentar, der erklärt, wie Sie das sicher tun. Wenn die Klasse nicht für Erweiterungen ausgelegt ist, sollten sie die Klasse 'RSSParser' final oder die Methode 'deleteSubscription' static/final/abstract/empty machen oder die zugelassene Annotation für die Methode hinzufügen. +[ERROR] src/main/java/org/psesquared/server/util/RSSParser.java:[185,39] (misc) FinalParameters: Der Parameter subscription sollte als 'final' deklariert sein. +[ERROR] src/main/java/org/psesquared/server/util/RSSParser.java:[190,43] (misc) FinalParameters: Der Parameter time sollte als 'final' deklariert sein. +[ERROR] src/main/java/org/psesquared/server/util/RSSParser.java:[208,31] (misc) ArrayTypeStyle: Array-Klammern an ungültiger Position. +[ERROR] src/main/java/org/psesquared/server/authentication_api/controller/AuthenticationResponse.java:[1] (javadoc) JavadocPackage: Es fehlt eine package-info.java. +[ERROR] src/main/java/org/psesquared/server/authentication_api/controller/AuthenticationResponse.java:[7,1] (javadoc) JavadocType: Im Javadoc des Typs fehlt das Tag @param token. +[ERROR] src/main/java/org/psesquared/server/authentication_api/controller/AuthenticationResponse.java:[7,52] (whitespace) WhitespaceAround: Nach '{' fehlt ein Leerzeichen. +[ERROR] src/main/java/org/psesquared/server/authentication_api/controller/AuthenticationResponse.java:[7,53] (whitespace) WhitespaceAround: Vor '}' fehlt ein Leerzeichen. +[ERROR] src/main/java/org/psesquared/server/authentication_api/controller/AuthenticationController.java:[9,47] (imports) AvoidStarImport: Stern-Importe der Form '.*' sollten vermieden werden - org.springframework.web.bind.annotation.*. +[ERROR] src/main/java/org/psesquared/server/authentication_api/controller/AuthenticationController.java:[15] (sizes) LineLength: Zeile ist 98 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/authentication_api/controller/AuthenticationController.java:[23,5] (javadoc) JavadocVariable: Es fehlt ein Javadoc-Kommentar. +[ERROR] src/main/java/org/psesquared/server/authentication_api/controller/AuthenticationController.java:[25,5] (javadoc) JavadocVariable: Es fehlt ein Javadoc-Kommentar. +[ERROR] src/main/java/org/psesquared/server/authentication_api/controller/AuthenticationController.java:[26,5] (javadoc) JavadocVariable: Es fehlt ein Javadoc-Kommentar. +[ERROR] src/main/java/org/psesquared/server/authentication_api/controller/AuthenticationController.java:[29] (sizes) LineLength: Zeile ist 117 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/authentication_api/controller/AuthenticationController.java:[30] (sizes) LineLength: Zeile ist 109 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/authentication_api/controller/AuthenticationController.java:[31] (sizes) LineLength: Zeile ist 99 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/authentication_api/controller/AuthenticationController.java:[34] (sizes) LineLength: Zeile ist 91 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/authentication_api/controller/AuthenticationController.java:[41,36] (misc) FinalParameters: Der Parameter userInfo sollte als 'final' deklariert sein. +[ERROR] src/main/java/org/psesquared/server/authentication_api/controller/AuthenticationController.java:[46] (sizes) LineLength: Zeile ist 118 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/authentication_api/controller/AuthenticationController.java:[47] (sizes) LineLength: Zeile ist 101 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/authentication_api/controller/AuthenticationController.java:[51] (sizes) LineLength: Zeile ist 99 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/authentication_api/controller/AuthenticationController.java:[56] (regexp) RegexpSingleline: Line has trailing spaces. +[ERROR] src/main/java/org/psesquared/server/authentication_api/controller/AuthenticationController.java:[60,42] (misc) FinalParameters: Der Parameter username sollte als 'final' deklariert sein. +[ERROR] src/main/java/org/psesquared/server/authentication_api/controller/AuthenticationController.java:[61,42] (misc) FinalParameters: Der Parameter token sollte als 'final' deklariert sein. +[ERROR] src/main/java/org/psesquared/server/authentication_api/controller/AuthenticationController.java:[62] (sizes) LineLength: Zeile ist 81 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/authentication_api/controller/AuthenticationController.java:[62,42] (misc) FinalParameters: Der Parameter response sollte als 'final' deklariert sein. +[ERROR] src/main/java/org/psesquared/server/authentication_api/controller/AuthenticationController.java:[63] (sizes) LineLength: Zeile ist 86 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/authentication_api/controller/AuthenticationController.java:[69] (sizes) LineLength: Zeile ist 112 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/authentication_api/controller/AuthenticationController.java:[71] (sizes) LineLength: Zeile ist 85 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/authentication_api/controller/AuthenticationController.java:[74] (sizes) LineLength: Zeile ist 89 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/authentication_api/controller/AuthenticationController.java:[81,29] (misc) FinalParameters: Der Parameter username sollte als 'final' deklariert sein. +[ERROR] src/main/java/org/psesquared/server/authentication_api/controller/AuthenticationController.java:[82,29] (misc) FinalParameters: Der Parameter response sollte als 'final' deklariert sein. +[ERROR] src/main/java/org/psesquared/server/authentication_api/controller/AuthenticationController.java:[87] (sizes) LineLength: Zeile ist 120 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/authentication_api/controller/AuthenticationController.java:[89] (sizes) LineLength: Zeile ist 85 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/authentication_api/controller/AuthenticationController.java:[92] (sizes) LineLength: Zeile ist 90 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/authentication_api/controller/AuthenticationController.java:[99,30] (misc) FinalParameters: Der Parameter username sollte als 'final' deklariert sein. +[ERROR] src/main/java/org/psesquared/server/authentication_api/controller/AuthenticationController.java:[100,30] (misc) FinalParameters: Der Parameter response sollte als 'final' deklariert sein. +[ERROR] src/main/java/org/psesquared/server/authentication_api/controller/AuthenticationController.java:[100,68] (whitespace) WhitespaceAround: Vor '{' fehlt ein Leerzeichen. +[ERROR] src/main/java/org/psesquared/server/authentication_api/controller/AuthenticationController.java:[105] (sizes) LineLength: Zeile ist 119 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/authentication_api/controller/AuthenticationController.java:[108] (sizes) LineLength: Zeile ist 83 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/authentication_api/controller/AuthenticationController.java:[115,38] (misc) FinalParameters: Der Parameter email sollte als 'final' deklariert sein. +[ERROR] src/main/java/org/psesquared/server/authentication_api/controller/AuthenticationController.java:[119,5] (javadoc) MissingJavadocMethod: Es fehlt ein Javadoc-Kommentar. +[ERROR] src/main/java/org/psesquared/server/authentication_api/controller/AuthenticationController.java:[119,5] (design) DesignForExtension: Klasse 'AuthenticationController' sieht wie zur Erweiterung entworfen aus (kann subclassed werden), aber die Methode 'resetPassword' besitzt keinen Javadoc-Kommentar, der erklärt, wie Sie das sicher tun. Wenn die Klasse nicht für Erweiterungen ausgelegt ist, sollten sie die Klasse 'AuthenticationController' final oder die Methode 'resetPassword' static/final/abstract/empty machen oder die zugelassene Annotation für die Methode hinzufügen. +[ERROR] src/main/java/org/psesquared/server/authentication_api/controller/AuthenticationController.java:[120,37] (misc) FinalParameters: Der Parameter username sollte als 'final' deklariert sein. +[ERROR] src/main/java/org/psesquared/server/authentication_api/controller/AuthenticationController.java:[121,37] (misc) FinalParameters: Der Parameter token sollte als 'final' deklariert sein. +[ERROR] src/main/java/org/psesquared/server/authentication_api/controller/AuthenticationController.java:[122,37] (misc) FinalParameters: Der Parameter requestBody sollte als 'final' deklariert sein. +[ERROR] src/main/java/org/psesquared/server/authentication_api/controller/AuthenticationController.java:[123] (sizes) LineLength: Zeile ist 81 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/authentication_api/controller/AuthenticationController.java:[126,5] (javadoc) MissingJavadocMethod: Es fehlt ein Javadoc-Kommentar. +[ERROR] src/main/java/org/psesquared/server/authentication_api/controller/AuthenticationController.java:[126,5] (design) DesignForExtension: Klasse 'AuthenticationController' sieht wie zur Erweiterung entworfen aus (kann subclassed werden), aber die Methode 'changePassword' besitzt keinen Javadoc-Kommentar, der erklärt, wie Sie das sicher tun. Wenn die Klasse nicht für Erweiterungen ausgelegt ist, sollten sie die Klasse 'AuthenticationController' final oder die Methode 'changePassword' static/final/abstract/empty machen oder die zugelassene Annotation für die Methode hinzufügen. +[ERROR] src/main/java/org/psesquared/server/authentication_api/controller/AuthenticationController.java:[127,38] (misc) FinalParameters: Der Parameter username sollte als 'final' deklariert sein. +[ERROR] src/main/java/org/psesquared/server/authentication_api/controller/AuthenticationController.java:[128] (sizes) LineLength: Zeile ist 86 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/authentication_api/controller/AuthenticationController.java:[128,38] (misc) FinalParameters: Der Parameter requestBody sollte als 'final' deklariert sein. +[ERROR] src/main/java/org/psesquared/server/authentication_api/controller/AuthenticationController.java:[132,5] (javadoc) MissingJavadocMethod: Es fehlt ein Javadoc-Kommentar. +[ERROR] src/main/java/org/psesquared/server/authentication_api/controller/AuthenticationController.java:[132,5] (design) DesignForExtension: Klasse 'AuthenticationController' sieht wie zur Erweiterung entworfen aus (kann subclassed werden), aber die Methode 'deleteUser' besitzt keinen Javadoc-Kommentar, der erklärt, wie Sie das sicher tun. Wenn die Klasse nicht für Erweiterungen ausgelegt ist, sollten sie die Klasse 'AuthenticationController' final oder die Methode 'deleteUser' static/final/abstract/empty machen oder die zugelassene Annotation für die Methode hinzufügen. +[ERROR] src/main/java/org/psesquared/server/authentication_api/controller/AuthenticationController.java:[133,34] (misc) FinalParameters: Der Parameter username sollte als 'final' deklariert sein. +[ERROR] src/main/java/org/psesquared/server/authentication_api/controller/AuthenticationController.java:[134,34] (misc) FinalParameters: Der Parameter requestBody sollte als 'final' deklariert sein. +[ERROR] src/main/java/org/psesquared/server/authentication_api/controller/AuthenticationController.java:[138,5] (javadoc) MissingJavadocMethod: Es fehlt ein Javadoc-Kommentar. +[ERROR] src/main/java/org/psesquared/server/authentication_api/controller/AuthenticationController.java:[138,5] (design) DesignForExtension: Klasse 'AuthenticationController' sieht wie zur Erweiterung entworfen aus (kann subclassed werden), aber die Methode 'getDeviceList' besitzt keinen Javadoc-Kommentar, der erklärt, wie Sie das sicher tun. Wenn die Klasse nicht für Erweiterungen ausgelegt ist, sollten sie die Klasse 'AuthenticationController' final oder die Methode 'getDeviceList' static/final/abstract/empty machen oder die zugelassene Annotation für die Methode hinzufügen. +[ERROR] src/main/java/org/psesquared/server/authentication_api/controller/AuthenticationController.java:[139] (sizes) LineLength: Zeile ist 91 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/authentication_api/controller/AuthenticationController.java:[139,62] (misc) FinalParameters: Der Parameter username sollte als 'final' deklariert sein. +[ERROR] src/main/java/org/psesquared/server/authentication_api/controller/AuthenticationController.java:[140] (sizes) LineLength: Zeile ist 101 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/authentication_api/controller/AuthenticationController.java:[140,62] (misc) FinalParameters: Der Parameter response sollte als 'final' deklariert sein. +[ERROR] src/main/java/org/psesquared/server/authentication_api/controller/AuthenticationController.java:[142] (sizes) LineLength: Zeile ist 107 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/authentication_api/controller/ForgotPasswordRequest.java:[5] (javadoc) JavadocStyle: Der erste Satz sollte mit einem Punkt enden. +[ERROR] src/main/java/org/psesquared/server/authentication_api/controller/ForgotPasswordRequest.java:[8] (sizes) LineLength: Zeile ist 83 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/authentication_api/controller/ForgotPasswordRequest.java:[8,1] (javadoc) JavadocType: Im Javadoc des Typs fehlt das Tag @param email. +[ERROR] src/main/java/org/psesquared/server/authentication_api/controller/ForgotPasswordRequest.java:[8,82] (whitespace) WhitespaceAround: Nach '{' fehlt ein Leerzeichen. +[ERROR] src/main/java/org/psesquared/server/authentication_api/controller/ForgotPasswordRequest.java:[8,83] (whitespace) WhitespaceAround: Vor '}' fehlt ein Leerzeichen. +[ERROR] src/main/java/org/psesquared/server/authentication_api/controller/DeviceWrapper.java:[3,8] (imports) UnusedImports: Nicht verwendeter Import - jakarta.servlet.http.HttpServletResponse. +[ERROR] src/main/java/org/psesquared/server/authentication_api/controller/DeviceWrapper.java:[14] (sizes) LineLength: Zeile ist 88 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/authentication_api/controller/DeviceWrapper.java:[16,5] (javadoc) JavadocVariable: Es fehlt ein Javadoc-Kommentar. +[ERROR] src/main/java/org/psesquared/server/authentication_api/controller/DeviceWrapper.java:[17,5] (javadoc) JavadocVariable: Es fehlt ein Javadoc-Kommentar. +[ERROR] src/main/java/org/psesquared/server/authentication_api/controller/DeviceWrapper.java:[18,5] (javadoc) JavadocVariable: Es fehlt ein Javadoc-Kommentar. +[ERROR] src/main/java/org/psesquared/server/authentication_api/controller/DeviceWrapper.java:[19,5] (javadoc) JavadocVariable: Es fehlt ein Javadoc-Kommentar. +[ERROR] src/main/java/org/psesquared/server/authentication_api/controller/DeviceWrapper.java:[21,5] (javadoc) MissingJavadocMethod: Es fehlt ein Javadoc-Kommentar. +[ERROR] src/main/java/org/psesquared/server/authentication_api/controller/ChangePasswordRequest.java:[5] (javadoc) JavadocStyle: Der erste Satz sollte mit einem Punkt enden. +[ERROR] src/main/java/org/psesquared/server/authentication_api/controller/ChangePasswordRequest.java:[9] (sizes) LineLength: Zeile ist 106 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/authentication_api/controller/ChangePasswordRequest.java:[9,1] (javadoc) JavadocType: Im Javadoc des Typs fehlt das Tag @param newPassword. +[ERROR] src/main/java/org/psesquared/server/authentication_api/controller/ChangePasswordRequest.java:[9,1] (javadoc) JavadocType: Im Javadoc des Typs fehlt das Tag @param oldPassword. +[ERROR] src/main/java/org/psesquared/server/authentication_api/controller/ChangePasswordRequest.java:[10] (sizes) LineLength: Zeile ist 84 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/authentication_api/controller/UserInfoRequest.java:[9,1] (javadoc) JavadocType: Im Javadoc des Typs fehlt das Tag @param email. +[ERROR] src/main/java/org/psesquared/server/authentication_api/controller/UserInfoRequest.java:[9,1] (javadoc) JavadocType: Im Javadoc des Typs fehlt das Tag @param password. +[ERROR] src/main/java/org/psesquared/server/authentication_api/controller/UserInfoRequest.java:[9,1] (javadoc) JavadocType: Im Javadoc des Typs fehlt das Tag @param username. +[ERROR] src/main/java/org/psesquared/server/authentication_api/controller/UserInfoRequest.java:[11,79] (whitespace) WhitespaceAround: Nach '{' fehlt ein Leerzeichen. +[ERROR] src/main/java/org/psesquared/server/authentication_api/controller/UserInfoRequest.java:[11,80] (whitespace) WhitespaceAround: Vor '}' fehlt ein Leerzeichen. +[ERROR] src/main/java/org/psesquared/server/authentication_api/controller/PasswordRequest.java:[9,1] (javadoc) JavadocType: Im Javadoc des Typs fehlt das Tag @param password. +[ERROR] src/main/java/org/psesquared/server/authentication_api/controller/PasswordRequest.java:[9,79] (whitespace) WhitespaceAround: Nach '{' fehlt ein Leerzeichen. +[ERROR] src/main/java/org/psesquared/server/authentication_api/controller/PasswordRequest.java:[9,80] (whitespace) WhitespaceAround: Vor '}' fehlt ein Leerzeichen. +[ERROR] src/main/java/org/psesquared/server/authentication_api/service/AuthenticationService.java:[1] (javadoc) JavadocPackage: Es fehlt eine package-info.java. +[ERROR] src/main/java/org/psesquared/server/authentication_api/service/AuthenticationService.java:[6,59] (imports) AvoidStarImport: Stern-Importe der Form '.*' sollten vermieden werden - org.psesquared.server.authentication_api.controller.*. +[ERROR] src/main/java/org/psesquared/server/authentication_api/service/AuthenticationService.java:[25,5] (javadoc) JavadocVariable: Es fehlt ein Javadoc-Kommentar. +[ERROR] src/main/java/org/psesquared/server/authentication_api/service/AuthenticationService.java:[26,5] (javadoc) JavadocVariable: Es fehlt ein Javadoc-Kommentar. +[ERROR] src/main/java/org/psesquared/server/authentication_api/service/AuthenticationService.java:[27,5] (javadoc) JavadocVariable: Es fehlt ein Javadoc-Kommentar. +[ERROR] src/main/java/org/psesquared/server/authentication_api/service/AuthenticationService.java:[28,5] (javadoc) JavadocVariable: Es fehlt ein Javadoc-Kommentar. +[ERROR] src/main/java/org/psesquared/server/authentication_api/service/AuthenticationService.java:[29,5] (javadoc) JavadocVariable: Es fehlt ein Javadoc-Kommentar. +[ERROR] src/main/java/org/psesquared/server/authentication_api/service/AuthenticationService.java:[30,5] (javadoc) JavadocVariable: Es fehlt ein Javadoc-Kommentar. +[ERROR] src/main/java/org/psesquared/server/authentication_api/service/AuthenticationService.java:[32,5] (javadoc) JavadocVariable: Es fehlt ein Javadoc-Kommentar. +[ERROR] src/main/java/org/psesquared/server/authentication_api/service/AuthenticationService.java:[33,5] (javadoc) JavadocVariable: Es fehlt ein Javadoc-Kommentar. +[ERROR] src/main/java/org/psesquared/server/authentication_api/service/AuthenticationService.java:[34,5] (javadoc) JavadocVariable: Es fehlt ein Javadoc-Kommentar. +[ERROR] src/main/java/org/psesquared/server/authentication_api/service/AuthenticationService.java:[35,5] (javadoc) JavadocVariable: Es fehlt ein Javadoc-Kommentar. +[ERROR] src/main/java/org/psesquared/server/authentication_api/service/AuthenticationService.java:[36,5] (javadoc) JavadocVariable: Es fehlt ein Javadoc-Kommentar. +[ERROR] src/main/java/org/psesquared/server/authentication_api/service/AuthenticationService.java:[37,5] (javadoc) JavadocVariable: Es fehlt ein Javadoc-Kommentar. +[ERROR] src/main/java/org/psesquared/server/authentication_api/service/AuthenticationService.java:[39,5] (javadoc) MissingJavadocMethod: Es fehlt ein Javadoc-Kommentar. +[ERROR] src/main/java/org/psesquared/server/authentication_api/service/AuthenticationService.java:[39,5] (design) DesignForExtension: Klasse 'AuthenticationService' sieht wie zur Erweiterung entworfen aus (kann subclassed werden), aber die Methode 'registerUser' besitzt keinen Javadoc-Kommentar, der erklärt, wie Sie das sicher tun. Wenn die Klasse nicht für Erweiterungen ausgelegt ist, sollten sie die Klasse 'AuthenticationService' final oder die Methode 'registerUser' static/final/abstract/empty machen oder die zugelassene Annotation für die Methode hinzufügen. +[ERROR] src/main/java/org/psesquared/server/authentication_api/service/AuthenticationService.java:[39,36] (misc) FinalParameters: Der Parameter userInfo sollte als 'final' deklariert sein. +[ERROR] src/main/java/org/psesquared/server/authentication_api/service/AuthenticationService.java:[55] (sizes) LineLength: Zeile ist 91 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/authentication_api/service/AuthenticationService.java:[56] (sizes) LineLength: Zeile ist 85 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/authentication_api/service/AuthenticationService.java:[66] (sizes) LineLength: Zeile ist 81 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/authentication_api/service/AuthenticationService.java:[75,5] (javadoc) MissingJavadocMethod: Es fehlt ein Javadoc-Kommentar. +[ERROR] src/main/java/org/psesquared/server/authentication_api/service/AuthenticationService.java:[75,5] (design) DesignForExtension: Klasse 'AuthenticationService' sieht wie zur Erweiterung entworfen aus (kann subclassed werden), aber die Methode 'verifyRegistration' besitzt keinen Javadoc-Kommentar, der erklärt, wie Sie das sicher tun. Wenn die Klasse nicht für Erweiterungen ausgelegt ist, sollten sie die Klasse 'AuthenticationService' final oder die Methode 'verifyRegistration' static/final/abstract/empty machen oder die zugelassene Annotation für die Methode hinzufügen. +[ERROR] src/main/java/org/psesquared/server/authentication_api/service/AuthenticationService.java:[75,42] (misc) FinalParameters: Der Parameter username sollte als 'final' deklariert sein. +[ERROR] src/main/java/org/psesquared/server/authentication_api/service/AuthenticationService.java:[75,59] (misc) FinalParameters: Der Parameter token sollte als 'final' deklariert sein. +[ERROR] src/main/java/org/psesquared/server/authentication_api/service/AuthenticationService.java:[95,5] (javadoc) MissingJavadocMethod: Es fehlt ein Javadoc-Kommentar. +[ERROR] src/main/java/org/psesquared/server/authentication_api/service/AuthenticationService.java:[95,5] (design) DesignForExtension: Klasse 'AuthenticationService' sieht wie zur Erweiterung entworfen aus (kann subclassed werden), aber die Methode 'login' besitzt keinen Javadoc-Kommentar, der erklärt, wie Sie das sicher tun. Wenn die Klasse nicht für Erweiterungen ausgelegt ist, sollten sie die Klasse 'AuthenticationService' final oder die Methode 'login' static/final/abstract/empty machen oder die zugelassene Annotation für die Methode hinzufügen. +[ERROR] src/main/java/org/psesquared/server/authentication_api/service/AuthenticationService.java:[95,29] (misc) FinalParameters: Der Parameter username sollte als 'final' deklariert sein. +[ERROR] src/main/java/org/psesquared/server/authentication_api/service/AuthenticationService.java:[96,36] (misc) FinalParameters: Der Parameter response sollte als 'final' deklariert sein. +[ERROR] src/main/java/org/psesquared/server/authentication_api/service/AuthenticationService.java:[110,5] (javadoc) MissingJavadocMethod: Es fehlt ein Javadoc-Kommentar. +[ERROR] src/main/java/org/psesquared/server/authentication_api/service/AuthenticationService.java:[110,5] (design) DesignForExtension: Klasse 'AuthenticationService' sieht wie zur Erweiterung entworfen aus (kann subclassed werden), aber die Methode 'logout' besitzt keinen Javadoc-Kommentar, der erklärt, wie Sie das sicher tun. Wenn die Klasse nicht für Erweiterungen ausgelegt ist, sollten sie die Klasse 'AuthenticationService' final oder die Methode 'logout' static/final/abstract/empty machen oder die zugelassene Annotation für die Methode hinzufügen. +[ERROR] src/main/java/org/psesquared/server/authentication_api/service/AuthenticationService.java:[110,30] (misc) FinalParameters: Der Parameter username sollte als 'final' deklariert sein. +[ERROR] src/main/java/org/psesquared/server/authentication_api/service/AuthenticationService.java:[111,30] (misc) FinalParameters: Der Parameter response sollte als 'final' deklariert sein. +[ERROR] src/main/java/org/psesquared/server/authentication_api/service/AuthenticationService.java:[112,58] (whitespace) WhitespaceAround: Vor '{' fehlt ein Leerzeichen. +[ERROR] src/main/java/org/psesquared/server/authentication_api/service/AuthenticationService.java:[122,5] (javadoc) MissingJavadocMethod: Es fehlt ein Javadoc-Kommentar. +[ERROR] src/main/java/org/psesquared/server/authentication_api/service/AuthenticationService.java:[122,5] (design) DesignForExtension: Klasse 'AuthenticationService' sieht wie zur Erweiterung entworfen aus (kann subclassed werden), aber die Methode 'forgotPassword' besitzt keinen Javadoc-Kommentar, der erklärt, wie Sie das sicher tun. Wenn die Klasse nicht für Erweiterungen ausgelegt ist, sollten sie die Klasse 'AuthenticationService' final oder die Methode 'forgotPassword' static/final/abstract/empty machen oder die zugelassene Annotation für die Methode hinzufügen. +[ERROR] src/main/java/org/psesquared/server/authentication_api/service/AuthenticationService.java:[122,38] (misc) FinalParameters: Der Parameter email sollte als 'final' deklariert sein. +[ERROR] src/main/java/org/psesquared/server/authentication_api/service/AuthenticationService.java:[124] (sizes) LineLength: Zeile ist 95 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/authentication_api/service/AuthenticationService.java:[128,11] (whitespace) WhitespaceAround: Nach 'catch' fehlt ein Leerzeichen. +[ERROR] src/main/java/org/psesquared/server/authentication_api/service/AuthenticationService.java:[133,5] (javadoc) MissingJavadocMethod: Es fehlt ein Javadoc-Kommentar. +[ERROR] src/main/java/org/psesquared/server/authentication_api/service/AuthenticationService.java:[133,5] (design) DesignForExtension: Klasse 'AuthenticationService' sieht wie zur Erweiterung entworfen aus (kann subclassed werden), aber die Methode 'resetPassword' besitzt keinen Javadoc-Kommentar, der erklärt, wie Sie das sicher tun. Wenn die Klasse nicht für Erweiterungen ausgelegt ist, sollten sie die Klasse 'AuthenticationService' final oder die Methode 'resetPassword' static/final/abstract/empty machen oder die zugelassene Annotation für die Methode hinzufügen. +[ERROR] src/main/java/org/psesquared/server/authentication_api/service/AuthenticationService.java:[133,37] (misc) FinalParameters: Der Parameter username sollte als 'final' deklariert sein. +[ERROR] src/main/java/org/psesquared/server/authentication_api/service/AuthenticationService.java:[134,31] (misc) FinalParameters: Der Parameter token sollte als 'final' deklariert sein. +[ERROR] src/main/java/org/psesquared/server/authentication_api/service/AuthenticationService.java:[135,31] (misc) FinalParameters: Der Parameter requestBody sollte als 'final' deklariert sein. +[ERROR] src/main/java/org/psesquared/server/authentication_api/service/AuthenticationService.java:[144] (sizes) LineLength: Zeile ist 81 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/authentication_api/service/AuthenticationService.java:[153,5] (javadoc) MissingJavadocMethod: Es fehlt ein Javadoc-Kommentar. +[ERROR] src/main/java/org/psesquared/server/authentication_api/service/AuthenticationService.java:[153,5] (design) DesignForExtension: Klasse 'AuthenticationService' sieht wie zur Erweiterung entworfen aus (kann subclassed werden), aber die Methode 'changePassword' besitzt keinen Javadoc-Kommentar, der erklärt, wie Sie das sicher tun. Wenn die Klasse nicht für Erweiterungen ausgelegt ist, sollten sie die Klasse 'AuthenticationService' final oder die Methode 'changePassword' static/final/abstract/empty machen oder die zugelassene Annotation für die Methode hinzufügen. +[ERROR] src/main/java/org/psesquared/server/authentication_api/service/AuthenticationService.java:[153,38] (misc) FinalParameters: Der Parameter username sollte als 'final' deklariert sein. +[ERROR] src/main/java/org/psesquared/server/authentication_api/service/AuthenticationService.java:[154,38] (misc) FinalParameters: Der Parameter requestBody sollte als 'final' deklariert sein. +[ERROR] src/main/java/org/psesquared/server/authentication_api/service/AuthenticationService.java:[158] (sizes) LineLength: Zeile ist 89 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/authentication_api/service/AuthenticationService.java:[168,5] (javadoc) MissingJavadocMethod: Es fehlt ein Javadoc-Kommentar. +[ERROR] src/main/java/org/psesquared/server/authentication_api/service/AuthenticationService.java:[168,5] (design) DesignForExtension: Klasse 'AuthenticationService' sieht wie zur Erweiterung entworfen aus (kann subclassed werden), aber die Methode 'deleteUser' besitzt keinen Javadoc-Kommentar, der erklärt, wie Sie das sicher tun. Wenn die Klasse nicht für Erweiterungen ausgelegt ist, sollten sie die Klasse 'AuthenticationService' final oder die Methode 'deleteUser' static/final/abstract/empty machen oder die zugelassene Annotation für die Methode hinzufügen. +[ERROR] src/main/java/org/psesquared/server/authentication_api/service/AuthenticationService.java:[168,34] (misc) FinalParameters: Der Parameter username sollte als 'final' deklariert sein. +[ERROR] src/main/java/org/psesquared/server/authentication_api/service/AuthenticationService.java:[169,34] (misc) FinalParameters: Der Parameter requestBody sollte als 'final' deklariert sein. +[ERROR] src/main/java/org/psesquared/server/authentication_api/service/AuthenticationService.java:[173] (sizes) LineLength: Zeile ist 86 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/authentication_api/service/AuthenticationService.java:[183,5] (javadoc) MissingJavadocMethod: Es fehlt ein Javadoc-Kommentar. +[ERROR] src/main/java/org/psesquared/server/authentication_api/service/AuthenticationService.java:[183,5] (design) DesignForExtension: Klasse 'AuthenticationService' sieht wie zur Erweiterung entworfen aus (kann subclassed werden), aber die Methode 'deleteInvalidUsersOlderThan' besitzt keinen Javadoc-Kommentar, der erklärt, wie Sie das sicher tun. Wenn die Klasse nicht für Erweiterungen ausgelegt ist, sollten sie die Klasse 'AuthenticationService' final oder die Methode 'deleteInvalidUsersOlderThan' static/final/abstract/empty machen oder die zugelassene Annotation für die Methode hinzufügen. +[ERROR] src/main/java/org/psesquared/server/authentication_api/service/AuthenticationService.java:[183,45] (misc) FinalParameters: Der Parameter timestamp sollte als 'final' deklariert sein. +[ERROR] src/main/java/org/psesquared/server/authentication_api/service/AuthenticationService.java:[184] (sizes) LineLength: Zeile ist 81 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/authentication_api/service/ResourceReader.java:[8,1] (design) HideUtilityClassConstructor: Hilfsklassen sollten keinen Standard-Konstruktur und keinen als public deklarierten Konstruktor haben. +[ERROR] src/main/java/org/psesquared/server/authentication_api/service/ResourceReader.java:[9] (sizes) LineLength: Zeile ist 83 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/authentication_api/service/ResourceReader.java:[9,5] (javadoc) MissingJavadocMethod: Es fehlt ein Javadoc-Kommentar. +[ERROR] src/main/java/org/psesquared/server/authentication_api/service/ResourceReader.java:[9,43] (misc) FinalParameters: Der Parameter path sollte als 'final' deklariert sein. +[ERROR] src/main/java/org/psesquared/server/authentication_api/service/ResourceReader.java:[10] (sizes) LineLength: Zeile ist 95 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/authentication_api/service/EMailServiceImpl.java:[16,5] (javadoc) JavadocVariable: Es fehlt ein Javadoc-Kommentar. +[ERROR] src/main/java/org/psesquared/server/authentication_api/service/EMailServiceImpl.java:[17,5] (javadoc) JavadocVariable: Es fehlt ein Javadoc-Kommentar. +[ERROR] src/main/java/org/psesquared/server/authentication_api/service/EMailServiceImpl.java:[18,5] (javadoc) JavadocVariable: Es fehlt ein Javadoc-Kommentar. +[ERROR] src/main/java/org/psesquared/server/authentication_api/service/EMailServiceImpl.java:[20,5] (javadoc) JavadocVariable: Es fehlt ein Javadoc-Kommentar. +[ERROR] src/main/java/org/psesquared/server/authentication_api/service/EMailServiceImpl.java:[23,5] (javadoc) JavadocVariable: Es fehlt ein Javadoc-Kommentar. +[ERROR] src/main/java/org/psesquared/server/authentication_api/service/EMailServiceImpl.java:[24] (sizes) LineLength: Zeile ist 110 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/authentication_api/service/EMailServiceImpl.java:[25,5] (javadoc) JavadocVariable: Es fehlt ein Javadoc-Kommentar. +[ERROR] src/main/java/org/psesquared/server/authentication_api/service/EMailServiceImpl.java:[26] (sizes) LineLength: Zeile ist 102 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/authentication_api/service/EMailServiceImpl.java:[28,5] (javadoc) JavadocVariable: Es fehlt ein Javadoc-Kommentar. +[ERROR] src/main/java/org/psesquared/server/authentication_api/service/EMailServiceImpl.java:[29] (sizes) LineLength: Zeile ist 82 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/authentication_api/service/EMailServiceImpl.java:[29,5] (javadoc) JavadocVariable: Es fehlt ein Javadoc-Kommentar. +[ERROR] src/main/java/org/psesquared/server/authentication_api/service/EMailServiceImpl.java:[30] (sizes) LineLength: Zeile ist 85 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/authentication_api/service/EMailServiceImpl.java:[30,5] (javadoc) JavadocVariable: Es fehlt ein Javadoc-Kommentar. +[ERROR] src/main/java/org/psesquared/server/authentication_api/service/EMailServiceImpl.java:[32,5] (javadoc) JavadocVariable: Es fehlt ein Javadoc-Kommentar. +[ERROR] src/main/java/org/psesquared/server/authentication_api/service/EMailServiceImpl.java:[33,5] (javadoc) JavadocVariable: Es fehlt ein Javadoc-Kommentar. +[ERROR] src/main/java/org/psesquared/server/authentication_api/service/EMailServiceImpl.java:[34,5] (javadoc) JavadocVariable: Es fehlt ein Javadoc-Kommentar. +[ERROR] src/main/java/org/psesquared/server/authentication_api/service/EMailServiceImpl.java:[35,5] (javadoc) JavadocVariable: Es fehlt ein Javadoc-Kommentar. +[ERROR] src/main/java/org/psesquared/server/authentication_api/service/EMailServiceImpl.java:[37] (sizes) LineLength: Zeile ist 83 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/authentication_api/service/EMailServiceImpl.java:[37,5] (javadoc) JavadocVariable: Es fehlt ein Javadoc-Kommentar. +[ERROR] src/main/java/org/psesquared/server/authentication_api/service/EMailServiceImpl.java:[37,83] (whitespace) OperatorWrap: '+' sollte in einer neuen Zeile stehen. +[ERROR] src/main/java/org/psesquared/server/authentication_api/service/EMailServiceImpl.java:[41] (sizes) LineLength: Zeile ist 83 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/authentication_api/service/EMailServiceImpl.java:[41,5] (javadoc) JavadocVariable: Es fehlt ein Javadoc-Kommentar. +[ERROR] src/main/java/org/psesquared/server/authentication_api/service/EMailServiceImpl.java:[41,83] (whitespace) OperatorWrap: '+' sollte in einer neuen Zeile stehen. +[ERROR] src/main/java/org/psesquared/server/authentication_api/service/EMailServiceImpl.java:[46] (sizes) LineLength: Zeile ist 119 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/authentication_api/service/EMailServiceImpl.java:[47] (sizes) LineLength: Zeile ist 134 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/authentication_api/service/EMailServiceImpl.java:[52,27] (misc) FinalParameters: Der Parameter to sollte als 'final' deklariert sein. +[ERROR] src/main/java/org/psesquared/server/authentication_api/service/EMailServiceImpl.java:[52,38] (misc) FinalParameters: Der Parameter mailSubject sollte als 'final' deklariert sein. +[ERROR] src/main/java/org/psesquared/server/authentication_api/service/EMailServiceImpl.java:[52,58] (misc) FinalParameters: Der Parameter body sollte als 'final' deklariert sein. +[ERROR] src/main/java/org/psesquared/server/authentication_api/service/EMailServiceImpl.java:[52,65] (javadoc) JavadocMethod: Erwartetes Tag @param für 'body'. +[ERROR] src/main/java/org/psesquared/server/authentication_api/service/EMailServiceImpl.java:[53] (sizes) LineLength: Zeile ist 87 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/authentication_api/service/EMailServiceImpl.java:[62] (sizes) LineLength: Zeile ist 90 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/authentication_api/service/EMailServiceImpl.java:[62,43] (misc) FinalParameters: Der Parameter template sollte als 'final' deklariert sein. +[ERROR] src/main/java/org/psesquared/server/authentication_api/service/EMailServiceImpl.java:[62,60] (misc) FinalParameters: Der Parameter user sollte als 'final' deklariert sein. +[ERROR] src/main/java/org/psesquared/server/authentication_api/service/EMailServiceImpl.java:[62,78] (misc) FinalParameters: Der Parameter url sollte als 'final' deklariert sein. +[ERROR] src/main/java/org/psesquared/server/authentication_api/service/EMailServiceImpl.java:[69] (sizes) LineLength: Zeile ist 81 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/authentication_api/service/EMailServiceImpl.java:[71] (sizes) LineLength: Zeile ist 115 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/authentication_api/service/EMailServiceImpl.java:[76] (sizes) LineLength: Zeile ist 82 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/authentication_api/service/EMailServiceImpl.java:[79,58] (whitespace) OperatorWrap: '+' sollte in einer neuen Zeile stehen. +[ERROR] src/main/java/org/psesquared/server/authentication_api/service/EMailServiceImpl.java:[80,54] (whitespace) OperatorWrap: '+' sollte in einer neuen Zeile stehen. +[ERROR] src/main/java/org/psesquared/server/authentication_api/service/EMailServiceImpl.java:[81,33] (whitespace) OperatorWrap: '+' sollte in einer neuen Zeile stehen. +[ERROR] src/main/java/org/psesquared/server/authentication_api/service/EMailServiceImpl.java:[82,32] (whitespace) OperatorWrap: '+' sollte in einer neuen Zeile stehen. +[ERROR] src/main/java/org/psesquared/server/authentication_api/service/EMailServiceImpl.java:[83,43] (whitespace) OperatorWrap: '+' sollte in einer neuen Zeile stehen. +[ERROR] src/main/java/org/psesquared/server/authentication_api/service/EMailServiceImpl.java:[84,33] (whitespace) OperatorWrap: '+' sollte in einer neuen Zeile stehen. +[ERROR] src/main/java/org/psesquared/server/authentication_api/service/EMailServiceImpl.java:[85,29] (whitespace) OperatorWrap: '+' sollte in einer neuen Zeile stehen. +[ERROR] src/main/java/org/psesquared/server/authentication_api/service/EMailServiceImpl.java:[91] (sizes) LineLength: Zeile ist 89 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/authentication_api/service/EMailServiceImpl.java:[92] (sizes) LineLength: Zeile ist 112 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/authentication_api/service/EMailServiceImpl.java:[95,34] (misc) FinalParameters: Der Parameter to sollte als 'final' deklariert sein. +[ERROR] src/main/java/org/psesquared/server/authentication_api/service/EMailServiceImpl.java:[95,41] (javadoc) JavadocMethod: Erwartetes Tag @param für 'to'. +[ERROR] src/main/java/org/psesquared/server/authentication_api/service/EMailServiceImpl.java:[95,45] (misc) FinalParameters: Der Parameter userDetails sollte als 'final' deklariert sein. +[ERROR] src/main/java/org/psesquared/server/authentication_api/service/EMailServiceImpl.java:[97] (sizes) LineLength: Zeile ist 89 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/authentication_api/service/EMailServiceImpl.java:[103] (sizes) LineLength: Zeile ist 102 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/authentication_api/service/EMailServiceImpl.java:[104] (sizes) LineLength: Zeile ist 118 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/authentication_api/service/EMailServiceImpl.java:[107,35] (misc) FinalParameters: Der Parameter to sollte als 'final' deklariert sein. +[ERROR] src/main/java/org/psesquared/server/authentication_api/service/EMailServiceImpl.java:[107,42] (javadoc) JavadocMethod: Erwartetes Tag @param für 'to'. +[ERROR] src/main/java/org/psesquared/server/authentication_api/service/EMailServiceImpl.java:[107,46] (misc) FinalParameters: Der Parameter userDetails sollte als 'final' deklariert sein. +[ERROR] src/main/java/org/psesquared/server/authentication_api/service/EMailServiceImpl.java:[109] (sizes) LineLength: Zeile ist 90 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/authentication_api/service/EncryptionService.java:[16,5] (javadoc) JavadocVariable: Es fehlt ein Javadoc-Kommentar. +[ERROR] src/main/java/org/psesquared/server/authentication_api/service/EncryptionService.java:[18,5] (javadoc) JavadocVariable: Es fehlt ein Javadoc-Kommentar. +[ERROR] src/main/java/org/psesquared/server/authentication_api/service/EncryptionService.java:[21] (sizes) LineLength: Zeile ist 96 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/authentication_api/service/EncryptionService.java:[26,36] (misc) FinalParameters: Der Parameter email sollte als 'final' deklariert sein. +[ERROR] src/main/java/org/psesquared/server/authentication_api/service/EncryptionService.java:[29] (sizes) LineLength: Zeile ist 81 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/authentication_api/service/EncryptionService.java:[34] (sizes) LineLength: Zeile ist 85 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/authentication_api/service/EncryptionService.java:[34,53] (coding) MagicNumber: Die magische Zahl '0xff' sollte als Konstante definiert werden. +[ERROR] src/main/java/org/psesquared/server/authentication_api/service/EncryptionService.java:[34,61] (coding) MagicNumber: Die magische Zahl '0x100' sollte als Konstante definiert werden. +[ERROR] src/main/java/org/psesquared/server/authentication_api/service/EncryptionService.java:[34,68] (coding) MagicNumber: Die magische Zahl '16' sollte als Konstante definiert werden. +[ERROR] src/main/java/org/psesquared/server/authentication_api/service/EncryptionService.java:[44] (sizes) LineLength: Zeile ist 118 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/authentication_api/service/EncryptionService.java:[49] (sizes) LineLength: Zeile ist 82 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/authentication_api/service/InputCheckService.java:[14,5] (javadoc) JavadocVariable: Es fehlt ein Javadoc-Kommentar. +[ERROR] src/main/java/org/psesquared/server/authentication_api/service/InputCheckService.java:[15,5] (javadoc) JavadocVariable: Es fehlt ein Javadoc-Kommentar. +[ERROR] src/main/java/org/psesquared/server/authentication_api/service/InputCheckService.java:[16,5] (javadoc) JavadocVariable: Es fehlt ein Javadoc-Kommentar. +[ERROR] src/main/java/org/psesquared/server/authentication_api/service/InputCheckService.java:[24] (sizes) LineLength: Zeile ist 101 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/authentication_api/service/InputCheckService.java:[44] (sizes) LineLength: Zeile ist 124 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/authentication_api/service/InputCheckService.java:[49] (sizes) LineLength: Zeile ist 174 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/authentication_api/service/InputCheckService.java:[59] (javadoc) JavadocStyle: Der erste Satz sollte mit einem Punkt enden. +[ERROR] src/main/java/org/psesquared/server/authentication_api/service/InputCheckService.java:[60] (sizes) LineLength: Zeile ist 93 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/authentication_api/service/InputCheckService.java:[63] (sizes) LineLength: Zeile ist 96 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/authentication_api/service/InputCheckService.java:[65] (javadoc) JavadocStyle: Der erste Satz sollte mit einem Punkt enden. +[ERROR] src/main/java/org/psesquared/server/authentication_api/service/InputCheckService.java:[66] (sizes) LineLength: Zeile ist 93 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/authentication_api/service/InputCheckService.java:[67] (sizes) LineLength: Zeile ist 177 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/authentication_api/service/InputCheckService.java:[69] (sizes) LineLength: Zeile ist 157 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/authentication_api/service/InputCheckService.java:[71] (javadoc) JavadocStyle: Der erste Satz sollte mit einem Punkt enden. +[ERROR] src/main/java/org/psesquared/server/authentication_api/service/InputCheckService.java:[72] (sizes) LineLength: Zeile ist 82 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/authentication_api/service/InputCheckService.java:[73] (sizes) LineLength: Zeile ist 94 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/authentication_api/service/InputCheckService.java:[77] (sizes) LineLength: Zeile ist 91 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/authentication_api/service/InputCheckService.java:[79,37] (misc) FinalParameters: Der Parameter username sollte als 'final' deklariert sein. +[ERROR] src/main/java/org/psesquared/server/authentication_api/service/InputCheckService.java:[87] (sizes) LineLength: Zeile ist 114 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/authentication_api/service/InputCheckService.java:[89] (sizes) LineLength: Zeile ist 91 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/authentication_api/service/InputCheckService.java:[91,34] (misc) FinalParameters: Der Parameter email sollte als 'final' deklariert sein. +[ERROR] src/main/java/org/psesquared/server/authentication_api/service/InputCheckService.java:[93] (sizes) LineLength: Zeile ist 81 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/authentication_api/service/InputCheckService.java:[101] (javadoc) JavadocStyle: Der erste Satz sollte mit einem Punkt enden. +[ERROR] src/main/java/org/psesquared/server/authentication_api/service/InputCheckService.java:[102] (sizes) LineLength: Zeile ist 82 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/authentication_api/service/InputCheckService.java:[106] (sizes) LineLength: Zeile ist 104 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/authentication_api/service/InputCheckService.java:[107] (sizes) LineLength: Zeile ist 106 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/authentication_api/service/InputCheckService.java:[109] (sizes) LineLength: Zeile ist 91 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/authentication_api/service/InputCheckService.java:[111,37] (misc) FinalParameters: Der Parameter password sollte als 'final' deklariert sein. +[ERROR] src/main/java/org/psesquared/server/authentication_api/data_access/AuthenticationDao.java:[1] (javadoc) JavadocPackage: Es fehlt eine package-info.java. +[ERROR] src/main/java/org/psesquared/server/authentication_api/data_access/AuthenticationDao.java:[18] (sizes) LineLength: Zeile ist 82 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/authentication_api/data_access/AuthenticationDao.java:[27] (sizes) LineLength: Zeile ist 81 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/authentication_api/data_access/AuthenticationDao.java:[40] (sizes) LineLength: Zeile ist 96 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/authentication_api/data_access/AuthenticationDao.java:[42] (sizes) LineLength: Zeile ist 97 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/episode_actions_api/controller/EpisodeActionController.java:[1] (javadoc) JavadocPackage: Es fehlt eine package-info.java. +[ERROR] src/main/java/org/psesquared/server/episode_actions_api/controller/EpisodeActionController.java:[7,47] (imports) AvoidStarImport: Stern-Importe der Form '.*' sollten vermieden werden - org.springframework.web.bind.annotation.*. +[ERROR] src/main/java/org/psesquared/server/episode_actions_api/controller/EpisodeActionController.java:[12] (sizes) LineLength: Zeile ist 98 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/episode_actions_api/controller/EpisodeActionController.java:[21,5] (javadoc) JavadocVariable: Es fehlt ein Javadoc-Kommentar. +[ERROR] src/main/java/org/psesquared/server/episode_actions_api/controller/EpisodeActionController.java:[24] (sizes) LineLength: Zeile ist 82 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/episode_actions_api/controller/EpisodeActionController.java:[25] (regexp) RegexpSingleline: Line has trailing spaces. +[ERROR] src/main/java/org/psesquared/server/episode_actions_api/controller/EpisodeActionController.java:[31] (sizes) LineLength: Zeile ist 93 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/episode_actions_api/controller/EpisodeActionController.java:[31,64] (misc) FinalParameters: Der Parameter username sollte als 'final' deklariert sein. +[ERROR] src/main/java/org/psesquared/server/episode_actions_api/controller/EpisodeActionController.java:[32] (sizes) LineLength: Zeile ist 121 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/episode_actions_api/controller/EpisodeActionController.java:[32,64] (misc) FinalParameters: Der Parameter episodeActionPosts sollte als 'final' deklariert sein. +[ERROR] src/main/java/org/psesquared/server/episode_actions_api/controller/EpisodeActionController.java:[37] (javadoc) JavadocStyle: Der erste Satz sollte mit einem Punkt enden. +[ERROR] src/main/java/org/psesquared/server/episode_actions_api/controller/EpisodeActionController.java:[38] (sizes) LineLength: Zeile ist 113 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/episode_actions_api/controller/EpisodeActionController.java:[39] (regexp) RegexpSingleline: Line has trailing spaces. +[ERROR] src/main/java/org/psesquared/server/episode_actions_api/controller/EpisodeActionController.java:[40] (sizes) LineLength: Zeile ist 82 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/episode_actions_api/controller/EpisodeActionController.java:[41] (sizes) LineLength: Zeile ist 91 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/episode_actions_api/controller/EpisodeActionController.java:[44] (sizes) LineLength: Zeile ist 102 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/episode_actions_api/controller/EpisodeActionController.java:[44,71] (misc) FinalParameters: Der Parameter username sollte als 'final' deklariert sein. +[ERROR] src/main/java/org/psesquared/server/episode_actions_api/controller/EpisodeActionController.java:[45] (sizes) LineLength: Zeile ist 127 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/episode_actions_api/controller/EpisodeActionController.java:[49] (javadoc) JavadocStyle: Der erste Satz sollte mit einem Punkt enden. +[ERROR] src/main/java/org/psesquared/server/episode_actions_api/controller/EpisodeActionController.java:[50] (sizes) LineLength: Zeile ist 112 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/episode_actions_api/controller/EpisodeActionController.java:[51] (regexp) RegexpSingleline: Line has trailing spaces. +[ERROR] src/main/java/org/psesquared/server/episode_actions_api/controller/EpisodeActionController.java:[52] (sizes) LineLength: Zeile ist 82 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/episode_actions_api/controller/EpisodeActionController.java:[54] (sizes) LineLength: Zeile ist 91 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/episode_actions_api/controller/EpisodeActionController.java:[57] (sizes) LineLength: Zeile ist 109 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/episode_actions_api/controller/EpisodeActionController.java:[57,80] (misc) FinalParameters: Der Parameter username sollte als 'final' deklariert sein. +[ERROR] src/main/java/org/psesquared/server/episode_actions_api/controller/EpisodeActionController.java:[58] (sizes) LineLength: Zeile ist 103 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/episode_actions_api/controller/EpisodeActionController.java:[58,59] (misc) FinalParameters: Der Parameter podcastURL sollte als 'final' deklariert sein. +[ERROR] src/main/java/org/psesquared/server/episode_actions_api/controller/EpisodeActionController.java:[59] (sizes) LineLength: Zeile ist 148 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/episode_actions_api/controller/EpisodeActionController.java:[63] (javadoc) JavadocStyle: Der erste Satz sollte mit einem Punkt enden. +[ERROR] src/main/java/org/psesquared/server/episode_actions_api/controller/EpisodeActionController.java:[64] (sizes) LineLength: Zeile ist 116 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/episode_actions_api/controller/EpisodeActionController.java:[65] (regexp) RegexpSingleline: Line has trailing spaces. +[ERROR] src/main/java/org/psesquared/server/episode_actions_api/controller/EpisodeActionController.java:[66] (sizes) LineLength: Zeile ist 82 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/episode_actions_api/controller/EpisodeActionController.java:[67] (sizes) LineLength: Zeile ist 89 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/episode_actions_api/controller/EpisodeActionController.java:[68] (sizes) LineLength: Zeile ist 91 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/episode_actions_api/controller/EpisodeActionController.java:[71] (sizes) LineLength: Zeile ist 105 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/episode_actions_api/controller/EpisodeActionController.java:[71,76] (misc) FinalParameters: Der Parameter username sollte als 'final' deklariert sein. +[ERROR] src/main/java/org/psesquared/server/episode_actions_api/controller/EpisodeActionController.java:[72] (sizes) LineLength: Zeile ist 95 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/episode_actions_api/controller/EpisodeActionController.java:[72,60] (misc) FinalParameters: Der Parameter since sollte als 'final' deklariert sein. +[ERROR] src/main/java/org/psesquared/server/episode_actions_api/controller/EpisodeActionController.java:[73] (sizes) LineLength: Zeile ist 139 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/episode_actions_api/controller/EpisodeActionController.java:[77] (javadoc) JavadocStyle: Der erste Satz sollte mit einem Punkt enden. +[ERROR] src/main/java/org/psesquared/server/episode_actions_api/controller/EpisodeActionController.java:[78] (sizes) LineLength: Zeile ist 132 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/episode_actions_api/controller/EpisodeActionController.java:[79] (regexp) RegexpSingleline: Line has trailing spaces. +[ERROR] src/main/java/org/psesquared/server/episode_actions_api/controller/EpisodeActionController.java:[80] (sizes) LineLength: Zeile ist 82 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/episode_actions_api/controller/EpisodeActionController.java:[82] (sizes) LineLength: Zeile ist 89 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/episode_actions_api/controller/EpisodeActionController.java:[83] (sizes) LineLength: Zeile ist 91 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/episode_actions_api/controller/EpisodeActionController.java:[86] (sizes) LineLength: Zeile ist 114 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/episode_actions_api/controller/EpisodeActionController.java:[86,85] (misc) FinalParameters: Der Parameter username sollte als 'final' deklariert sein. +[ERROR] src/main/java/org/psesquared/server/episode_actions_api/controller/EpisodeActionController.java:[87] (sizes) LineLength: Zeile ist 106 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/episode_actions_api/controller/EpisodeActionController.java:[87,64] (misc) FinalParameters: Der Parameter podcastURL sollte als 'final' deklariert sein. +[ERROR] src/main/java/org/psesquared/server/episode_actions_api/controller/EpisodeActionController.java:[88] (sizes) LineLength: Zeile ist 99 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/episode_actions_api/controller/EpisodeActionController.java:[88,64] (misc) FinalParameters: Der Parameter since sollte als 'final' deklariert sein. +[ERROR] src/main/java/org/psesquared/server/episode_actions_api/controller/EpisodeActionController.java:[89] (sizes) LineLength: Zeile ist 160 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/episode_actions_api/controller/EpisodeActionGetResponse.java:[13,5] (javadoc) JavadocVariable: Es fehlt ein Javadoc-Kommentar. +[ERROR] src/main/java/org/psesquared/server/episode_actions_api/controller/EpisodeActionGetResponse.java:[14,5] (javadoc) JavadocVariable: Es fehlt ein Javadoc-Kommentar. +[ERROR] src/main/java/org/psesquared/server/episode_actions_api/controller/EpisodeActionGetResponse.java:[19,37] (misc) FinalParameters: Der Parameter actions sollte als 'final' deklariert sein. +[ERROR] src/main/java/org/psesquared/server/episode_actions_api/controller/EpisodeActionGetResponse.java:[19,61] (coding) HiddenField: Die Variable 'actions' verbirgt ein Feld. +[ERROR] src/main/java/org/psesquared/server/episode_actions_api/controller/EpisodeActionGetResponse.java:[24,5] (javadoc) MissingJavadocMethod: Es fehlt ein Javadoc-Kommentar. +[ERROR] src/main/java/org/psesquared/server/episode_actions_api/controller/EpisodeActionGetResponse.java:[24,5] (design) DesignForExtension: Klasse 'EpisodeActionGetResponse' sieht wie zur Erweiterung entworfen aus (kann subclassed werden), aber die Methode 'getActions' besitzt keinen Javadoc-Kommentar, der erklärt, wie Sie das sicher tun. Wenn die Klasse nicht für Erweiterungen ausgelegt ist, sollten sie die Klasse 'EpisodeActionGetResponse' final oder die Methode 'getActions' static/final/abstract/empty machen oder die zugelassene Annotation für die Methode hinzufügen. +[ERROR] src/main/java/org/psesquared/server/episode_actions_api/controller/EpisodeActionGetResponse.java:[28,5] (javadoc) MissingJavadocMethod: Es fehlt ein Javadoc-Kommentar. +[ERROR] src/main/java/org/psesquared/server/episode_actions_api/controller/EpisodeActionGetResponse.java:[28,5] (design) DesignForExtension: Klasse 'EpisodeActionGetResponse' sieht wie zur Erweiterung entworfen aus (kann subclassed werden), aber die Methode 'getTimestamp' besitzt keinen Javadoc-Kommentar, der erklärt, wie Sie das sicher tun. Wenn die Klasse nicht für Erweiterungen ausgelegt ist, sollten sie die Klasse 'EpisodeActionGetResponse' final oder die Methode 'getTimestamp' static/final/abstract/empty machen oder die zugelassene Annotation für die Methode hinzufügen. +[ERROR] src/main/java/org/psesquared/server/episode_actions_api/controller/EpisodeActionPost.java:[14] (sizes) LineLength: Zeile ist 94 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/episode_actions_api/controller/EpisodeActionPost.java:[22,5] (javadoc) JavadocVariable: Es fehlt ein Javadoc-Kommentar. +[ERROR] src/main/java/org/psesquared/server/episode_actions_api/controller/EpisodeActionPost.java:[26,5] (javadoc) JavadocVariable: Es fehlt ein Javadoc-Kommentar. +[ERROR] src/main/java/org/psesquared/server/episode_actions_api/controller/EpisodeActionPost.java:[30,5] (javadoc) JavadocVariable: Es fehlt ein Javadoc-Kommentar. +[ERROR] src/main/java/org/psesquared/server/episode_actions_api/controller/EpisodeActionPost.java:[32,5] (javadoc) JavadocVariable: Es fehlt ein Javadoc-Kommentar. +[ERROR] src/main/java/org/psesquared/server/episode_actions_api/controller/EpisodeActionPost.java:[34,5] (javadoc) JavadocVariable: Es fehlt ein Javadoc-Kommentar. +[ERROR] src/main/java/org/psesquared/server/episode_actions_api/controller/EpisodeActionPost.java:[36,5] (javadoc) JavadocVariable: Es fehlt ein Javadoc-Kommentar. +[ERROR] src/main/java/org/psesquared/server/episode_actions_api/service/EpisodeActionService.java:[1] (javadoc) JavadocPackage: Es fehlt eine package-info.java. +[ERROR] src/main/java/org/psesquared/server/episode_actions_api/service/EpisodeActionService.java:[30,5] (javadoc) JavadocVariable: Es fehlt ein Javadoc-Kommentar. +[ERROR] src/main/java/org/psesquared/server/episode_actions_api/service/EpisodeActionService.java:[32,5] (javadoc) JavadocVariable: Es fehlt ein Javadoc-Kommentar. +[ERROR] src/main/java/org/psesquared/server/episode_actions_api/service/EpisodeActionService.java:[34,5] (javadoc) JavadocVariable: Es fehlt ein Javadoc-Kommentar. +[ERROR] src/main/java/org/psesquared/server/episode_actions_api/service/EpisodeActionService.java:[36,5] (javadoc) JavadocVariable: Es fehlt ein Javadoc-Kommentar. +[ERROR] src/main/java/org/psesquared/server/episode_actions_api/service/EpisodeActionService.java:[38,5] (javadoc) JavadocVariable: Es fehlt ein Javadoc-Kommentar. +[ERROR] src/main/java/org/psesquared/server/episode_actions_api/service/EpisodeActionService.java:[40,5] (javadoc) JavadocVariable: Es fehlt ein Javadoc-Kommentar. +[ERROR] src/main/java/org/psesquared/server/episode_actions_api/service/EpisodeActionService.java:[42,5] (javadoc) JavadocVariable: Es fehlt ein Javadoc-Kommentar. +[ERROR] src/main/java/org/psesquared/server/episode_actions_api/service/EpisodeActionService.java:[44] (javadoc) JavadocStyle: Der erste Satz sollte mit einem Punkt enden. +[ERROR] src/main/java/org/psesquared/server/episode_actions_api/service/EpisodeActionService.java:[45] (sizes) LineLength: Zeile ist 84 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/episode_actions_api/service/EpisodeActionService.java:[50] (sizes) LineLength: Zeile ist 82 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/episode_actions_api/service/EpisodeActionService.java:[53] (sizes) LineLength: Zeile ist 96 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/episode_actions_api/service/EpisodeActionService.java:[53,35] (misc) FinalParameters: Der Parameter username sollte als 'final' deklariert sein. +[ERROR] src/main/java/org/psesquared/server/episode_actions_api/service/EpisodeActionService.java:[53,52] (misc) FinalParameters: Der Parameter episodeActionPosts sollte als 'final' deklariert sein. +[ERROR] src/main/java/org/psesquared/server/episode_actions_api/service/EpisodeActionService.java:[55] (sizes) LineLength: Zeile ist 106 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/episode_actions_api/service/EpisodeActionService.java:[59,68] (misc) FinalParameters: Der Parameter user sollte als 'final' deklariert sein. +[ERROR] src/main/java/org/psesquared/server/episode_actions_api/service/EpisodeActionService.java:[60,13] (misc) FinalParameters: Der Parameter episodeActionPosts sollte als 'final' deklariert sein. +[ERROR] src/main/java/org/psesquared/server/episode_actions_api/service/EpisodeActionService.java:[63] (sizes) LineLength: Zeile ist 90 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/episode_actions_api/service/EpisodeActionService.java:[68] (sizes) LineLength: Zeile ist 108 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/episode_actions_api/service/EpisodeActionService.java:[68,60] (misc) FinalParameters: Der Parameter user sollte als 'final' deklariert sein. +[ERROR] src/main/java/org/psesquared/server/episode_actions_api/service/EpisodeActionService.java:[68,71] (misc) FinalParameters: Der Parameter episodeActionPost sollte als 'final' deklariert sein. +[ERROR] src/main/java/org/psesquared/server/episode_actions_api/service/EpisodeActionService.java:[75] (sizes) LineLength: Zeile ist 89 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/episode_actions_api/service/EpisodeActionService.java:[82] (sizes) LineLength: Zeile ist 109 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/episode_actions_api/service/EpisodeActionService.java:[83] (sizes) LineLength: Zeile ist 81 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/episode_actions_api/service/EpisodeActionService.java:[87] (sizes) LineLength: Zeile ist 102 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/episode_actions_api/service/EpisodeActionService.java:[96] (sizes) LineLength: Zeile ist 81 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/episode_actions_api/service/EpisodeActionService.java:[96,44] (misc) FinalParameters: Der Parameter episodeActionPost sollte als 'final' deklariert sein. +[ERROR] src/main/java/org/psesquared/server/episode_actions_api/service/EpisodeActionService.java:[103,9] (blocks) RightCurly: '}' an Position 9 sollte in der gleichen Zeile stehen wie der nächste Teil der Multi-Block-Anweisung (eine Anweisung, die mehrere Blöcke enthält: if/else-if/else, do/while oder try/catch/finally). +[ERROR] src/main/java/org/psesquared/server/episode_actions_api/service/EpisodeActionService.java:[112,9] (blocks) RightCurly: '}' an Position 9 sollte in der gleichen Zeile stehen wie der nächste Teil der Multi-Block-Anweisung (eine Anweisung, die mehrere Blöcke enthält: if/else-if/else, do/while oder try/catch/finally). +[ERROR] src/main/java/org/psesquared/server/episode_actions_api/service/EpisodeActionService.java:[120,35] (misc) FinalParameters: Der Parameter episodeActionPost sollte als 'final' deklariert sein. +[ERROR] src/main/java/org/psesquared/server/episode_actions_api/service/EpisodeActionService.java:[125] (sizes) LineLength: Zeile ist 105 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/episode_actions_api/service/EpisodeActionService.java:[134] (sizes) LineLength: Zeile ist 93 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/episode_actions_api/service/EpisodeActionService.java:[134,46] (misc) FinalParameters: Der Parameter user sollte als 'final' deklariert sein. +[ERROR] src/main/java/org/psesquared/server/episode_actions_api/service/EpisodeActionService.java:[134,57] (misc) FinalParameters: Der Parameter episodeActions sollte als 'final' deklariert sein. +[ERROR] src/main/java/org/psesquared/server/episode_actions_api/service/EpisodeActionService.java:[140] (sizes) LineLength: Zeile ist 85 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/episode_actions_api/service/EpisodeActionService.java:[140,45] (misc) FinalParameters: Der Parameter user sollte als 'final' deklariert sein. +[ERROR] src/main/java/org/psesquared/server/episode_actions_api/service/EpisodeActionService.java:[140,56] (misc) FinalParameters: Der Parameter episodeAction sollte als 'final' deklariert sein. +[ERROR] src/main/java/org/psesquared/server/episode_actions_api/service/EpisodeActionService.java:[141] (sizes) LineLength: Zeile ist 106 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/episode_actions_api/service/EpisodeActionService.java:[149] (sizes) LineLength: Zeile ist 91 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/episode_actions_api/service/EpisodeActionService.java:[149,51] (misc) FinalParameters: Der Parameter user sollte als 'final' deklariert sein. +[ERROR] src/main/java/org/psesquared/server/episode_actions_api/service/EpisodeActionService.java:[149,62] (misc) FinalParameters: Der Parameter episodeAction sollte als 'final' deklariert sein. +[ERROR] src/main/java/org/psesquared/server/episode_actions_api/service/EpisodeActionService.java:[150] (sizes) LineLength: Zeile ist 96 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/episode_actions_api/service/EpisodeActionService.java:[153] (sizes) LineLength: Zeile ist 84 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/episode_actions_api/service/EpisodeActionService.java:[159] (javadoc) JavadocStyle: Der erste Satz sollte mit einem Punkt enden. +[ERROR] src/main/java/org/psesquared/server/episode_actions_api/service/EpisodeActionService.java:[163] (sizes) LineLength: Zeile ist 82 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/episode_actions_api/service/EpisodeActionService.java:[164] (sizes) LineLength: Zeile ist 83 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/episode_actions_api/service/EpisodeActionService.java:[166,54] (misc) FinalParameters: Der Parameter username sollte als 'final' deklariert sein. +[ERROR] src/main/java/org/psesquared/server/episode_actions_api/service/EpisodeActionService.java:[167] (sizes) LineLength: Zeile ist 91 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/episode_actions_api/service/EpisodeActionService.java:[175] (sizes) LineLength: Zeile ist 84 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/episode_actions_api/service/EpisodeActionService.java:[177] (sizes) LineLength: Zeile ist 83 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/episode_actions_api/service/EpisodeActionService.java:[179] (sizes) LineLength: Zeile ist 99 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/episode_actions_api/service/EpisodeActionService.java:[179,63] (misc) FinalParameters: Der Parameter username sollte als 'final' deklariert sein. +[ERROR] src/main/java/org/psesquared/server/episode_actions_api/service/EpisodeActionService.java:[179,80] (misc) FinalParameters: Der Parameter podcastURL sollte als 'final' deklariert sein. +[ERROR] src/main/java/org/psesquared/server/episode_actions_api/service/EpisodeActionService.java:[180] (sizes) LineLength: Zeile ist 115 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/episode_actions_api/service/EpisodeActionService.java:[185] (javadoc) JavadocStyle: Der erste Satz sollte mit einem Punkt enden. +[ERROR] src/main/java/org/psesquared/server/episode_actions_api/service/EpisodeActionService.java:[186] (sizes) LineLength: Zeile ist 82 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/episode_actions_api/service/EpisodeActionService.java:[189] (sizes) LineLength: Zeile ist 82 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/episode_actions_api/service/EpisodeActionService.java:[192] (sizes) LineLength: Zeile ist 83 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/episode_actions_api/service/EpisodeActionService.java:[194] (sizes) LineLength: Zeile ist 88 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/episode_actions_api/service/EpisodeActionService.java:[194,59] (misc) FinalParameters: Der Parameter username sollte als 'final' deklariert sein. +[ERROR] src/main/java/org/psesquared/server/episode_actions_api/service/EpisodeActionService.java:[194,76] (misc) FinalParameters: Der Parameter since sollte als 'final' deklariert sein. +[ERROR] src/main/java/org/psesquared/server/episode_actions_api/service/EpisodeActionService.java:[195] (sizes) LineLength: Zeile ist 114 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/episode_actions_api/service/EpisodeActionService.java:[196] (sizes) LineLength: Zeile ist 118 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/episode_actions_api/service/EpisodeActionService.java:[201] (javadoc) JavadocStyle: Der erste Satz sollte mit einem Punkt enden. +[ERROR] src/main/java/org/psesquared/server/episode_actions_api/service/EpisodeActionService.java:[202] (sizes) LineLength: Zeile ist 83 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/episode_actions_api/service/EpisodeActionService.java:[206] (sizes) LineLength: Zeile ist 84 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/episode_actions_api/service/EpisodeActionService.java:[210] (sizes) LineLength: Zeile ist 83 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/episode_actions_api/service/EpisodeActionService.java:[212] (sizes) LineLength: Zeile ist 116 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/episode_actions_api/service/EpisodeActionService.java:[212,68] (misc) FinalParameters: Der Parameter username sollte als 'final' deklariert sein. +[ERROR] src/main/java/org/psesquared/server/episode_actions_api/service/EpisodeActionService.java:[212,85] (misc) FinalParameters: Der Parameter podcastURL sollte als 'final' deklariert sein. +[ERROR] src/main/java/org/psesquared/server/episode_actions_api/service/EpisodeActionService.java:[212,104] (misc) FinalParameters: Der Parameter since sollte als 'final' deklariert sein. +[ERROR] src/main/java/org/psesquared/server/episode_actions_api/service/EpisodeActionService.java:[213] (sizes) LineLength: Zeile ist 114 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/episode_actions_api/service/EpisodeActionService.java:[215] (sizes) LineLength: Zeile ist 114 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/episode_actions_api/service/EpisodeActionService.java:[220] (sizes) LineLength: Zeile ist 108 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/episode_actions_api/service/EpisodeActionService.java:[220,72] (misc) FinalParameters: Der Parameter episodeActions sollte als 'final' deklariert sein. +[ERROR] src/main/java/org/psesquared/server/episode_actions_api/data_access/EpisodeActionDao.java:[1] (javadoc) JavadocPackage: Es fehlt eine package-info.java. +[ERROR] src/main/java/org/psesquared/server/episode_actions_api/data_access/EpisodeActionDao.java:[16] (javadoc) JavadocStyle: Der erste Satz sollte mit einem Punkt enden. +[ERROR] src/main/java/org/psesquared/server/episode_actions_api/data_access/EpisodeActionDao.java:[22] (javadoc) JavadocStyle: Der erste Satz sollte mit einem Punkt enden. +[ERROR] src/main/java/org/psesquared/server/episode_actions_api/data_access/EpisodeActionDao.java:[30] (javadoc) JavadocStyle: Der erste Satz sollte mit einem Punkt enden. +[ERROR] src/main/java/org/psesquared/server/episode_actions_api/data_access/EpisodeActionDao.java:[31] (sizes) LineLength: Zeile ist 82 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/episode_actions_api/data_access/EpisodeActionDao.java:[36] (sizes) LineLength: Zeile ist 82 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/episode_actions_api/data_access/EpisodeActionDao.java:[38] (sizes) LineLength: Zeile ist 97 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/episode_actions_api/data_access/EpisodeActionDao.java:[40] (sizes) LineLength: Zeile ist 84 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/episode_actions_api/data_access/EpisodeActionDao.java:[40,5] (javadoc) MissingJavadocMethod: Es fehlt ein Javadoc-Kommentar. +[ERROR] src/main/java/org/psesquared/server/episode_actions_api/data_access/EpisodeActionDao.java:[42] (sizes) LineLength: Zeile ist 85 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/episode_actions_api/data_access/EpisodeActionDao.java:[42,5] (javadoc) MissingJavadocMethod: Es fehlt ein Javadoc-Kommentar. +[ERROR] src/main/java/org/psesquared/server/episode_actions_api/data_access/EpisodeActionDao.java:[44] (sizes) LineLength: Zeile ist 99 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/episode_actions_api/data_access/EpisodeActionDao.java:[44,5] (javadoc) MissingJavadocMethod: Es fehlt ein Javadoc-Kommentar. +[ERROR] src/main/java/org/psesquared/server/episode_actions_api/data_access/EpisodeActionDao.java:[46] (javadoc) JavadocStyle: Der erste Satz sollte mit einem Punkt enden. +[ERROR] src/main/java/org/psesquared/server/episode_actions_api/data_access/EpisodeActionDao.java:[50] (sizes) LineLength: Zeile ist 84 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/episode_actions_api/data_access/EpisodeActionDao.java:[54] (sizes) LineLength: Zeile ist 113 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/episode_actions_api/data_access/EpisodeActionDao.java:[56] (javadoc) JavadocStyle: Der erste Satz sollte mit einem Punkt enden. +[ERROR] src/main/java/org/psesquared/server/episode_actions_api/data_access/EpisodeActionDao.java:[57] (sizes) LineLength: Zeile ist 83 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/episode_actions_api/data_access/EpisodeActionDao.java:[60] (sizes) LineLength: Zeile ist 84 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/episode_actions_api/data_access/EpisodeActionDao.java:[64] (sizes) LineLength: Zeile ist 82 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/episode_actions_api/data_access/EpisodeActionDao.java:[67] (sizes) LineLength: Zeile ist 112 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/episode_actions_api/data_access/EpisodeActionDao.java:[72,5] (javadoc) MissingJavadocMethod: Es fehlt ein Javadoc-Kommentar. +[ERROR] src/main/java/org/psesquared/server/episode_actions_api/data_access/EpisodeDao.java:[9] (javadoc) JavadocStyle: Der erste Satz sollte mit einem Punkt enden. +[ERROR] src/main/java/org/psesquared/server/episode_actions_api/data_access/EpisodeDao.java:[15] (javadoc) JavadocStyle: Der erste Satz sollte mit einem Punkt enden. +[ERROR] src/main/java/org/psesquared/server/episode_actions_api/data_access/EpisodeDao.java:[23] (javadoc) JavadocStyle: Der erste Satz sollte mit einem Punkt enden. +[ERROR] src/main/java/org/psesquared/server/episode_actions_api/data_access/EpisodeDao.java:[31] (javadoc) JavadocStyle: Der erste Satz sollte mit einem Punkt enden. +[ERROR] src/main/java/org/psesquared/server/episode_actions_api/data_access/EpisodeDao.java:[39] (javadoc) JavadocStyle: Der erste Satz sollte mit einem Punkt enden. +[ERROR] src/main/java/org/psesquared/server/subscriptions_api/controller/SubscriptionTitles.java:[1] (javadoc) JavadocPackage: Es fehlt eine package-info.java. +[ERROR] src/main/java/org/psesquared/server/subscriptions_api/controller/SubscriptionTitles.java:[9] (javadoc) JavadocStyle: Der erste Satz sollte mit einem Punkt enden. +[ERROR] src/main/java/org/psesquared/server/subscriptions_api/controller/SubscriptionTitles.java:[12] (sizes) LineLength: Zeile ist 110 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/subscriptions_api/controller/SubscriptionTitles.java:[12,1] (javadoc) JavadocType: Im Javadoc des Typs fehlt das Tag @param episodes. +[ERROR] src/main/java/org/psesquared/server/subscriptions_api/controller/SubscriptionTitles.java:[12,1] (javadoc) JavadocType: Im Javadoc des Typs fehlt das Tag @param subscription. +[ERROR] src/main/java/org/psesquared/server/subscriptions_api/controller/SubscriptionController.java:[7,47] (imports) AvoidStarImport: Stern-Importe der Form '.*' sollten vermieden werden - org.springframework.web.bind.annotation.*. +[ERROR] src/main/java/org/psesquared/server/subscriptions_api/controller/SubscriptionController.java:[12] (sizes) LineLength: Zeile ist 96 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/subscriptions_api/controller/SubscriptionController.java:[13] (sizes) LineLength: Zeile ist 97 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/subscriptions_api/controller/SubscriptionController.java:[20,5] (javadoc) JavadocVariable: Es fehlt ein Javadoc-Kommentar. +[ERROR] src/main/java/org/psesquared/server/subscriptions_api/controller/SubscriptionController.java:[22,5] (javadoc) JavadocVariable: Es fehlt ein Javadoc-Kommentar. +[ERROR] src/main/java/org/psesquared/server/subscriptions_api/controller/SubscriptionController.java:[25] (sizes) LineLength: Zeile ist 81 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/subscriptions_api/controller/SubscriptionController.java:[27] (regexp) RegexpSingleline: Line has trailing spaces. +[ERROR] src/main/java/org/psesquared/server/subscriptions_api/controller/SubscriptionController.java:[29] (sizes) LineLength: Zeile ist 127 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/subscriptions_api/controller/SubscriptionController.java:[30] (sizes) LineLength: Zeile ist 111 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/subscriptions_api/controller/SubscriptionController.java:[34] (sizes) LineLength: Zeile ist 84 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/subscriptions_api/controller/SubscriptionController.java:[34,55] (misc) FinalParameters: Der Parameter username sollte als 'final' deklariert sein. +[ERROR] src/main/java/org/psesquared/server/subscriptions_api/controller/SubscriptionController.java:[35,48] (misc) FinalParameters: Der Parameter deviceID sollte als 'final' deklariert sein. +[ERROR] src/main/java/org/psesquared/server/subscriptions_api/controller/SubscriptionController.java:[36] (sizes) LineLength: Zeile ist 89 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/subscriptions_api/controller/SubscriptionController.java:[36,48] (misc) FinalParameters: Der Parameter subscriptions sollte als 'final' deklariert sein. +[ERROR] src/main/java/org/psesquared/server/subscriptions_api/controller/SubscriptionController.java:[43] (regexp) RegexpSingleline: Line has trailing spaces. +[ERROR] src/main/java/org/psesquared/server/subscriptions_api/controller/SubscriptionController.java:[44] (sizes) LineLength: Zeile ist 88 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/subscriptions_api/controller/SubscriptionController.java:[45] (sizes) LineLength: Zeile ist 113 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/subscriptions_api/controller/SubscriptionController.java:[47] (sizes) LineLength: Zeile ist 102 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/subscriptions_api/controller/SubscriptionController.java:[48] (sizes) LineLength: Zeile ist 88 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/subscriptions_api/controller/SubscriptionController.java:[50] (sizes) LineLength: Zeile ist 103 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/subscriptions_api/controller/SubscriptionController.java:[51] (sizes) LineLength: Zeile ist 87 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/subscriptions_api/controller/SubscriptionController.java:[51,58] (misc) FinalParameters: Der Parameter username sollte als 'final' deklariert sein. +[ERROR] src/main/java/org/psesquared/server/subscriptions_api/controller/SubscriptionController.java:[52] (sizes) LineLength: Zeile ist 98 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/subscriptions_api/controller/SubscriptionController.java:[52,51] (misc) FinalParameters: Der Parameter deviceID sollte als 'final' deklariert sein. +[ERROR] src/main/java/org/psesquared/server/subscriptions_api/controller/SubscriptionController.java:[53] (sizes) LineLength: Zeile ist 122 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/subscriptions_api/controller/SubscriptionController.java:[53,51] (misc) FinalParameters: Der Parameter functionJSONP sollte als 'final' deklariert sein. +[ERROR] src/main/java/org/psesquared/server/subscriptions_api/controller/SubscriptionController.java:[54] (sizes) LineLength: Zeile ist 84 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/subscriptions_api/controller/SubscriptionController.java:[59] (sizes) LineLength: Zeile ist 128 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/subscriptions_api/controller/SubscriptionController.java:[61] (regexp) RegexpSingleline: Line has trailing spaces. +[ERROR] src/main/java/org/psesquared/server/subscriptions_api/controller/SubscriptionController.java:[62] (sizes) LineLength: Zeile ist 92 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/subscriptions_api/controller/SubscriptionController.java:[63] (sizes) LineLength: Zeile ist 121 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/subscriptions_api/controller/SubscriptionController.java:[64] (sizes) LineLength: Zeile ist 92 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/subscriptions_api/controller/SubscriptionController.java:[65] (regexp) RegexpSingleline: Line has trailing spaces. +[ERROR] src/main/java/org/psesquared/server/subscriptions_api/controller/SubscriptionController.java:[65] (sizes) LineLength: Zeile ist 100 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/subscriptions_api/controller/SubscriptionController.java:[68] (sizes) LineLength: Zeile ist 98 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/subscriptions_api/controller/SubscriptionController.java:[68,69] (misc) FinalParameters: Der Parameter username sollte als 'final' deklariert sein. +[ERROR] src/main/java/org/psesquared/server/subscriptions_api/controller/SubscriptionController.java:[69] (sizes) LineLength: Zeile ist 98 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/subscriptions_api/controller/SubscriptionController.java:[69,69] (misc) FinalParameters: Der Parameter deviceID sollte als 'final' deklariert sein. +[ERROR] src/main/java/org/psesquared/server/subscriptions_api/controller/SubscriptionController.java:[70] (sizes) LineLength: Zeile ist 107 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/subscriptions_api/controller/SubscriptionController.java:[70,69] (misc) FinalParameters: Der Parameter delta sollte als 'final' deklariert sein. +[ERROR] src/main/java/org/psesquared/server/subscriptions_api/controller/SubscriptionController.java:[76] (sizes) LineLength: Zeile ist 94 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/subscriptions_api/controller/SubscriptionController.java:[77] (regexp) RegexpSingleline: Line has trailing spaces. +[ERROR] src/main/java/org/psesquared/server/subscriptions_api/controller/SubscriptionController.java:[78] (sizes) LineLength: Zeile ist 92 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/subscriptions_api/controller/SubscriptionController.java:[79] (sizes) LineLength: Zeile ist 120 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/subscriptions_api/controller/SubscriptionController.java:[80] (sizes) LineLength: Zeile ist 81 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/subscriptions_api/controller/SubscriptionController.java:[81] (sizes) LineLength: Zeile ist 105 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/subscriptions_api/controller/SubscriptionController.java:[84] (sizes) LineLength: Zeile ist 96 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/subscriptions_api/controller/SubscriptionController.java:[84,67] (misc) FinalParameters: Der Parameter username sollte als 'final' deklariert sein. +[ERROR] src/main/java/org/psesquared/server/subscriptions_api/controller/SubscriptionController.java:[85] (sizes) LineLength: Zeile ist 96 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/subscriptions_api/controller/SubscriptionController.java:[85,67] (misc) FinalParameters: Der Parameter deviceID sollte als 'final' deklariert sein. +[ERROR] src/main/java/org/psesquared/server/subscriptions_api/controller/SubscriptionController.java:[86] (sizes) LineLength: Zeile ist 102 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/subscriptions_api/controller/SubscriptionController.java:[86,67] (misc) FinalParameters: Der Parameter since sollte als 'final' deklariert sein. +[ERROR] src/main/java/org/psesquared/server/subscriptions_api/controller/SubscriptionController.java:[87] (sizes) LineLength: Zeile ist 92 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/subscriptions_api/controller/SubscriptionController.java:[93] (sizes) LineLength: Zeile ist 97 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/subscriptions_api/controller/SubscriptionController.java:[94] (regexp) RegexpSingleline: Line has trailing spaces. +[ERROR] src/main/java/org/psesquared/server/subscriptions_api/controller/SubscriptionController.java:[95] (sizes) LineLength: Zeile ist 82 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/subscriptions_api/controller/SubscriptionController.java:[96] (sizes) LineLength: Zeile ist 100 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/subscriptions_api/controller/SubscriptionController.java:[99] (sizes) LineLength: Zeile ist 94 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/subscriptions_api/controller/SubscriptionController.java:[99,63] (misc) FinalParameters: Der Parameter username sollte als 'final' deklariert sein. +[ERROR] src/main/java/org/psesquared/server/subscriptions_api/controller/SubscriptionController.java:[100] (sizes) LineLength: Zeile ist 88 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/subscriptions_api/controller/SubscriptionDelta.java:[10] (javadoc) JavadocStyle: Der erste Satz sollte mit einem Punkt enden. +[ERROR] src/main/java/org/psesquared/server/subscriptions_api/controller/SubscriptionDelta.java:[11] (sizes) LineLength: Zeile ist 114 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/subscriptions_api/controller/SubscriptionDelta.java:[16,5] (javadoc) JavadocVariable: Es fehlt ein Javadoc-Kommentar. +[ERROR] src/main/java/org/psesquared/server/subscriptions_api/controller/SubscriptionDelta.java:[20,5] (javadoc) JavadocVariable: Es fehlt ein Javadoc-Kommentar. +[ERROR] src/main/java/org/psesquared/server/subscriptions_api/controller/SubscriptionDelta.java:[24,5] (javadoc) JavadocVariable: Es fehlt ein Javadoc-Kommentar. +[ERROR] src/main/java/org/psesquared/server/subscriptions_api/controller/SubscriptionDelta.java:[28] (sizes) LineLength: Zeile ist 84 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/subscriptions_api/controller/SubscriptionDelta.java:[29] (sizes) LineLength: Zeile ist 89 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/subscriptions_api/controller/SubscriptionDelta.java:[31,30] (misc) FinalParameters: Der Parameter add sollte als 'final' deklariert sein. +[ERROR] src/main/java/org/psesquared/server/subscriptions_api/controller/SubscriptionDelta.java:[31,43] (coding) HiddenField: Die Variable 'add' verbirgt ein Feld. +[ERROR] src/main/java/org/psesquared/server/subscriptions_api/controller/SubscriptionDelta.java:[31,48] (misc) FinalParameters: Der Parameter remove sollte als 'final' deklariert sein. +[ERROR] src/main/java/org/psesquared/server/subscriptions_api/controller/SubscriptionDelta.java:[31,61] (coding) HiddenField: Die Variable 'remove' verbirgt ein Feld. +[ERROR] src/main/java/org/psesquared/server/subscriptions_api/controller/SubscriptionDelta.java:[37] (javadoc) JavadocStyle: Der erste Satz sollte mit einem Punkt enden. +[ERROR] src/main/java/org/psesquared/server/subscriptions_api/controller/SubscriptionDelta.java:[39] (regexp) RegexpSingleline: Line has trailing spaces. +[ERROR] src/main/java/org/psesquared/server/subscriptions_api/controller/SubscriptionDelta.java:[46] (javadoc) JavadocStyle: Der erste Satz sollte mit einem Punkt enden. +[ERROR] src/main/java/org/psesquared/server/subscriptions_api/controller/SubscriptionDelta.java:[48] (regexp) RegexpSingleline: Line has trailing spaces. +[ERROR] src/main/java/org/psesquared/server/subscriptions_api/controller/SubscriptionDelta.java:[55] (javadoc) JavadocStyle: Der erste Satz sollte mit einem Punkt enden. +[ERROR] src/main/java/org/psesquared/server/subscriptions_api/controller/SubscriptionDelta.java:[57] (regexp) RegexpSingleline: Line has trailing spaces. +[ERROR] src/main/java/org/psesquared/server/subscriptions_api/service/SubscriptionService.java:[1] (javadoc) JavadocPackage: Es fehlt eine package-info.java. +[ERROR] src/main/java/org/psesquared/server/subscriptions_api/service/SubscriptionService.java:[12,35] (imports) AvoidStarImport: Stern-Importe der Form '.*' sollten vermieden werden - org.psesquared.server.model.*. +[ERROR] src/main/java/org/psesquared/server/subscriptions_api/service/SubscriptionService.java:[28,5] (javadoc) JavadocVariable: Es fehlt ein Javadoc-Kommentar. +[ERROR] src/main/java/org/psesquared/server/subscriptions_api/service/SubscriptionService.java:[29,5] (javadoc) JavadocVariable: Es fehlt ein Javadoc-Kommentar. +[ERROR] src/main/java/org/psesquared/server/subscriptions_api/service/SubscriptionService.java:[31,5] (javadoc) JavadocVariable: Es fehlt ein Javadoc-Kommentar. +[ERROR] src/main/java/org/psesquared/server/subscriptions_api/service/SubscriptionService.java:[33,5] (javadoc) JavadocVariable: Es fehlt ein Javadoc-Kommentar. +[ERROR] src/main/java/org/psesquared/server/subscriptions_api/service/SubscriptionService.java:[35,5] (javadoc) JavadocVariable: Es fehlt ein Javadoc-Kommentar. +[ERROR] src/main/java/org/psesquared/server/subscriptions_api/service/SubscriptionService.java:[37,5] (javadoc) JavadocVariable: Es fehlt ein Javadoc-Kommentar. +[ERROR] src/main/java/org/psesquared/server/subscriptions_api/service/SubscriptionService.java:[47] (regexp) RegexpSingleline: Line has trailing spaces. +[ERROR] src/main/java/org/psesquared/server/subscriptions_api/service/SubscriptionService.java:[49] (sizes) LineLength: Zeile ist 84 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/subscriptions_api/service/SubscriptionService.java:[52] (sizes) LineLength: Zeile ist 87 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/subscriptions_api/service/SubscriptionService.java:[52,36] (misc) FinalParameters: Der Parameter username sollte als 'final' deklariert sein. +[ERROR] src/main/java/org/psesquared/server/subscriptions_api/service/SubscriptionService.java:[52,53] (misc) FinalParameters: Der Parameter subscriptionStrings sollte als 'final' deklariert sein. +[ERROR] src/main/java/org/psesquared/server/subscriptions_api/service/SubscriptionService.java:[60] (sizes) LineLength: Zeile ist 85 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/subscriptions_api/service/SubscriptionService.java:[65] (sizes) LineLength: Zeile ist 118 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/subscriptions_api/service/SubscriptionService.java:[67] (sizes) LineLength: Zeile ist 84 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/subscriptions_api/service/SubscriptionService.java:[70] (sizes) LineLength: Zeile ist 98 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/subscriptions_api/service/SubscriptionService.java:[71] (sizes) LineLength: Zeile ist 85 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/subscriptions_api/service/SubscriptionService.java:[76] (sizes) LineLength: Zeile ist 102 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/subscriptions_api/service/SubscriptionService.java:[80] (sizes) LineLength: Zeile ist 99 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/subscriptions_api/service/SubscriptionService.java:[89] (javadoc) JavadocStyle: Der erste Satz sollte mit einem Punkt enden. +[ERROR] src/main/java/org/psesquared/server/subscriptions_api/service/SubscriptionService.java:[90] (sizes) LineLength: Zeile ist 82 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/subscriptions_api/service/SubscriptionService.java:[92] (regexp) RegexpSingleline: Line has trailing spaces. +[ERROR] src/main/java/org/psesquared/server/subscriptions_api/service/SubscriptionService.java:[97,42] (misc) FinalParameters: Der Parameter username sollte als 'final' deklariert sein. +[ERROR] src/main/java/org/psesquared/server/subscriptions_api/service/SubscriptionService.java:[98] (sizes) LineLength: Zeile ist 118 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/subscriptions_api/service/SubscriptionService.java:[106] (javadoc) JavadocStyle: Der erste Satz sollte mit einem Punkt enden. +[ERROR] src/main/java/org/psesquared/server/subscriptions_api/service/SubscriptionService.java:[108] (regexp) RegexpSingleline: Line has trailing spaces. +[ERROR] src/main/java/org/psesquared/server/subscriptions_api/service/SubscriptionService.java:[109] (sizes) LineLength: Zeile ist 84 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/subscriptions_api/service/SubscriptionService.java:[110] (sizes) LineLength: Zeile ist 82 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/subscriptions_api/service/SubscriptionService.java:[114] (sizes) LineLength: Zeile ist 81 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/subscriptions_api/service/SubscriptionService.java:[114,39] (misc) FinalParameters: Der Parameter username sollte als 'final' deklariert sein. +[ERROR] src/main/java/org/psesquared/server/subscriptions_api/service/SubscriptionService.java:[114,56] (misc) FinalParameters: Der Parameter delta sollte als 'final' deklariert sein. +[ERROR] src/main/java/org/psesquared/server/subscriptions_api/service/SubscriptionService.java:[121,56] (whitespace) OperatorWrap: '&&' sollte in einer neuen Zeile stehen. +[ERROR] src/main/java/org/psesquared/server/subscriptions_api/service/SubscriptionService.java:[122] (sizes) LineLength: Zeile ist 112 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/subscriptions_api/service/SubscriptionService.java:[125] (sizes) LineLength: Zeile ist 93 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/subscriptions_api/service/SubscriptionService.java:[132] (sizes) LineLength: Zeile ist 100 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/subscriptions_api/service/SubscriptionService.java:[139] (javadoc) JavadocStyle: Der erste Satz sollte mit einem Punkt enden. +[ERROR] src/main/java/org/psesquared/server/subscriptions_api/service/SubscriptionService.java:[142] (regexp) RegexpSingleline: Line has trailing spaces. +[ERROR] src/main/java/org/psesquared/server/subscriptions_api/service/SubscriptionService.java:[143] (sizes) LineLength: Zeile ist 84 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/subscriptions_api/service/SubscriptionService.java:[145] (sizes) LineLength: Zeile ist 82 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/subscriptions_api/service/SubscriptionService.java:[147] (sizes) LineLength: Zeile ist 82 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/subscriptions_api/service/SubscriptionService.java:[149,51] (misc) FinalParameters: Der Parameter username sollte als 'final' deklariert sein. +[ERROR] src/main/java/org/psesquared/server/subscriptions_api/service/SubscriptionService.java:[149,68] (misc) FinalParameters: Der Parameter since sollte als 'final' deklariert sein. +[ERROR] src/main/java/org/psesquared/server/subscriptions_api/service/SubscriptionService.java:[154] (sizes) LineLength: Zeile ist 81 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/subscriptions_api/service/SubscriptionService.java:[167] (sizes) LineLength: Zeile ist 82 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/subscriptions_api/service/SubscriptionService.java:[169] (regexp) RegexpSingleline: Line has trailing spaces. +[ERROR] src/main/java/org/psesquared/server/subscriptions_api/service/SubscriptionService.java:[172] (sizes) LineLength: Zeile ist 82 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/subscriptions_api/service/SubscriptionService.java:[175,47] (misc) FinalParameters: Der Parameter username sollte als 'final' deklariert sein. +[ERROR] src/main/java/org/psesquared/server/subscriptions_api/service/SubscriptionService.java:[176] (sizes) LineLength: Zeile ist 118 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/subscriptions_api/service/SubscriptionService.java:[189] (sizes) LineLength: Zeile ist 109 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/subscriptions_api/data_access/SubscriptionActionDao.java:[1] (javadoc) JavadocPackage: Es fehlt eine package-info.java. +[ERROR] src/main/java/org/psesquared/server/subscriptions_api/data_access/SubscriptionActionDao.java:[12] (javadoc) JavadocStyle: Der erste Satz sollte mit einem Punkt enden. +[ERROR] src/main/java/org/psesquared/server/subscriptions_api/data_access/SubscriptionActionDao.java:[16] (sizes) LineLength: Zeile ist 88 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/subscriptions_api/data_access/SubscriptionActionDao.java:[34] (sizes) LineLength: Zeile ist 83 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/subscriptions_api/data_access/SubscriptionActionDao.java:[37] (sizes) LineLength: Zeile ist 97 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/subscriptions_api/data_access/SubscriptionActionDao.java:[45] (sizes) LineLength: Zeile ist 84 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/subscriptions_api/data_access/SubscriptionActionDao.java:[49] (sizes) LineLength: Zeile ist 109 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/subscriptions_api/data_access/SubscriptionActionDao.java:[54] (sizes) LineLength: Zeile ist 81 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/subscriptions_api/data_access/SubscriptionActionDao.java:[60] (sizes) LineLength: Zeile ist 83 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/subscriptions_api/data_access/SubscriptionActionDao.java:[63] (sizes) LineLength: Zeile ist 82 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/subscriptions_api/data_access/SubscriptionActionDao.java:[64] (sizes) LineLength: Zeile ist 82 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/subscriptions_api/data_access/SubscriptionActionDao.java:[68] (sizes) LineLength: Zeile ist 104 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/subscriptions_api/data_access/SubscriptionActionDao.java:[70] (regexp) RegexpSingleline: Line has trailing spaces. +[ERROR] src/main/java/org/psesquared/server/subscriptions_api/data_access/SubscriptionDao.java:[10] (javadoc) JavadocStyle: Der erste Satz sollte mit einem Punkt enden. +[ERROR] src/main/java/org/psesquared/server/subscriptions_api/data_access/SubscriptionDao.java:[16,5] (javadoc) MissingJavadocMethod: Es fehlt ein Javadoc-Kommentar. +[ERROR] src/main/java/org/psesquared/server/subscriptions_api/data_access/SubscriptionDao.java:[17,5] (javadoc) InvalidJavadocPosition: Javadoc-Kommentar ist an der falschen Stelle platziert. +[ERROR] src/main/java/org/psesquared/server/subscriptions_api/data_access/SubscriptionDao.java:[25] (javadoc) JavadocStyle: Der erste Satz sollte mit einem Punkt enden. +[ERROR] src/main/java/org/psesquared/server/subscriptions_api/data_access/SubscriptionDao.java:[26] (sizes) LineLength: Zeile ist 81 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/ServerApplication.java:[1] (javadoc) JavadocPackage: Es fehlt eine package-info.java. +[ERROR] src/main/java/org/psesquared/server/ServerApplication.java:[9] (javadoc) JavadocStyle: Der erste Satz sollte mit einem Punkt enden. +[ERROR] src/main/java/org/psesquared/server/ServerApplication.java:[12,1] (design) HideUtilityClassConstructor: Hilfsklassen sollten keinen Standard-Konstruktur und keinen als public deklarierten Konstruktor haben. +[ERROR] src/main/java/org/psesquared/server/ServerApplication.java:[13] (sizes) LineLength: Zeile ist 95 Zeichen lang (Obergrenze ist 80). +[ERROR] src/main/java/org/psesquared/server/ServerApplication.java:[16] (javadoc) JavadocStyle: Der erste Satz sollte mit einem Punkt enden. +[ERROR] src/main/java/org/psesquared/server/ServerApplication.java:[16,1] (whitespace) FileTabCharacter: Die Datei enthält Tabulatorzeichen (dies ist das erste Vorkommen). +[ERROR] src/main/java/org/psesquared/server/ServerApplication.java:[18] (regexp) RegexpSingleline: Line has trailing spaces. +[ERROR] src/main/java/org/psesquared/server/ServerApplication.java:[21,33] (misc) FinalParameters: Der Parameter args sollte als 'final' deklariert sein. +[ERROR] src/main/resources/security.properties:[1] (misc) NewlineAtEndOfFile: Datei endet nicht mit einem Zeilenumbruch. +[INFO] ------------------------------------------------------------------------ +[INFO] BUILD FAILURE +[INFO] ------------------------------------------------------------------------ +[INFO] Total time: 13.946 s +[INFO] Finished at: 2023-03-18T08:10:57+01:00 +[INFO] ------------------------------------------------------------------------ +[ERROR] Failed to execute goal org.apache.maven.plugins:maven-checkstyle-plugin:3.2.1:check (default-cli) on project server: You have 799 Checkstyle violations. -> [Help 1] +[ERROR] +[ERROR] To see the full stack trace of the errors, re-run Maven with the -e switch. +[ERROR] Re-run Maven using the -X switch to enable full debug logging. +[ERROR] +[ERROR] For more information about the errors and possible solutions, please read the following articles: +[ERROR] [Help 1] http://cwiki.apache.org/confluence/display/MAVEN/MojoFailureException diff --git a/30-qualitaetsheft/assets/sources/linter-dashboard-after-processed.txt b/30-qualitaetsheft/assets/sources/linter-dashboard-after-processed.txt new file mode 100644 index 0000000..cab82f3 --- /dev/null +++ b/30-qualitaetsheft/assets/sources/linter-dashboard-after-processed.txt @@ -0,0 +1 @@ +1 warning vue/no-v-html diff --git a/30-qualitaetsheft/assets/sources/linter-dashboard-after.txt b/30-qualitaetsheft/assets/sources/linter-dashboard-after.txt new file mode 100644 index 0000000..f9bda3b --- /dev/null +++ b/30-qualitaetsheft/assets/sources/linter-dashboard-after.txt @@ -0,0 +1,10 @@ + +> pse-dashboard@0.0.0 lint +> eslint 'src/**/*.{js,vue}' + + +/home/gero/documents/uni/pse/pse-dashboard/src/components/HelpModal.vue + 25:21 warning 'v-html' directive can lead to XSS attack vue/no-v-html + +✖ 1 problem (0 errors, 1 warning) + diff --git a/30-qualitaetsheft/assets/sources/linter-dashboard-before-processed.txt b/30-qualitaetsheft/assets/sources/linter-dashboard-before-processed.txt new file mode 100644 index 0000000..150ea46 --- /dev/null +++ b/30-qualitaetsheft/assets/sources/linter-dashboard-before-processed.txt @@ -0,0 +1,22 @@ +26 error vue/html-indent +17 error vue/script-indent +6 error vue/require-v-for-key +5 error vue/multi-word-component-names +1 error vue/valid-v-for +1 error vue/valid-template-root +125 warning vue/max-attributes-per-line +34 warning vue/attributes-order +26 warning vue/singleline-html-element-content-newline +12 warning vue/require-default-prop +12 warning vue/html-self-closing +11 warning vue/first-attribute-linebreak +10 warning vue/html-closing-bracket-newline +8 warning vue/multiline-html-element-content-newline +5 warning vue/html-closing-bracket-spacing +3 warning vue/require-prop-types +3 warning vue/attribute-hyphenation +2 warning vue/html-self-closing +1 warning vue/v-on-event-hyphenation +1 warning vue/no-v-html +1 warning vue/mustache-interpolation-spacing + diff --git a/30-qualitaetsheft/assets/sources/linter-dashboard-before.txt b/30-qualitaetsheft/assets/sources/linter-dashboard-before.txt new file mode 100644 index 0000000..04a8317 --- /dev/null +++ b/30-qualitaetsheft/assets/sources/linter-dashboard-before.txt @@ -0,0 +1,358 @@ + +> pse-dashboard@0.0.0 lint +> eslint 'src/**/*.{js,vue}' + + +/home/gero/documents/uni/pse/pse-dashboard/src/components/Episode.vue + 1:1 error Component name "Episode" should always be multi-word vue/multi-word-component-names + 5:5 warning Prop 'action' requires default value to be set vue/require-default-prop + 5:5 warning Prop "action" should define at least its type vue/require-prop-types + 20:17 warning 'class' should be on a new line vue/max-attributes-per-line + 20:69 warning 'aria-current' should be on a new line vue/max-attributes-per-line + 23:67 warning 'style' should be on a new line vue/max-attributes-per-line + 38:51 warning 'style' should be on a new line vue/max-attributes-per-line + 39:17 warning Require self-closing on HTML elements (
) vue/html-self-closing + 39:43 warning ':style' should be on a new line vue/max-attributes-per-line + +/home/gero/documents/uni/pse/pse-dashboard/src/components/ErrorLog.vue + 14:1 error Expected indentation of 8 spaces but found 4 spaces vue/html-indent + 15:1 error Expected indentation of 12 spaces but found 8 spaces vue/html-indent + 15:9 error Elements in iteration expect to have 'v-bind:key' directives vue/require-v-for-key + 15:14 warning Expected a linebreak before this attribute vue/first-attribute-linebreak + 15:43 warning 'class' should be on a new line vue/max-attributes-per-line + 16:29 warning ':class' should be on a new line vue/max-attributes-per-line + 16:58 warning Expected 1 line break before closing bracket, but no line breaks found vue/html-closing-bracket-newline + 17:1 error Expected indentation of 16 spaces but found 12 spaces vue/html-indent + 17:13 warning Require self-closing on HTML elements (`), but no line breaks found vue/singleline-html-element-content-newline + +/home/gero/documents/uni/pse/pse-dashboard/src/components/LastUpdate.vue + 12:5 warning Prop 'iso' requires default value to be set vue/require-default-prop + 13:5 warning Prop 'unix' requires default value to be set vue/require-default-prop + 36:1 error Expected indentation of 4 spaces but found 0 spaces vue/html-indent + +/home/gero/documents/uni/pse/pse-dashboard/src/components/Loading.vue + 1:1 error Component name "Loading" should always be multi-word vue/multi-word-component-names + 9:30 warning Attribute "v-else" should go before "class" vue/attributes-order + 9:30 warning 'v-else' should be on a new line vue/max-attributes-per-line + +/home/gero/documents/uni/pse/pse-dashboard/src/components/LogEntry.vue + 3:1 error The template requires child element vue/valid-template-root + 3:1 warning Require self-closing on HTML elements (