diff options
Diffstat (limited to '00-pflichtenheft')
38 files changed, 8599 insertions, 0 deletions
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 Binary files differnew file mode 100644 index 0000000..7de8ed4 --- /dev/null +++ b/00-pflichtenheft/assets/KIT_Deckblatt.pdf diff --git a/00-pflichtenheft/assets/logo.pdf b/00-pflichtenheft/assets/logo.pdf Binary files differnew file mode 100644 index 0000000..91fd334 --- /dev/null +++ b/00-pflichtenheft/assets/logo.pdf 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 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<svg + version="1.2" + width="87.589989mm" + height="52.16547mm" + viewBox="0 0 8758.9989 5216.547" + preserveAspectRatio="xMidYMid" + fill-rule="evenodd" + stroke-width="28.222" + stroke-linejoin="round" + xml:space="preserve" + id="svg206" + xmlns="http://www.w3.org/2000/svg" + xmlns:svg="http://www.w3.org/2000/svg"> + <defs + class="ClipPathGroup" + id="defs8" /> + <defs + id="defs51"><font + id="EmbeddedFont_1" + horiz-adv-x="2048" + horiz-origin-x="0" + horiz-origin-y="0" + vert-origin-x="512" + vert-origin-y="768" + vert-adv-y="1024"> + <font-face + font-family="Noto Sans Display Light embedded" + units-per-em="2048" + font-weight="normal" + font-style="normal" + ascent="2170" + descent="609" + id="font-face10" /> + <missing-glyph + horiz-adv-x="2048" + d="M 0,0 L 2047,0 2047,2047 0,2047 0,0 Z" + id="missing-glyph12" /> + <glyph + unicode="y" + horiz-adv-x="927" + d="M 2,1089 L 127,1089 367,413 C 388,352 407,298 422,250 437,203 449,161 457,124 L 463,124 C 471,156 483,196 498,246 513,296 530,350 551,409 L 786,1089 913,1089 453,-193 C 418,-290 377,-364 328,-416 279,-468 213,-494 131,-494 107,-494 84,-493 63,-490 43,-487 25,-482 8,-476 L 8,-377 C 23,-383 40,-387 57,-391 75,-394 95,-396 117,-396 170,-396 214,-378 249,-342 284,-307 314,-252 340,-179 L 403,4 2,1089 Z" + id="glyph14" /> + <glyph + unicode="t" + horiz-adv-x="610" + d="M 465,80 C 494,80 521,82 547,87 573,91 595,97 614,105 L 614,11 C 594,3 569,-5 541,-11 512,-17 481,-20 449,-20 363,-20 296,5 249,56 202,106 178,189 178,304 L 178,996 27,996 27,1061 178,1100 219,1350 295,1350 295,1090 608,1090 608,996 295,996 295,310 C 295,157 352,80 465,80 Z" + id="glyph16" /> + <glyph + unicode="s" + horiz-adv-x="742" + d="M 817,289 C 817,191 782,115 713,61 643,7 545,-20 418,-20 347,-20 284,-14 228,-1 173,12 126,29 88,50 L 88,162 C 134,138 186,118 244,102 301,86 360,78 420,78 520,78 592,96 636,133 680,169 702,218 702,281 702,341 679,387 632,419 585,451 515,484 422,519 359,542 303,565 255,589 207,613 169,643 142,680 116,717 102,768 102,832 102,919 136,987 204,1037 271,1086 361,1110 473,1110 535,1110 592,1104 646,1092 700,1080 750,1063 795,1043 L 754,946 C 713,964 667,980 617,993 568,1006 518,1012 469,1012 388,1012 326,997 283,967 239,937 217,893 217,836 217,792 228,758 249,733 270,707 301,686 342,668 383,650 433,630 492,609 553,585 608,562 657,537 707,512 745,481 774,443 803,405 817,353 817,289 Z" + id="glyph18" /> + <glyph + unicode="r" + horiz-adv-x="583" + d="M 596,1108 C 646,1108 692,1102 733,1091 L 717,983 C 674,995 632,1001 590,1001 497,1001 423,964 368,890 312,817 285,719 285,598 L 285,-1 168,-1 168,1089 266,1089 279,886 285,886 C 311,948 350,1000 402,1043 455,1086 520,1108 596,1108 Z" + id="glyph20" /> + <glyph + unicode="o" + horiz-adv-x="927" + d="M 1030,547 C 1030,433 1012,333 976,248 940,164 887,98 818,51 749,4 665,-20 565,-20 471,-20 390,3 322,50 253,96 201,162 164,247 127,333 109,433 109,547 109,723 150,861 232,961 315,1061 429,1110 575,1110 672,1110 755,1087 822,1040 890,993 941,927 977,842 1012,757 1030,659 1030,547 Z M 229,547 C 229,407 257,294 312,208 368,123 453,80 567,80 685,80 771,123 826,209 882,295 909,408 909,547 909,637 898,717 875,787 851,856 815,911 766,951 717,990 653,1010 573,1010 459,1010 373,969 315,887 258,805 229,692 229,547 Z" + id="glyph22" /> + <glyph + unicode="n" + horiz-adv-x="847" + d="M 633,1110 C 749,1110 838,1078 900,1014 962,950 993,850 993,713 L 993,1 877,1 877,705 C 877,809 854,885 810,935 766,985 701,1010 616,1010 395,1010 285,871 285,594 L 285,1 168,1 168,1090 262,1090 279,901 287,901 C 314,962 357,1011 416,1051 474,1091 547,1110 633,1110 Z" + id="glyph24" /> + <glyph + unicode="m" + horiz-adv-x="1430" + d="M 1245,1110 C 1348,1110 1427,1080 1485,1018 1542,957 1571,860 1571,727 L 1571,1 1454,1 1454,723 C 1454,820 1434,892 1393,939 1352,986 1296,1010 1227,1010 1130,1010 1056,980 1005,919 953,858 928,764 928,637 L 928,1 811,1 811,723 C 811,820 791,892 750,939 709,986 653,1010 584,1010 487,1010 413,978 361,914 310,850 285,751 285,619 L 285,1 168,1 168,1090 262,1090 279,918 287,918 C 313,971 352,1016 403,1054 455,1092 521,1110 600,1110 675,1110 739,1093 791,1059 842,1025 879,975 899,908 L 907,908 C 936,972 980,1022 1038,1057 1097,1093 1166,1110 1245,1110 Z" + id="glyph26" /> + <glyph + unicode="i" + horiz-adv-x="187" + d="M 227,1493 C 279,1493 305,1464 305,1405 305,1345 279,1315 227,1315 175,1315 150,1345 150,1405 150,1464 175,1493 227,1493 Z M 285,1090 L 285,0 168,0 168,1090 285,1090 Z" + id="glyph28" /> + <glyph + unicode="h" + horiz-adv-x="847" + d="M 285,1059 C 285,1031 284,1003 283,977 281,951 279,926 276,901 L 285,901 C 312,962 355,1011 413,1051 471,1091 543,1110 629,1110 746,1110 836,1078 899,1014 962,950 993,850 993,713 L 993,1 877,1 877,705 C 877,809 854,885 810,935 766,985 701,1010 616,1010 395,1010 285,871 285,594 L 285,1 168,1 168,1557 285,1557 285,1059 Z" + id="glyph30" /> + <glyph + unicode="f" + horiz-adv-x="689" + d="M 575,995 L 332,995 332,-1 213,-1 213,995 27,995 27,1058 213,1093 213,1202 C 213,1445 316,1566 522,1566 559,1566 593,1563 623,1557 653,1551 680,1544 705,1536 L 678,1439 C 655,1448 630,1454 603,1460 577,1465 550,1468 524,1468 456,1468 407,1447 377,1405 347,1362 332,1295 332,1202 L 332,1089 575,1089 575,995 Z" + id="glyph32" /> + <glyph + unicode="e" + horiz-adv-x="874" + d="M 559,1110 C 646,1110 720,1089 779,1046 839,1003 883,944 913,869 943,794 958,708 958,611 L 958,531 229,531 C 231,386 262,275 325,198 387,121 476,82 592,82 656,82 712,88 759,100 806,111 858,130 915,156 L 915,50 C 865,25 814,7 764,-4 713,-15 655,-20 588,-20 434,-20 315,30 232,129 150,229 109,365 109,537 109,648 126,746 162,832 197,918 249,986 315,1036 382,1085 464,1110 559,1110 Z M 559,1012 C 465,1012 389,979 333,912 276,845 243,750 233,627 L 838,627 C 838,742 815,835 769,906 723,977 653,1012 559,1012 Z" + id="glyph34" /> + <glyph + unicode="d" + horiz-adv-x="900" + d="M 535,-20 C 398,-20 293,27 219,120 145,214 109,352 109,535 109,722 147,865 224,963 301,1061 408,1110 545,1110 629,1110 698,1090 752,1050 805,1010 845,961 872,904 L 881,904 C 879,935 878,970 876,1009 873,1048 872,1084 872,1117 L 872,1557 989,1557 989,0 895,0 879,191 872,191 C 845,132 805,82 751,41 697,0 625,-20 535,-20 Z M 553,80 C 669,80 752,119 801,195 850,271 875,382 875,527 L 875,545 C 875,695 850,810 801,890 752,970 671,1010 559,1010 451,1010 369,969 313,886 257,804 229,686 229,533 229,385 256,273 309,196 363,119 444,80 553,80 Z" + id="glyph36" /> + <glyph + unicode="c" + horiz-adv-x="768" + d="M 580,-20 C 429,-20 313,29 231,127 150,226 109,363 109,539 109,662 129,766 170,850 211,935 269,1000 343,1044 417,1088 504,1110 602,1110 651,1110 698,1106 742,1096 787,1087 825,1074 858,1057 L 825,957 C 791,972 754,984 714,993 673,1002 636,1006 600,1006 481,1006 390,964 326,881 261,798 229,685 229,541 229,405 258,294 314,210 371,126 459,84 580,84 630,84 678,90 723,101 768,112 809,125 846,142 L 846,37 C 812,20 773,6 729,-5 685,-15 636,-20 580,-20 Z" + id="glyph38" /> + <glyph + unicode="a" + horiz-adv-x="822" + d="M 535,1108 C 651,1108 737,1078 795,1018 852,958 881,863 881,734 L 881,0 793,0 772,185 766,185 C 729,123 684,74 631,36 578,-1 503,-20 408,-20 311,-20 233,6 176,59 119,111 90,187 90,285 90,394 132,477 215,533 298,589 420,621 580,629 L 764,639 764,715 C 764,822 744,897 705,942 665,987 606,1010 528,1010 477,1010 426,1002 378,987 329,972 281,953 231,928 L 195,1022 C 242,1047 295,1067 352,1084 410,1100 470,1108 535,1108 Z M 594,543 C 466,536 370,512 307,470 244,429 213,367 213,285 213,217 232,165 271,131 310,96 363,78 430,78 535,78 617,111 676,176 735,240 764,330 764,445 L 764,551 594,543 Z" + id="glyph40" /> + <glyph + unicode="S" + horiz-adv-x="875" + d="M 956,381 C 956,294 936,220 894,160 852,100 796,55 724,25 652,-5 571,-20 479,-20 396,-20 324,-14 262,-2 201,11 147,26 102,46 L 102,162 C 152,142 209,124 273,109 338,94 409,87 485,87 589,87 673,110 738,158 803,206 836,278 836,373 836,431 823,478 798,515 772,553 734,586 682,614 630,642 565,670 485,699 410,726 345,757 291,791 236,825 194,868 164,919 134,970 119,1035 119,1112 119,1192 138,1259 176,1314 214,1369 267,1411 333,1440 399,1469 474,1483 559,1483 626,1483 689,1476 750,1463 810,1449 868,1430 924,1405 L 883,1303 C 772,1352 663,1377 555,1377 462,1377 386,1354 328,1310 269,1266 240,1200 240,1114 240,1052 252,1001 278,964 303,926 340,895 389,869 438,843 498,817 567,791 648,762 717,731 775,698 833,664 878,623 909,573 941,523 956,459 956,381 Z" + id="glyph42" /> + <glyph + unicode="P" + horiz-adv-x="848" + d="M 539,1462 C 869,1462 1034,1325 1034,1049 1034,908 992,798 907,718 823,638 690,598 510,598 L 311,598 311,0 193,0 193,1462 539,1462 Z M 528,1358 L 311,1358 311,702 498,702 C 629,702 730,727 803,776 875,825 911,914 911,1043 911,1152 880,1232 818,1282 756,1333 659,1358 528,1358 Z" + id="glyph44" /> + <glyph + unicode="E" + horiz-adv-x="769" + d="M 950,0 L 193,0 193,1462 950,1462 950,1356 311,1356 311,821 913,821 913,715 311,715 311,107 950,107 950,0 Z" + id="glyph46" /> + <glyph + unicode=" " + horiz-adv-x="503" + id="glyph48" /> + </font></defs> + <defs + class="TextShapeIndex" + id="defs55" /> + <defs + class="EmbeddedBulletChars" + id="defs87" /> + + <g + id="id10" + clip-path="none" + transform="translate(-700.00001,-2550)"> + + <text + class="SVGTextShape" + id="text151" + x="217.60002" + y="-56.506969" + style="letter-spacing:4.7625px;word-spacing:104.775px"><tspan + class="TextParagraph" + font-family="'Noto Sans Display Light', sans-serif" + font-size="494px" + font-weight="400" + id="tspan149"><tspan + class="TextPosition" + x="650.59998" + y="7647.4932" + id="tspan147"><tspan + fill="#808080" + stroke="none" + style="white-space:pre" + id="tspan145" + dx="2.1199999">Podcast Synchronisation made Efficient</tspan></tspan></tspan></text> + </g><g + id="g1277" + transform="translate(-700.00001,-2550)"><path + id="path131-3" + d="m 6694.0006,4763 c -559.5515,4.407 -980.3924,428.0893 -986.038,985.9863 18.2894,552.8957 454.3127,974.1166 989.0352,986.0379 v -235.0244 c -406.8751,-27.3888 -715.1078,-362.1649 -719.0259,-748.0163 12.7874,-421.0793 236.9242,-746.3999 750.0318,-749.98 895.0688,1.4728 1915.5158,0 2730.9957,0 V 4763 Z" + style="fill:#0084d1;fill-opacity:1" + clip-path="none" /><path + id="path131" + d="m 6685.0183,2562.996 c -559.5515,4.407 -980.3924,428.0893 -986.038,985.9863 18.2894,552.8957 454.3127,974.1167 989.0352,986.038 v -235.0244 c -406.8751,-27.3888 -715.1078,-362.165 -719.0259,-748.0164 12.7874,-421.0792 236.9242,-746.3998 750.0318,-749.98 895.0688,1.4728 1915.5158,0 2730.996,0 V 2562.996 Z" + style="fill:#0084d1;fill-opacity:1" + clip-path="none" /></g><g + id="g1263" + transform="translate(-700.00001,-2550)"><path + fill="none" + stroke="#069a2e" + stroke-width="265" + stroke-linejoin="round" + d="m 2793,5962 c 1283,0 429,-2762 1712,-2762" + id="path124" /><path + id="path110-6" + d="M 3198.0212,6550 V 6300.0411 H 2448.0411 V 6050.0305 5550.0094 H 2198.0305 V 6300.0411 6550 H 2448.0411 2698 Z" + style="fill:#069a2e;fill-opacity:1" /><path + id="path110" + d="m 4111.997,2550.0252 v 249.9589 h 749.9801 v 250.0106 500.0211 h 250.0106 v -750.0317 -249.9589 h -250.0106 -249.9589 z" + style="fill:#069a2e;fill-opacity:1" /></g> + <g + id="g1249" + transform="translate(-700.00001,-2550)"><path + fill="#ff8000" + stroke="none" + d="M 2215,4164 C 2412.6918,3832.6035 2379.3124,3383.7591 2135.4189,3084.8193 1956.8564,2857.4026 1671.3097,2718.8851 1382,2721 c 0,-52.6667 0,-105.3333 0,-158 -60.1303,-1.0792 190.6585,-1.2724 121.9814,2.7933 434.6311,30.3756 832.6257,336.7309 974.9655,748.2365 148.7336,402.6249 41.0263,883.7477 -266.0719,1183.8452 C 1996.6852,4716.1853 1689.0048,4838.6187 1382,4830 c 0,-61.6667 0,-123.3333 0,-185 338.199,2.9253 666.226,-186.816 833,-481 z" + id="path193" /><path + fill="#ff8000" + stroke="none" + d="m 1936,3979 c 175.8129,-283.3241 59.7943,-700.6319 -239.8255,-849.407 -94.2713,-50.9942 -201.887,-77.9344 -309.1745,-74.593 0,-57 0,-114 0,-171 396.3865,-24.0968 777.5367,297.6517 818.5878,693.1841 44.0348,337.5467 -149.4277,694.9971 -466.4953,826.9493 -110.1027,49.0687 -231.5272,73.543 -352.0925,67.8666 0,-61.6667 0,-123.3333 0,-185 220.6353,7.5804 440.8554,-115.3286 549,-308 z" + id="path186" /><path + fill="#ff8000" + stroke="none" + d="m 1659,3822 c 86.3933,-138.0398 18.2474,-344.669 -134.6962,-402.3205 C 1483.44,3402.2177 1438.3524,3394.2432 1394,3398 c 0,-56.6667 0,-113.3333 0,-170 223.1964,-19.6143 444.7886,153.3254 478.4886,375.1817 32.646,181.6951 -54.6854,381.4331 -217.3127,472.2205 -78.2316,46.131 -170.3991,69.3119 -261.1759,62.5978 0,-58.3333 0,-116.6667 0,-175 105.6736,9.5509 212.8962,-49.2218 265,-141 z" + id="path179" /><rect + class="BoundingBox" + stroke="none" + fill="none" + x="700" + y="2550" + width="501" + height="4001" + id="rect136" + style="fill:#ff8000;fill-opacity:1" /></g> + +</svg> diff --git a/00-pflichtenheft/assets/ui/help-desktop.png b/00-pflichtenheft/assets/ui/help-desktop.png Binary files differnew file mode 100644 index 0000000..b3d1231 --- /dev/null +++ b/00-pflichtenheft/assets/ui/help-desktop.png diff --git a/00-pflichtenheft/assets/ui/listening-progess-account-dropdown.png b/00-pflichtenheft/assets/ui/listening-progess-account-dropdown.png Binary files differnew file mode 100644 index 0000000..df69bdc --- /dev/null +++ b/00-pflichtenheft/assets/ui/listening-progess-account-dropdown.png diff --git a/00-pflichtenheft/assets/ui/listening-progress-desktop.png b/00-pflichtenheft/assets/ui/listening-progress-desktop.png Binary files differnew file mode 100644 index 0000000..cf69719 --- /dev/null +++ b/00-pflichtenheft/assets/ui/listening-progress-desktop.png diff --git a/00-pflichtenheft/assets/ui/listening-progress-mobile.png b/00-pflichtenheft/assets/ui/listening-progress-mobile.png Binary files differnew file mode 100644 index 0000000..4fe4e1f --- /dev/null +++ b/00-pflichtenheft/assets/ui/listening-progress-mobile.png diff --git a/00-pflichtenheft/assets/ui/login-desktop.png b/00-pflichtenheft/assets/ui/login-desktop.png Binary files differnew file mode 100644 index 0000000..bb26027 --- /dev/null +++ b/00-pflichtenheft/assets/ui/login-desktop.png diff --git a/00-pflichtenheft/assets/ui/login-mobile.png b/00-pflichtenheft/assets/ui/login-mobile.png Binary files differnew file mode 100644 index 0000000..78f619d --- /dev/null +++ b/00-pflichtenheft/assets/ui/login-mobile.png diff --git a/00-pflichtenheft/assets/ui/podcasts-desktop-change-language.png b/00-pflichtenheft/assets/ui/podcasts-desktop-change-language.png Binary files differnew file mode 100644 index 0000000..68e2339 --- /dev/null +++ b/00-pflichtenheft/assets/ui/podcasts-desktop-change-language.png diff --git a/00-pflichtenheft/assets/ui/podcasts-desktop.png b/00-pflichtenheft/assets/ui/podcasts-desktop.png Binary files differnew file mode 100644 index 0000000..33337a1 --- /dev/null +++ b/00-pflichtenheft/assets/ui/podcasts-desktop.png diff --git a/00-pflichtenheft/assets/ui/podcasts-mobile.png b/00-pflichtenheft/assets/ui/podcasts-mobile.png Binary files differnew file mode 100644 index 0000000..d4939f7 --- /dev/null +++ b/00-pflichtenheft/assets/ui/podcasts-mobile.png diff --git a/00-pflichtenheft/assets/ui/settings-desktop-1.png b/00-pflichtenheft/assets/ui/settings-desktop-1.png Binary files differnew file mode 100644 index 0000000..ed995fb --- /dev/null +++ b/00-pflichtenheft/assets/ui/settings-desktop-1.png diff --git a/00-pflichtenheft/assets/ui/settings-desktop-2.png b/00-pflichtenheft/assets/ui/settings-desktop-2.png Binary files differnew file mode 100644 index 0000000..cdce478 --- /dev/null +++ b/00-pflichtenheft/assets/ui/settings-desktop-2.png diff --git a/00-pflichtenheft/assets/ui/settings-mobile-1.png b/00-pflichtenheft/assets/ui/settings-mobile-1.png Binary files differnew file mode 100644 index 0000000..acdf7ca --- /dev/null +++ b/00-pflichtenheft/assets/ui/settings-mobile-1.png diff --git a/00-pflichtenheft/assets/ui/settings-mobile-2.png b/00-pflichtenheft/assets/ui/settings-mobile-2.png Binary files differnew file mode 100644 index 0000000..2389d6b --- /dev/null +++ b/00-pflichtenheft/assets/ui/settings-mobile-2.png 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 <xuyuan.cn@gmail.com>, Southeast University, China +% Contributor: Nobel Huang <nobel1984@gmail.com>, 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 <humbert@uni-wuppertal.de> 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] {<<Website>> 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 + |